141 lines
4.2 KiB
Python
Raw Normal View History

2019-02-09 19:22:37 -08:00
#!/usr/bin/env python3
2019-04-25 19:44:44 -07:00
import sys
2019-02-09 19:22:37 -08:00
import serial
2019-04-25 19:44:44 -07:00
import threading
2019-02-09 19:22:37 -08:00
from threading import Thread
import time
2019-05-09 10:22:04 -07:00
import binascii
from base64 import b64decode, b64encode
2019-02-09 19:22:37 -08:00
from e32 import E32
import messages_pb2 as messages
from google.protobuf.message import Message
class UGVComms(E32):
2019-05-09 10:22:04 -07:00
MAX_WRITE_RETRY = 5
RETRY_TIME = 1.0
2019-04-25 19:44:44 -07:00
2019-02-09 19:22:37 -08:00
def __init__(self, serial_port: serial.Serial):
E32.__init__(self, serial_port)
2019-04-25 19:44:44 -07:00
self.msg_acks = []
self.ack_cv = threading.Condition()
self.next_command_id = 1
self.last_status = None
self.rx_thread = None
2019-02-09 19:22:37 -08:00
2019-05-09 10:22:04 -07:00
def write_base64(self, data: bytes):
encoded = b64encode(data)
self.ser.write(encoded)
self.ser.write(b'\n')
2019-02-09 19:22:37 -08:00
def write_message(self, msg: Message):
2019-04-25 19:44:44 -07:00
print("writing message: ", msg)
2019-02-09 19:22:37 -08:00
data = msg.SerializeToString()
2019-05-09 10:22:04 -07:00
self.write_base64(data)
2019-02-09 19:22:37 -08:00
def write_command(self, command, retry=True):
2019-04-25 19:44:44 -07:00
cmdid = self.next_command_id
self.next_command_id += 1
gmsg = messages.GroundMessage()
if type(command) is int:
gmsg.command.type = command
else:
gmsg.command.CopyFrom(command)
2019-04-25 19:44:44 -07:00
gmsg.command.id = cmdid
self.write_message(gmsg)
last_write_time = time.time()
if not retry:
return
with self.ack_cv:
while True:
if cmdid in self.msg_acks:
self.msg_acks.remove(cmdid)
print("received ack for command")
return
time_left = time.time() - last_write_time
if time_left >= self.RETRY_TIME:
print("retry writing command")
self.write_message(gmsg)
last_write_time = time.time()
self.ack_cv.wait(timeout=time_left)
2019-02-09 19:22:37 -08:00
def read_message(self):
2019-05-09 10:22:04 -07:00
data = self.ser.read_until(terminator=b'\n')
if len(data) is 0:
2019-02-09 19:33:09 -08:00
return None
2019-05-09 10:22:04 -07:00
try:
decoded = b64decode(data, validate=True)
except binascii.Error:
print("read bad data: ", data)
2019-02-09 19:33:09 -08:00
self.ser.flush()
return None
2019-02-09 19:22:37 -08:00
msg = messages.UGV_Message()
2019-05-09 10:22:04 -07:00
msg.ParseFromString(decoded)
2019-02-09 19:22:37 -08:00
return msg
2019-04-25 19:44:44 -07:00
def process_message(self, msg: messages.UGV_Message):
if msg is None:
return
print("received UGV message: ", msg)
if msg.HasField("command_ack"):
with self.ack_cv:
self.msg_acks.append(msg.command_ack)
self.ack_cv.notify()
elif msg.HasField("status"):
self.last_status = msg.status
2019-05-09 10:22:04 -07:00
2019-04-25 19:44:44 -07:00
def start(self):
self.rx_thread = Thread(target=self.__rx_thread_entry, daemon=True)
self.rx_thread.start()
2019-02-09 19:22:37 -08:00
2019-04-25 19:44:44 -07:00
def stop(self):
self.rx_thread.join()
2019-02-09 19:22:37 -08:00
2019-04-25 19:44:44 -07:00
def __rx_thread_entry(self):
while self.ser.is_open:
try:
msg = self.read_message()
self.process_message(msg)
except serial.SerialException as e:
print("serial error: ", e, file=sys.stderr)
return
except Exception as e:
print("error reading message: ", e, file=sys.stderr)
continue
2019-02-09 19:22:37 -08:00
2019-05-09 10:22:04 -07:00
2019-04-25 19:44:44 -07:00
def main():
2019-05-08 21:33:09 -07:00
if len(sys.argv) >= 2:
ser_url = sys.argv[1]
else:
ser_url = "hwgrep://"
ser = serial.serial_for_url(ser_url, baudrate=9600, parity=serial.PARITY_NONE,
2019-02-09 19:22:37 -08:00
stopbits=serial.STOPBITS_ONE, bytesize=serial.EIGHTBITS,
2019-05-09 10:22:04 -07:00
timeout=0.5)
2019-02-09 19:22:37 -08:00
ugv = UGVComms(ser)
2019-04-25 19:44:44 -07:00
ugv.start()
2019-02-09 19:22:37 -08:00
time.sleep(0.2)
2019-02-09 19:48:52 -08:00
try:
while True:
if ugv.last_status is None or ugv.last_status.state is not messages.STATE_DRIVE_HEADING:
cmd = messages.GroundCommand()
cmd.type = messages.CMD_DRIVE_HEADING
cmd.drive_heading.heading = -90.0
cmd.drive_heading.power = 0.2
ugv.write_command(cmd)
time.sleep(1.0)
2019-02-09 19:48:52 -08:00
except KeyboardInterrupt:
2019-04-25 19:44:44 -07:00
ugv.write_command(messages.CMD_DISABLE)
2019-02-09 19:48:52 -08:00
print("exiting...")
finally:
2019-04-25 19:44:44 -07:00
ugv.ser.flush()
2019-02-09 19:48:52 -08:00
ugv.ser.close()
2019-04-25 19:44:44 -07:00
ugv.stop()
2019-02-09 19:22:37 -08:00
2019-05-09 10:22:04 -07:00
2019-04-25 19:44:44 -07:00
if __name__ == "__main__":
main()