Как получить информацию о сокете, если вы не знаете, сколько данных поступает? - PullRequest
3 голосов
/ 10 июня 2011

Я работаю над интерфейсом GUI на основе 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 Ответ

5 голосов
/ 10 июня 2011

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

Ожидание занято

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

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 . Они также отправляют пакеты меньше , чем запрошено, чтобы избежать фрагментации . )

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

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

...