Как получить информацию о сокете, если вы не знаете, сколько данных поступает?
Я работаю над графическим интерфейсом на основе 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. Они также отправляют пакеты меньше запрошенного, чтобы избежать фрагментации.)
Что было бы намного безопаснее, так это заполнить очередь содержимым, полученным от удаленного партнера, а затем проанализировать очередь для ваших команд / сообщений. Вы можете найти десять команд в очереди, вы можете найти только часть команды - но ваш код должен быть готов к тому, чтобы помещать частичные сообщения в очередь и потреблять полные сообщения из очереди, когда они доступны.
Это немного дополнительная работа, но она необходима для безопасной работы в неидеальных ситуациях. Вы можете никогда не столкнуться с проблемой в вашей локальной сети, но испытываете проблемы, когда вы переходите на беспроводную связь или маршрутизируете через большие сети.