Как получить информацию о сокете, если вы не знаете, сколько данных поступает?

Я работаю над графическим интерфейсом на основе Python для взаимодействия с роботом, работающим на Python, и Arduino Mega в качестве контроллера мотора и контроллера сенсора.

Первоначально я собирался использовать удаленный рабочий стол для загрузки моего графического интерфейса от робота. Это оказалось очень медленно из-за удаленного рабочего стола. Я решил, что сервер и клиент будут лучше.

У меня на Arduino работает эскиз, который ловит моторные команды и выполняет их. Он также ожидает поступления команды "Ping", после чего он должен проверить ультразвуковой датчик в трех разных положениях, затем записать эту информацию обратно на сервер, который должен перехватить эти данные и, в свою очередь, передать их клиенту. GUI. Я получил почти все это на работу, но я не могу передать данные с сервера обратно клиенту. Я думал, что простой "client.recv()" достигнет этого, но это не так.

Как я могу получить эти данные, если я точно не знаю, сколько данных возвращается?

Arduino отправляет данные как "dist1,dist2,dist3 \n".

Вот мой код Arduino:

#include <LiquidCrystal.h>
#include <Ping.h>
#include <Servo.h>
// Parallax Ping Unit for distance sensing.
Ping sonic(22);

// Setup LCD pins.
LiquidCrystal lcd(7, 8, 9, 10, 11, 12);

// Servo for Ping unit sweeping.
Servo  PingServo;

// Setup Ping distance setting variables.
int pingDisCent;
int pingDisLeft;
int pingDisRight;

// Variable to keep commands in.
char MsgRcvd;

// Motor setup information.

int LF[] = {23,24};
int LR[] = {25,26};
int RF[] = {27,28};
int RR[] = {29,30};

// Set Debugging here
// 1 - Debug on - Motors don't turn when commands are sent.
// 0 - Debug off - Motors turn when commands are sent.
int debug = 1;

//Variables for speed
int SpdPin = 22;
int Speed = 255;

void setup()
{
  Serial.begin(9600);  // start serial communications

  // Setup motors for output.
  int i;
  for(i = 0; i < 2; i++){
    pinMode(LF[i], OUTPUT);
    pinMode(LR[i], OUTPUT);
    pinMode(RF[i], OUTPUT);
    pinMode(RR[i], OUTPUT);
  }

  // Setup servo to sweep.
  PingServo.attach(6);
  PingServo.write(90);

  // Set up the LCD's number of rows and columns:
  lcd.begin(16, 2);

  // Print a message to the LCD.
  lcd.print("Waiting...");

  // Setup speed pin.
  pinMode(SpdPin, OUTPUT);
}

void loop()
{
  if (Serial.available() > 0)    //Check to see if a command is available.
  {
    MsgRcvd = Serial.read();    // If a command is there, see what it is.
    switch (MsgRcvd)
    {
      case '0':
        Stop();
        break;
      case '1':
        MoveForward();
        break;
      case '2':
        MoveLeft();
        break;
      case '3':
        MoveRight();
        break;
      case '4':
        MoveBackward();
        break;
      case '~':
        active_ir();
        break;
      case 'M':                    // Check to see if we have a connection from the GUI - if so spit out information to the LCD.
        lcd.clear();
        lcd.print("Connected");
        lcd.setCursor(0,1);
        lcd.print("waiting..");
        break;
      case 'D':
        lcd.setCursor(0,1);
        lcd.print("Disconnected"); // Client disconnected - spit out a disconnect to the LCD.
        break;
    }
  }
  delay(100);
}

//  ===================================
//  =====    Ping Ultrasonic      =====
//  ===================================
void active_ir()
{
  // Read to the right.
  PingServo.write(30);
  delay(300);
  pingDisRight = sonic.inch();
  delay(500);

  // Read to the front.
  PingServo.write(90);
  delay(300);
  pingDisCent = sonic.inch();
  delay(500);
  //  Read to the left.
  PingServo.write(150);
  delay(300);
  pingDisLeft = sonic.inch();
  delay(500);
  Serial.print(pingDisLeft);
  Serial.print(',');
  Serial.print(pingDisCent);
  Serial.print(',');
  Serial.println(pingDisRight);
  return;
}



//  ==========================================
//  ======        MOTOR CONTROL      =========
//  ==========================================

void MoveForward()
{
  lcd.clear();
  lcd.setCursor(0,0);
  lcd.print("Forward");
  if (debug == 0){

    digitalWrite(LF[0], HIGH);
    digitalWrite(LF[1], LOW);
    digitalWrite(LR[0], HIGH);
    digitalWrite(LR[1], LOW);
    digitalWrite(RF[0], HIGH);
    digitalWrite(RF[1], LOW);
    digitalWrite(RR[0], HIGH);
    digitalWrite(RR[1], LOW);
  }
}

void MoveBackward()
{
  lcd.clear();
  lcd.setCursor(0,0);
  lcd.print("Reverse");
  if (debug == 0){
    analogWrite(SpdPin, Speed);
    digitalWrite(LF[0], LOW);
    digitalWrite(LF[1], HIGH);
    digitalWrite(LR[0], LOW);
    digitalWrite(LR[1], HIGH);
    digitalWrite(RF[0], LOW);
    digitalWrite(RF[1], HIGH);
    digitalWrite(RR[0], LOW);
    digitalWrite(RR[1], HIGH);
  }
}

void MoveLeft()
{
  lcd.clear();
  lcd.setCursor(0,0);
  lcd.print("Left");
  if (debug == 0){
    analogWrite(SpdPin, Speed);
    digitalWrite(LF[0], LOW);
    digitalWrite(LF[1], HIGH);
    digitalWrite(LR[0], LOW);
    digitalWrite(LR[1], HIGH);
    digitalWrite(RF[0], HIGH);
    digitalWrite(RF[1], LOW);
    digitalWrite(RR[0], HIGH);
    digitalWrite(RR[1], LOW);
  }
}

void MoveRight()
{
  lcd.clear();
  lcd.setCursor(0,0);
  lcd.print("Right");
  if (debug == 0) {
    analogWrite(SpdPin, Speed);
    digitalWrite(LF[0], HIGH);
    digitalWrite(LF[1], LOW);
    digitalWrite(LR[0], HIGH);
    digitalWrite(LR[1], LOW);
    digitalWrite(RF[0], LOW);
    digitalWrite(RF[1], HIGH);
    digitalWrite(RR[0], LOW);
    digitalWrite(RR[1], HIGH);
  }
}

void Stop()
{
  lcd.clear();
  lcd.setCursor(0,0);
  lcd.print("Stopping");
  if (debug == 0){
    digitalWrite(LF[0], LOW);
    digitalWrite(LF[1], LOW);
    digitalWrite(LR[0], LOW);
    digitalWrite(LR[1], LOW);
    digitalWrite(RF[0], LOW);
    digitalWrite(RF[1], LOW);
    digitalWrite(RR[0], LOW);
    digitalWrite(RR[1], LOW);
  }
}

Вот мой код сервера Python:

import serial
import socket

Serv = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
Serv.bind(('', 9000))
Serv.listen(1)
print "Listening on TCP 9000"
motor = serial.Serial('/dev/ttyUSB0', 9600, timeout=1)
print "Connected to Motor Controller: /dev/ttyUSB0"

while(1):
    print "Waiting For Connection..."
    connection, addr = Serv.accept()
    connection.setblocking(0)
    print "Connected by", addr[0]
    while(1):
        try:
            Servdata = connection.recv(1)
            break
        except:
            pass
    if (Servdata == 'M'):
        print "Entering Manual Mode"
        motor.write(Servdata)
        while(Servdata != 'X'):
            Servdata = '9'
            try:
                Servdata = connection.recv(1)
            except:
                pass
            if Servdata == 'X':
                print "Exiting"
                break
            if Servdata == '0':
                print "Stopping"
                motor.write(Servdata)
            if Servdata == '1':
                print "Forward"
                motor.write(Servdata)
            if Servdata == '2':
                print "Left"
                motor.write(Servdata)
            if Servdata == '3':
                print "Right"
                motor.write(Servdata)
            if Servdata == '4':
                motor.write(Servdata)
                print "Backwards"
            if Servdata == '~':
                motor.write(Servdata)
                retval = motor.readline()
                Serv.send(retval)
            else:
                pass
        motor.write('0')
        connection.close()
        print addr[0], "Closed Manual Mode"

И, наконец, что не менее важно, код GUI клиента (и это также, где я думаю, что мои проблемы лежат...):

from socket import *
from PythonCard import model
HOST = ''
PORT = 9000
ADDR = (HOST,PORT)
BUFSIZE = 4096
Client = socket (AF_INET,SOCK_STREAM)
Client.connect((ADDR))
Client.send('M')
class MainWindow(model.Background):
    def on_SetSpdBtn_mouseClick(self, event):
        spd = self.components.SpdSpn.value
    def on_FwdBtn_mouseClick(self, event):
        spd = self.components.SpdSpn.value
        Client.send('1')
    def on_LftBtn_mouseClick(self, event):
        spd = self.components.SpdSpn.value
        Client.send('2')
    def on_RitBtn_mouseClick(self, event):
        spd = self.components.SpdSpn.value
        Client.send('3')
    def on_RevBtn_mouseClick(self, event):
        spd = self.components.SpdSpn.value
        Client.send('4')
    def on_StpBtn_mouseClick(self, event):
        spd = self.components.SpdSpn.value
        Client.send('0')
    def on_GetPing_mouseClick(self, event):
        Client.send('~')
        retval = Client.recv()
        ping_data = retval.strip() # Strip out the newline, if you read an entire line.
        split_data = ping_data.split(',')
        L_Ping = split_data[0]
        R_Ping = split_data[1]
        self.components.PingLeft.text = str(L_Ping)
        self.components.PingRight.text = str(R_Ping)
app = model.Application(MainWindow)
app.MainLoop()

1 ответ

Решение

Я думаю, что нашел три проблемы в этом коде; первое расточительно, а второе, вероятно, почему вы пришли сюда сегодня, а третье, почему вы думаете, что пришли сегодня.:)

Ожидание в ожидании

Этот код занят ожиданием поступления данных в соединение:

connection.setblocking(0)
print "Connected by", addr[0]
while(1):
    try:
        Servdata = connection.recv(1)
        break
    except:
        pass

И снова здесь:

    while(Servdata != 'X'):
        Servdata = '9'
        try:
            Servdata = connection.recv(1)
        except:
            pass
        # ...
        else:
            pass

Это сжигает циклы процессора как сумасшедшие; Надеюсь, вы не работаете от батареи. Это также ничего не покупает; Вы могли бы также назвать блокировку recv(), Пусть процессор перейдет в спящий режим, пока он ждет следующего входного байта. (Если бы вы фактически использовали неблокирование для чего-либо, то ожидание занятости имело бы больше смысла, но это не так. Если вы хотите ограничить время, которое сервер будет блокировать для ввода, всегда есть settimeout(), Но не просто используйте это вслепую, потому что этот код выиграет больше всего от простого переключения на блокировку recv().)

Не отправка данных клиенту

        if Servdata == '~':
            motor.write(Servdata)
            retval = motor.readline()
            Serv.send(retval)

Я не думаю, что этот блок кода был выполнен:) Serv ни к чему не подключен, это гнездо для прослушивания. Ты наверное имел ввиду connection.send(retval) Вот; Я не могу найти никаких других строк на сервере, которые бы на самом деле отправляли данные клиенту, так что предположительно это должно было быть.

Все сразу

Этот код в клиенте немного хрупок, но, вероятно, никогда не сломается:

def on_GetPing_mouseClick(self, event):
    Client.send('~')
    retval = Client.recv()
    ping_data = retval.strip() # strip out the newline, if you read an entire line
    split_data = ping_data.split(',')
    L_Ping = split_data[0]
    R_Ping = split_data[1]
    self.components.PingLeft.text = str(L_Ping)
    self.components.PingRight.text = str(R_Ping)

Этот код предполагает, что recv() вызов вернет ровно одно сообщение протокола. Потоки TCP не работают таким образом, одноранговые узлы могут свободно отправлять исходящие данные в любом размере, который они чертовски хорошо, пожалуйста. (Стеки TCP/IP постоянно объединяют несколько сообщений уровня приложения в один пакет TCP. Они также отправляют пакеты меньше запрошенного, чтобы избежать фрагментации.)

Что было бы намного безопаснее, так это заполнить очередь содержимым, полученным от удаленного партнера, а затем проанализировать очередь для ваших команд / сообщений. Вы можете найти десять команд в очереди, вы можете найти только часть команды - но ваш код должен быть готов к тому, чтобы помещать частичные сообщения в очередь и потреблять полные сообщения из очереди, когда они доступны.

Это немного дополнительная работа, но она необходима для безопасной работы в неидеальных ситуациях. Вы можете никогда не столкнуться с проблемой в вашей локальной сети, но испытываете проблемы, когда вы переходите на беспроводную связь или маршрутизируете через большие сети.

Другие вопросы по тегам