From e89024a911e5de6363a8745c57530800e69a7dbd Mon Sep 17 00:00:00 2001 From: Andrej Volckov <125829671+AndrejVD@users.noreply.github.com> Date: Wed, 18 Oct 2023 07:01:51 +0100 Subject: [PATCH 1/5] messages and two sensors --- app/content/messages/response_message.py | 99 +++++++++++++ app/content/messages/smessages.py | 34 +++++ app/content/microcontroller/arduino_serial.py | 130 +++++++++++++----- app/content/motor_commands/open.py | 44 ++++++ app/content/sensors/arduino/igniter.py | 15 +- app/content/sensors/arduino/photoresistor.py | 69 ++++++++++ app/content/sensors/arduino/servo.py | 17 ++- .../sensors/arduino/temperaturesensor.py | 69 ++++++++++ app/rockets/make_spatula.py | 5 +- 9 files changed, 440 insertions(+), 42 deletions(-) create mode 100644 app/content/messages/response_message.py create mode 100644 app/content/messages/smessages.py create mode 100644 app/content/sensors/arduino/photoresistor.py create mode 100644 app/content/sensors/arduino/temperaturesensor.py diff --git a/app/content/messages/response_message.py b/app/content/messages/response_message.py new file mode 100644 index 0000000..66ebff3 --- /dev/null +++ b/app/content/messages/response_message.py @@ -0,0 +1,99 @@ + +from typing import Collection, Iterable, Tuple, Type, Union, cast + + +class AReceivedMessage: + partCode : float + + def __init__(self, partCode : bytes = 0x00): + self.partCode = partCode + +class CommunicationErrors: + result : dict + + def __init__(self): + self.result = dict() + self.result[0x00] = 'Success' + + self.result[0x01] = 'Incompatible Launch Phase' + + self.result[0x10] = 'Message Layout Error! Wrong Start Byte' + self.result[0x11] = 'Message Layout Error! Wrong Connection Byte' + self.result[0x12] = 'Message Layout Error! Wrong State Byte' + self.result[0x13] = 'Message Layout Error! WrongPartByte' + self.result[0x14] = 'Message Layout Error! WrongCommandByte' + self.result[0x15] = 'Message Layout Error! WrongEndByte' + + def __getitem__(self, item : bytes) -> str: + return self.result[item] + +class ResponseM(AReceivedMessage): + + commandCode : bytes + + resultCode : bytes + + def __init__(self, message : bytearray): + super().__init__(message[3]) + self.commandCode = message[4] + self.resultCode = message[5] + + def get_result(self) -> str: + ce = CommunicationErrors() + return ce[self.resultCode] + +class SensorDataMessage(AReceivedMessage): + data : float + partCode : float + + def __init__(self, message : bytearray): + super().__init__(message[3]) + + dataSize = int(message[4]) + self.data = float(message[5: dataSize + 5]) + + def get_data(self): + return self.data + + +class ArduinoStateMessage(AReceivedMessage): + + def __init__(self, message : bytearray): + super().__init__() + + + + +class ResponseMessage: + message : bytearray + + + def __init__(self): + self.message = bytearray([]) + + def parse(self, received_msg : bytearray) -> Union[ResponseM, SensorDataMessage, ArduinoStateMessage, None]: + msg = None + for i in received_msg: + if len(self.message) and self.message[-1] != 0x7E and i == 0x7E: + self.message.append(i) + msg = self.checkCommandType() + self.message = bytearray([]) + else: + self.message.append(i) + + return msg + + + def checkCommandType(self) -> Union[ResponseM, SensorDataMessage, ArduinoStateMessage, None]: + if(len(self.message)): + print(self.message) + if self.message[2] == 0x00: + return ResponseM(self.message) + + elif self.message[2] == 0x01: + return ArduinoStateMessage(self.message) + + elif self.message[2] == 0x02: + return SensorDataMessage(self.message) + + return None diff --git a/app/content/messages/smessages.py b/app/content/messages/smessages.py new file mode 100644 index 0000000..9840901 --- /dev/null +++ b/app/content/messages/smessages.py @@ -0,0 +1,34 @@ +class Message: + partCode : bytes + commandCode : bytes + + def __init__(self, partCode: chr = 0x00, commandCode: chr = 0x00): + self.partCode = partCode + self.commandCode = commandCode + + def getMessage(self): + messageStart = messageEnd = 0x7E + direction = messageType = 0x00 + + message = [messageStart, direction, messageType, self.partCode, self.commandCode, messageEnd] + return bytearray(message) + + +class AMessageList: + partCode : chr + messageDict : dict() + + def __init__(self, partCode: chr = 0x00): + self.partCode = partCode + self.messageDict = dict() + + def addCommandMessage(self, commandName: str, commandCode: chr): + self.messageDict[commandName] = Message(self.partCode, commandCode) + + def sendCommand(self, messageNmae: str) -> bytearray : + return self.messageDict[messageNmae].getMessage() + + def __getitem__(self, key : str)-> bytearray: + return self.messageDict[key].getMessage() + + diff --git a/app/content/microcontroller/arduino_serial.py b/app/content/microcontroller/arduino_serial.py index b769d7b..6c250ef 100644 --- a/app/content/microcontroller/arduino_serial.py +++ b/app/content/microcontroller/arduino_serial.py @@ -10,12 +10,18 @@ from app.content.motor_commands.open import OpenCommand, CloseCommand, IgniteCommand from app.logic.commands.command import Command, Command from app.content.general_commands.enable import DisableCommand, EnableCommand, ResetCommand +from app.content.motor_commands.open import SetIgnitionPhaseCommand, SetLiftoffPhaseCommand, SetRecoveryPhaseCommand +from app.content.motor_commands.open import SetPreparationPhaseCommand from app.logic.rocket_definition import Part, Rocket from kivy.utils import platform import tinyproto +from app.content.messages.response_message import ResponseM, ArduinoStateMessage, SensorDataMessage, ResponseMessage + + +from app.content.messages.smessages import AMessageList if platform == 'android': from usb4a import usb @@ -39,6 +45,8 @@ def to_bytes(self): return [*self.index.to_bytes(), *self.command.to_bytes(), *self.payload_size.to_bytes(), *self.payload] + + class ArduinoSerial(Part): type = 'Microcontroller.Arduino.Serial' @@ -87,6 +95,13 @@ class ArduinoSerial(Part): commands_list = [] + messageList : AMessageList + + temperature : float + + lightValue : int + + launchPhase : str def __init__(self, _id: UUID, name: str, parent: Union[Part, Rocket, None], start_enabled = True): self.part_state = None @@ -99,6 +114,16 @@ def __init__(self, _id: UUID, name: str, parent: Union[Part, Rocket, None], star self.hdlc = None + self.messageList = AMessageList(0x00) + self.messageList.addCommandMessage('Reset', 0x00) + self.messageList.addCommandMessage('Preparation', 0x01) + self.messageList.addCommandMessage('Ignition', 0x02) + self.messageList.addCommandMessage('Liftoff', 0x03) + self.messageList.addCommandMessage('Recovery', 0x04) + + self.temperature = 0.0 + self.lightValue = 0 + self.launchPhase = 'Preparation' def try_get_device_list(self): @@ -211,10 +236,15 @@ def on_read(b: bytes): self.last_message = package + + message = ResponseMessage() + hdlc.on_read = on_read hdlc.crc = 8 hdlc.begin() + + try: while True: @@ -226,18 +256,9 @@ def on_read(b: bytes): self.serial_port.in_waiting ) if received_msg: - print(received_msg) - # hdlc.rx(received_msg) - - - for i in received_msg: - if len(self.current_message) and self.current_message[-1] != 0x7E and i == 0x7E: - self.current_message.append(i) - self.logs.append(self.current_message) - self.parse() - self.current_message = bytearray([]) - else: - self.current_message.append(i) + response = message.parse(received_msg) + if response: + self.action(response) except Exception as ex: self.connected = False @@ -248,27 +269,33 @@ def on_read(b: bytes): print(f'crash read thread {ex.args[0]}') raise ex - def parse(self): - part = self.current_message[3] - command = self.current_message[4] - success_bit = self.current_message[5] - success = success_bit == 0x01 + def action(self, response): - if self.response_future is None or self.response_future.done(): - return + if isinstance(response, ResponseM): + self.part_state = 'success' + + if self.response_future is None or self.response_future.done(): + return + + result = response.get_result() + if result == 'Success': + self.response_future.set_result(result) + else: + self.response_future.set_exception(Exception(result)) + + elif isinstance(response, SensorDataMessage): + if response.partCode == 0x50: + self.temperature = response.get_data() + elif response.partCode == 0x52: + self.lightValue = int(response.get_data()) + + elif isinstance(response, ArduinoStateMessage): + self.send_message_hdlc(self.messageList[self.launchPhase]) - if part != self.expected_next_response_part: - self.response_future.set_exception(Exception('Received response from arduino for wrong part (commands where send to fast)')) - elif command != self.expected_next_response_command: - self.response_future.set_exception(Exception('Received response from arduino for wrong command (commands where send to fast)')) - elif not success: - self.response_future.set_exception(Exception('The arduino send back that execution of the command was unsuccessful')) - else: - self.response_future.set_result(None) def get_accepted_commands(self) -> list[Type[Command]]: - return [EnableCommand, DisableCommand, ResetCommand] + return [EnableCommand, DisableCommand, ResetCommand, SetPreparationPhaseCommand, SetIgnitionPhaseCommand, SetLiftoffPhaseCommand, SetRecoveryPhaseCommand] def send_message_hdlc(self, message: bytearray): if self.serial_port is None or self.hdlc is None: @@ -277,7 +304,7 @@ def send_message_hdlc(self, message: bytearray): self.hdlc.put(message) self.serial_port.write(self.hdlc.tx()) - def send_message(self, part: int, command: int): + def send_message(self, message : bytearray): '''Sends the given message to the arduino and returns a future that will be completed if the command got processed. If the command did not get processed or the connection dies the future will throw''' @@ -287,32 +314,65 @@ def send_message(self, part: int, command: int): future = asyncio.Future() self.response_future = future - self.expected_next_response_part = part - self.expected_next_response_command = command try: - self.send_message_hdlc(bytearray([0x7E, 0xFF, 0x4F, part, command, 0x7E])) + self.send_message_hdlc(message) except Exception as e: future.set_exception(e) return future - def reset_arduino(self): - return self.send_message(0x00, 0x01) def update(self, commands: Iterable[Command], now, iteration): for c in commands: + if c.state == 'processing' and self.last_command != c: + c.state = 'failed' + c.response_message = 'Another ignite command was send, this command will no longer be processed' + continue + + if c.state == 'processing' and self.last_ignite_future is not None and self.last_ignite_future.done(): + exception = self.last_ignite_future.exception() + if exception is not None: + c.state = 'failed' + c.response_message = exception.args[0] + continue + + if self.last_ignite_future.result() == "Success": + c.state = 'success' + c.response_message = 'success' + + if isinstance(c, SetIgnitionPhaseCommand): + self.launchPhase = 'Ignition' + elif isinstance(c, SetPreparationPhaseCommand): + self.launchPhase = 'Preparation' + + if isinstance(c, EnableCommand): self.enabled = True c.state = "success" elif isinstance(c, DisableCommand): self.enabled = False c.state = "success" + elif isinstance(c, ResetCommand): - self.reset_arduino() + self.send_message(self.messageList['Reset']) c.state = "success" + + elif isinstance(c, SetPreparationPhaseCommand): + if c.state == 'received': + self.last_command = c + self.last_ignite_future = self.send_message(self.messageList['Preparation']) + c.state = 'processing' + + + elif isinstance(c, SetIgnitionPhaseCommand): + if c.state == 'received': + self.last_command = c + self.last_ignite_future = self.send_message(self.messageList['Ignition']) + c.state = 'processing' + else: c.state = 'failed' # Part cannot handle this command continue diff --git a/app/content/motor_commands/open.py b/app/content/motor_commands/open.py index db7fb71..cdeff01 100644 --- a/app/content/motor_commands/open.py +++ b/app/content/motor_commands/open.py @@ -34,5 +34,49 @@ class IgniteCommand(Command): response_schema = BasicErrorResponseSchema() + def set_payload(self, payload): + pass + +class SetPreparationPhaseCommand(Command): + + command_type = 'Control.SetPreparationPhase' + + payload_schema = None + + response_schema = BasicErrorResponseSchema() + + def set_payload(self, payload): + pass + +class SetIgnitionPhaseCommand(Command): + + command_type = 'Control.SetIgnitionPhasePhase' + + payload_schema = None + + response_schema = BasicErrorResponseSchema() + + def set_payload(self, payload): + pass + +class SetLiftoffPhaseCommand(Command): + + command_type = 'Control.SetLiftoffPhase' + + payload_schema = None + + response_schema = BasicErrorResponseSchema() + + def set_payload(self, payload): + pass + +class SetRecoveryPhaseCommand(Command): + + command_type = 'Control.SetRecoveryPhase' + + payload_schema = None + + response_schema = BasicErrorResponseSchema() + def set_payload(self, payload): pass \ No newline at end of file diff --git a/app/content/sensors/arduino/igniter.py b/app/content/sensors/arduino/igniter.py index b293b97..4d280f0 100644 --- a/app/content/sensors/arduino/igniter.py +++ b/app/content/sensors/arduino/igniter.py @@ -14,6 +14,8 @@ from kivy.utils import platform +from app.content.messages.smessages import AMessageList + class IgniterSensor(Part): type = 'Igniter' @@ -38,6 +40,9 @@ class IgniterSensor(Part): state: str + messageList : AMessageList + + def __init__(self, _id: UUID, name: str, parent: Union[Part, Rocket, None], arduino_parent: Union[ArduinoSerial, None], parachute: ServoSensor, start_enabled=True): self.arduino = arduino_parent self.enabled = start_enabled @@ -45,6 +50,11 @@ def __init__(self, _id: UUID, name: str, parent: Union[Part, Rocket, None], ardu self.parachute = parachute super().__init__(_id, name, parent, list()) # type: ignore + self.messageList = AMessageList(0x02) + self.messageList.addCommandMessage('Ignite', 0x00) + + self.messageList2 = AMessageList(0x01) + self.messageList2.addCommandMessage('Open', 0x01) def get_accepted_commands(self) -> list[Type[Command]]: return [IgniteCommand] @@ -60,7 +70,7 @@ def update(self, commands: Iterable[Command], now, iteration): if c.state == 'received': self.last_command = c - self.last_ignite_future = self.arduino.send_message(0x02, 0x01) + self.last_ignite_future = self.arduino.send_message(self.messageList['Ignite']) self.last_ignited = now self.parachute_triggered = False c.state = 'processing' @@ -78,6 +88,7 @@ def update(self, commands: Iterable[Command], now, iteration): continue c.state = 'success' c.response_message = 'Igniter triggered' + self.arduino.launchPhase = 'Liftoff' else: c.state = 'failed' # Part cannot handle this command @@ -86,7 +97,7 @@ def update(self, commands: Iterable[Command], now, iteration): if self.arduino is not None and self.last_ignited is not None and not self.parachute_triggered and (now - self.last_ignited) >= self.deploy_parachute_delay: self.parachute_triggered = True - self.arduino.send_message(0x01, 0x04) + self.arduino.send_message(self.messageList['Open']) # def add_command_to_queue(command_code: int, payload): diff --git a/app/content/sensors/arduino/photoresistor.py b/app/content/sensors/arduino/photoresistor.py new file mode 100644 index 0000000..2c9acac --- /dev/null +++ b/app/content/sensors/arduino/photoresistor.py @@ -0,0 +1,69 @@ +from asyncio import Future, Task +from datetime import timedelta +from typing import Collection, Iterable, Tuple, Type, Union, cast +from uuid import UUID + +from dataclasses import dataclass +from app.content.general_commands.enable import DisableCommand, EnableCommand +from app.content.motor_commands.open import OpenCommand, CloseCommand, IgniteCommand +from app.logic.commands.command import Command, Command +from app.content.general_commands.enable import DisableCommand, EnableCommand, ResetCommand +from app.content.microcontroller.arduino_serial import ArduinoSerial +from app.logic.rocket_definition import Part, Rocket + +from kivy.utils import platform + +from app.content.messages.smessages import AMessageList + +class PhotoresistorSensor(Part): + type = 'Photoresistor' + + enabled: bool = True + + min_update_period = timedelta(milliseconds=20) + + min_measurement_period = timedelta(milliseconds=1000) + + arduino: Union[ArduinoSerial, None] + + last_ignite_future: Union[Future, None] = None + + last_command: Union[None, Command] = None + + lightVal : int + + def __init__(self, _id: UUID, name: str, parent: Union[Part, Rocket, None], arduino_parent: Union[ArduinoSerial, None],start_enabled=True): + self.arduino = arduino_parent + self.enabled = start_enabled + + super().__init__(_id, name, parent, list()) # type: ignore + + self.lightVal = 0 + + + def get_accepted_commands(self) -> list[Type[Command]]: + return [EnableCommand, DisableCommand] + + def update(self, commands: Iterable[Command], now, iteration): + + for c in commands: + + if isinstance(c, EnableCommand): + self.enabled = True + c.state = "success" + + elif isinstance(c, DisableCommand): + self.enabled = False + c.state = "success" + + + self.lightVal = self.arduino.lightValue + + def get_measurement_shape(self) -> Iterable[Tuple[str, Type]]: + return [ + ('light', float), + ] + + def collect_measurements(self, now, iteration) -> Iterable[Iterable[float]]: + return [[self.lightVal]] + diff --git a/app/content/sensors/arduino/servo.py b/app/content/sensors/arduino/servo.py index 601dcb8..938e5e0 100644 --- a/app/content/sensors/arduino/servo.py +++ b/app/content/sensors/arduino/servo.py @@ -13,6 +13,7 @@ from kivy.utils import platform +from app.content.messages.smessages import AMessageList class ServoSensor(Part): type = 'Servo' @@ -31,12 +32,18 @@ class ServoSensor(Part): last_command: Union[None, Command] = None + messageList : AMessageList + def __init__(self, _id: UUID, name: str, parent: Union[Part, Rocket, None], arduino_parent: Union[ArduinoSerial, None],start_enabled=True): self.arduino = arduino_parent self.enabled = start_enabled self.state = 'close' super().__init__(_id, name, parent, list()) # type: ignore + self.messageList = AMessageList(0x01) + self.messageList.addCommandMessage('Close', 0x00) + self.messageList.addCommandMessage('Open', 0x01) + def get_accepted_commands(self) -> list[Type[Command]]: return [OpenCommand, CloseCommand] @@ -61,21 +68,23 @@ def update(self, commands: Iterable[Command], now, iteration): c.state = 'failed' c.response_message = exception.args[0] continue - c.state = 'success' - c.response_message = 'Servo actuated' + if self.last_ignite_future.result() == "Success": + c.state = 'success' + c.response_message = 'Servo activated' + if isinstance(c, CloseCommand): if c.state == 'received': self.last_command = c - self.last_ignite_future = self.arduino.send_message(0x01, 0x03) + self.last_ignite_future = self.arduino.send_message(self.messageList["Close"]) c.state = 'processing' elif isinstance(c, OpenCommand): if c.state == 'received': self.last_command = c - self.last_ignite_future = self.arduino.send_message(0x01, 0x04) + self.last_ignite_future = self.arduino.send_message(self.messageList["Open"]) c.state = 'processing' else: diff --git a/app/content/sensors/arduino/temperaturesensor.py b/app/content/sensors/arduino/temperaturesensor.py new file mode 100644 index 0000000..34bf6d0 --- /dev/null +++ b/app/content/sensors/arduino/temperaturesensor.py @@ -0,0 +1,69 @@ +from asyncio import Future, Task +from datetime import timedelta +from typing import Collection, Iterable, Tuple, Type, Union, cast +from uuid import UUID + +from dataclasses import dataclass +from app.content.general_commands.enable import DisableCommand, EnableCommand +from app.content.motor_commands.open import OpenCommand, CloseCommand, IgniteCommand +from app.logic.commands.command import Command, Command +from app.content.general_commands.enable import DisableCommand, EnableCommand, ResetCommand +from app.content.microcontroller.arduino_serial import ArduinoSerial +from app.logic.rocket_definition import Part, Rocket + +from kivy.utils import platform + +from app.content.messages.smessages import AMessageList + +class TemperatureSensor(Part): + type = 'Temperature' + + enabled: bool = True + + min_update_period = timedelta(milliseconds=20) + + min_measurement_period = timedelta(milliseconds=1000) + + arduino: Union[ArduinoSerial, None] + + + last_ignite_future: Union[Future, None] = None + + last_command: Union[None, Command] = None + + temperature : float + + def __init__(self, _id: UUID, name: str, parent: Union[Part, Rocket, None], arduino_parent: Union[ArduinoSerial, None],start_enabled=True): + self.arduino = arduino_parent + self.enabled = start_enabled + + super().__init__(_id, name, parent, list()) # type: ignore + + self.temperature = 0.0 + + + def get_accepted_commands(self) -> list[Type[Command]]: + return [EnableCommand, DisableCommand] + + def update(self, commands: Iterable[Command], now, iteration): + + for c in commands: + + if isinstance(c, EnableCommand): + self.enabled = True + c.state = "success" + + elif isinstance(c, DisableCommand): + self.enabled = False + c.state = "success" + + self.temperature = self.arduino.temperature + + def get_measurement_shape(self) -> Iterable[Tuple[str, Type]]: + return [ + ('temperature', float), + ] + + def collect_measurements(self, now, iteration) -> Iterable[Iterable[float]]: + return [[self.temperature]] + diff --git a/app/rockets/make_spatula.py b/app/rockets/make_spatula.py index b6fc6ef..d96e485 100755 --- a/app/rockets/make_spatula.py +++ b/app/rockets/make_spatula.py @@ -21,6 +21,8 @@ from app.content.sensors.arduino.servo import ServoSensor from app.content.sensors.arduino.igniter import IgniterSensor +from app.content.sensors.arduino.temperaturesensor import TemperatureSensor +from app.content.sensors.arduino.photoresistor import PhotoresistorSensor from app.content.sensors.plyer.spatial_orientation_plyer import PlyerSpatialOrientationSensor @@ -61,7 +63,8 @@ def make_spatula() -> FlightConfig: arduino_serial = ArduinoSerial(UUID('cd170fff-0138-4820-8e97-969eb3f2f287'), 'Serial Port', rocket) parachute = ServoSensor(UUID('9f86acb1-9795-46fc-b083-e6451f214d1f'), 'Servo', rocket, arduino_serial) igniter = IgniterSensor(UUID('f309669d-6bd7-4ee3-90a5-45a0e1bdd60e'), 'Igniter', rocket, arduino_serial, parachute) - + temperature = TemperatureSensor(UUID('ac93964a-6bb0-11ee-b962-0242ac120002'), 'Temperature', rocket, arduino_serial) + photoresistor = PhotoresistorSensor(UUID('158314cc-6d1f-11ee-b962-0242ac120002'), 'Photoresistor', rocket, arduino_serial) # FlightDirector(UUID('37155a2c-c51d-41b7-9dae-67d640d8c284'), 'Flight Director', rocket, arduino_serial, igniter, parachute, acc, gyro, inertialFrame) return FlightConfig(rocket, [ArduinoSerialSelectUI(arduino_serial), ArduinoSerialMonitorUI(arduino_serial)], True) From ef8767350c81379b50c58be469b2e13f3c4d8dc6 Mon Sep 17 00:00:00 2001 From: Andrej Volckov <125829671+AndrejVD@users.noreply.github.com> Date: Wed, 1 Nov 2023 15:28:46 +0000 Subject: [PATCH 2/5] new sensors --- .gitignore | 77 ++++++++++++++++++- app/content/messages/response_message.py | 8 +- app/content/microcontroller/arduino_serial.py | 40 ++++++++-- app/content/sensors/arduino/altitude.py | 70 +++++++++++++++++ app/content/sensors/arduino/photoresistor.py | 18 +++-- app/content/sensors/arduino/pressure.py | 69 +++++++++++++++++ .../sensors/arduino/temperaturesensor.py | 3 + app/rockets/make_spatula.py | 8 +- 8 files changed, 277 insertions(+), 16 deletions(-) create mode 100644 app/content/sensors/arduino/altitude.py create mode 100644 app/content/sensors/arduino/pressure.py diff --git a/.gitignore b/.gitignore index 076cf0c..6c2d44d 100755 --- a/.gitignore +++ b/.gitignore @@ -6,4 +6,79 @@ Lib/ .idea/ -vessel_info.json \ No newline at end of file +vessel_info.json +share/sdl2/bin/LICENSE.png.txt +share/sdl2/bin/LICENSE.opusfile.txt +share/sdl2/bin/zlib1.dll +share/sdl2/bin/SDL2_ttf.dll +share/sdl2/bin/SDL2_mixer.dll +share/sdl2/bin/SDL2_image.dll +share/sdl2/bin/SDL2.dll +share/sdl2/bin/LICENSE.zlib.txt +share/sdl2/bin/LICENSE.webp.txt +share/sdl2/bin/LICENSE.tiff.txt +share/sdl2/bin/LICENSE.opus.txt +share/sdl2/bin/LICENSE.ogg-vorbis.txt +share/sdl2/bin/LICENSE.mpg123.txt +share/sdl2/bin/LICENSE.modplug.txt +share/sdl2/bin/LICENSE.jpeg.txt +share/sdl2/bin/LICENSE.harfbuzz.txt +share/sdl2/bin/LICENSE.freetype.txt +share/sdl2/bin/LICENSE.FLAC.txt +share/sdl2/bin/libwebp-7.dll +share/sdl2/bin/libvorbisfile-3.dll +share/sdl2/bin/libvorbis-0.dll +share/sdl2/bin/libtiff-5.dll +share/sdl2/bin/libpng16-16.dll +share/sdl2/bin/libopusfile-0.dll +share/sdl2/bin/libopus-0.dll +share/sdl2/bin/libogg-0.dll +share/sdl2/bin/libmpg123-0.dll +share/sdl2/bin/libmodplug-1.dll +share/sdl2/bin/libjpeg-9.dll +share/sdl2/bin/libFLAC-8.dll +share/glew/bin/glew32.dll +share/angle/bin/libGLESv2.dll +share/angle/bin/libEGL.dll +share/angle/bin/d3dcompiler_47.dll +Scripts/wsdump.exe +Scripts/rstpep2html.py +Scripts/rst2xml.py +Scripts/rst2xetex.py +Scripts/rst2s5.py +Scripts/rst2pseudoxml.py +Scripts/rst2odt_prepstyles.py +Scripts/rst2odt.py +Scripts/rst2man.py +Scripts/rst2latex.py +Scripts/rst2html5.py +Scripts/rst2html4.py +Scripts/rst2html.py +Scripts/pywin32_testall.py +Scripts/pywin32_postinstall.py +Scripts/pythonw.exe +Scripts/python.exe +Scripts/pyserial-ports.exe +Scripts/pyserial-miniterm.exe +Scripts/pygmentize.exe +Scripts/pip3.exe +Scripts/pip3.10.exe +Scripts/pip.exe +Scripts/pasteurize.exe +Scripts/pasteurize-script.py +Scripts/normalizer.exe +Scripts/jsonschema.exe +Scripts/httpx.exe +Scripts/garden.bat +Scripts/garden +Scripts/futurize.exe +Scripts/futurize-script.py +Scripts/f2py.exe +Scripts/docutils.exe +Scripts/deactivate.bat +Scripts/Activate.ps1 +Scripts/activate.bat +Scripts/activate +pyvenv.cfg +Include/site/python3.10/greenlet/greenlet.h +submodules/tinyproto diff --git a/app/content/messages/response_message.py b/app/content/messages/response_message.py index 66ebff3..b379134 100644 --- a/app/content/messages/response_message.py +++ b/app/content/messages/response_message.py @@ -45,16 +45,20 @@ def get_result(self) -> str: class SensorDataMessage(AReceivedMessage): data : float partCode : float + dataPart : float def __init__(self, message : bytearray): super().__init__(message[3]) - dataSize = int(message[4]) - self.data = float(message[5: dataSize + 5]) + dataSize = int(message[5]) + self.data = float(message[6: dataSize + 6]) + self.dataPart = message[4] def get_data(self): return self.data + def get_data_part(self): + return self.dataPart class ArduinoStateMessage(AReceivedMessage): diff --git a/app/content/microcontroller/arduino_serial.py b/app/content/microcontroller/arduino_serial.py index 6c250ef..6bf5cde 100644 --- a/app/content/microcontroller/arduino_serial.py +++ b/app/content/microcontroller/arduino_serial.py @@ -99,7 +99,15 @@ class ArduinoSerial(Part): temperature : float - lightValue : int + pressure : float + + altitude : float + + xOrientation : float + + yOrientation : float + + zOrientation : float launchPhase : str @@ -121,8 +129,9 @@ def __init__(self, _id: UUID, name: str, parent: Union[Part, Rocket, None], star self.messageList.addCommandMessage('Liftoff', 0x03) self.messageList.addCommandMessage('Recovery', 0x04) - self.temperature = 0.0 - self.lightValue = 0 + self.temperature = self.pressure = self.altitude = 0.0 + self.xOrientation = self.yOrientation = self.zOrientation = 0.0 + self.launchPhase = 'Preparation' @@ -285,10 +294,27 @@ def action(self, response): self.response_future.set_exception(Exception(result)) elif isinstance(response, SensorDataMessage): - if response.partCode == 0x50: - self.temperature = response.get_data() - elif response.partCode == 0x52: - self.lightValue = int(response.get_data()) + dataPart = response.get_data_part() + if response.partCode == 0x53: + if dataPart == 0x01: + self.temperature = response.get_data() + + elif dataPart == 0x02: + self.pressure = response.get_data() + + elif dataPart == 0x03: + self.altitude = response.get_data() + + + elif response.partCode == 0x54: + if dataPart == 0x01: + self.xOrientation = response.get_data() + + elif dataPart == 0x02: + self.yOrientation = response.get_data() + + elif dataPart == 0x03: + self.zOrientation = response.get_data() elif isinstance(response, ArduinoStateMessage): self.send_message_hdlc(self.messageList[self.launchPhase]) diff --git a/app/content/sensors/arduino/altitude.py b/app/content/sensors/arduino/altitude.py new file mode 100644 index 0000000..5c30bfb --- /dev/null +++ b/app/content/sensors/arduino/altitude.py @@ -0,0 +1,70 @@ +from asyncio import Future, Task +from datetime import timedelta +from typing import Collection, Iterable, Tuple, Type, Union, cast +from uuid import UUID + +from dataclasses import dataclass +from app.content.general_commands.enable import DisableCommand, EnableCommand +from app.content.motor_commands.open import OpenCommand, CloseCommand, IgniteCommand +from app.logic.commands.command import Command, Command +from app.content.general_commands.enable import DisableCommand, EnableCommand, ResetCommand +from app.content.microcontroller.arduino_serial import ArduinoSerial +from app.logic.rocket_definition import Part, Rocket + +from kivy.utils import platform + +from app.content.messages.smessages import AMessageList + +class AltitudeSensor(Part): + type = 'Altitude' + + enabled: bool = True + + min_update_period = timedelta(milliseconds=20) + + min_measurement_period = timedelta(milliseconds=1000) + + arduino: Union[ArduinoSerial, None] + + + last_ignite_future: Union[Future, None] = None + + last_command: Union[None, Command] = None + + altitude : float + + def __init__(self, _id: UUID, name: str, parent: Union[Part, Rocket, None], arduino_parent: Union[ArduinoSerial, None],start_enabled=True): + self.arduino = arduino_parent + self.enabled = start_enabled + + super().__init__(_id, name, parent, list()) # type: ignore + + self.altitude = 0.0 + + + + def get_accepted_commands(self) -> list[Type[Command]]: + return [EnableCommand, DisableCommand] + + def update(self, commands: Iterable[Command], now, iteration): + + for c in commands: + + if isinstance(c, EnableCommand): + self.enabled = True + c.state = "success" + + elif isinstance(c, DisableCommand): + self.enabled = False + c.state = "success" + + self.altitude = self.arduino.altitude + + def get_measurement_shape(self) -> Iterable[Tuple[str, Type]]: + return [ + ('altitude', float), + ] + + def collect_measurements(self, now, iteration) -> Iterable[Iterable[float]]: + return [[self.altitude]] + diff --git a/app/content/sensors/arduino/photoresistor.py b/app/content/sensors/arduino/photoresistor.py index 2c9acac..e0c0293 100644 --- a/app/content/sensors/arduino/photoresistor.py +++ b/app/content/sensors/arduino/photoresistor.py @@ -30,7 +30,10 @@ class PhotoresistorSensor(Part): last_command: Union[None, Command] = None - lightVal : int + + xOrientation : float + yOrientation : float + zOrientation : float def __init__(self, _id: UUID, name: str, parent: Union[Part, Rocket, None], arduino_parent: Union[ArduinoSerial, None],start_enabled=True): self.arduino = arduino_parent @@ -38,7 +41,7 @@ def __init__(self, _id: UUID, name: str, parent: Union[Part, Rocket, None], ardu super().__init__(_id, name, parent, list()) # type: ignore - self.lightVal = 0 + self.xOrientation = self.yOrientation = self.zOrientation = 0.0 def get_accepted_commands(self) -> list[Type[Command]]: @@ -57,13 +60,18 @@ def update(self, commands: Iterable[Command], now, iteration): c.state = "success" - self.lightVal = self.arduino.lightValue + self.xOrientation = self.arduino.xOrientation + self.yOrientation = self.arduino.yOrientation + self.zOrientation = self.arduino.zOrientation + def get_measurement_shape(self) -> Iterable[Tuple[str, Type]]: return [ - ('light', float), + ('X', float), + ('Y', float), + ('Z', float), ] def collect_measurements(self, now, iteration) -> Iterable[Iterable[float]]: - return [[self.lightVal]] + return [[self.xOrientation, self.yOrientation, self.zOrientation]] diff --git a/app/content/sensors/arduino/pressure.py b/app/content/sensors/arduino/pressure.py new file mode 100644 index 0000000..eca5ff5 --- /dev/null +++ b/app/content/sensors/arduino/pressure.py @@ -0,0 +1,69 @@ +from asyncio import Future, Task +from datetime import timedelta +from typing import Collection, Iterable, Tuple, Type, Union, cast +from uuid import UUID + +from dataclasses import dataclass +from app.content.general_commands.enable import DisableCommand, EnableCommand +from app.content.motor_commands.open import OpenCommand, CloseCommand, IgniteCommand +from app.logic.commands.command import Command, Command +from app.content.general_commands.enable import DisableCommand, EnableCommand, ResetCommand +from app.content.microcontroller.arduino_serial import ArduinoSerial +from app.logic.rocket_definition import Part, Rocket + +from kivy.utils import platform + +from app.content.messages.smessages import AMessageList + +class PressureSensor(Part): + type = 'Pressure' + + enabled: bool = True + + min_update_period = timedelta(milliseconds=20) + + min_measurement_period = timedelta(milliseconds=1000) + + arduino: Union[ArduinoSerial, None] + + + last_ignite_future: Union[Future, None] = None + + last_command: Union[None, Command] = None + + pressure : float + + def __init__(self, _id: UUID, name: str, parent: Union[Part, Rocket, None], arduino_parent: Union[ArduinoSerial, None],start_enabled=True): + self.arduino = arduino_parent + self.enabled = start_enabled + + super().__init__(_id, name, parent, list()) # type: ignore + + self.pressure = 0.0 + + + def get_accepted_commands(self) -> list[Type[Command]]: + return [EnableCommand, DisableCommand] + + def update(self, commands: Iterable[Command], now, iteration): + + for c in commands: + + if isinstance(c, EnableCommand): + self.enabled = True + c.state = "success" + + elif isinstance(c, DisableCommand): + self.enabled = False + c.state = "success" + + self.pressure = self.arduino.pressure + + def get_measurement_shape(self) -> Iterable[Tuple[str, Type]]: + return [ + ('pressure', float), + ] + + def collect_measurements(self, now, iteration) -> Iterable[Iterable[float]]: + return [[self.pressure]] + diff --git a/app/content/sensors/arduino/temperaturesensor.py b/app/content/sensors/arduino/temperaturesensor.py index 34bf6d0..9da3903 100644 --- a/app/content/sensors/arduino/temperaturesensor.py +++ b/app/content/sensors/arduino/temperaturesensor.py @@ -42,6 +42,9 @@ def __init__(self, _id: UUID, name: str, parent: Union[Part, Rocket, None], ardu self.temperature = 0.0 + + + def get_accepted_commands(self) -> list[Type[Command]]: return [EnableCommand, DisableCommand] diff --git a/app/rockets/make_spatula.py b/app/rockets/make_spatula.py index d96e485..8471c43 100755 --- a/app/rockets/make_spatula.py +++ b/app/rockets/make_spatula.py @@ -22,6 +22,9 @@ from app.content.sensors.arduino.servo import ServoSensor from app.content.sensors.arduino.igniter import IgniterSensor from app.content.sensors.arduino.temperaturesensor import TemperatureSensor +from app.content.sensors.arduino.pressure import PressureSensor +from app.content.sensors.arduino.altitude import AltitudeSensor + from app.content.sensors.arduino.photoresistor import PhotoresistorSensor from app.content.sensors.plyer.spatial_orientation_plyer import PlyerSpatialOrientationSensor @@ -64,7 +67,10 @@ def make_spatula() -> FlightConfig: parachute = ServoSensor(UUID('9f86acb1-9795-46fc-b083-e6451f214d1f'), 'Servo', rocket, arduino_serial) igniter = IgniterSensor(UUID('f309669d-6bd7-4ee3-90a5-45a0e1bdd60e'), 'Igniter', rocket, arduino_serial, parachute) temperature = TemperatureSensor(UUID('ac93964a-6bb0-11ee-b962-0242ac120002'), 'Temperature', rocket, arduino_serial) - photoresistor = PhotoresistorSensor(UUID('158314cc-6d1f-11ee-b962-0242ac120002'), 'Photoresistor', rocket, arduino_serial) + photoresistor = PhotoresistorSensor(UUID('158314cc-6d1f-11ee-b962-0242ac120002'), 'Orientation', rocket, arduino_serial) + pressure = PressureSensor(UUID('eedd649e-78c7-11ee-b962-0242ac120002'), 'Pressure', rocket, arduino_serial) + altitude = AltitudeSensor(UUID('f526cb42-78c7-11ee-b962-0242ac120002'), 'Altitude', rocket, arduino_serial) + # FlightDirector(UUID('37155a2c-c51d-41b7-9dae-67d640d8c284'), 'Flight Director', rocket, arduino_serial, igniter, parachute, acc, gyro, inertialFrame) return FlightConfig(rocket, [ArduinoSerialSelectUI(arduino_serial), ArduinoSerialMonitorUI(arduino_serial)], True) From ae02652050d470b2389da1275cdc6720bb15d7b1 Mon Sep 17 00:00:00 2001 From: Andrej Volckov <125829671+AndrejVD@users.noreply.github.com> Date: Mon, 27 Nov 2023 23:08:58 +0000 Subject: [PATCH 3/5] hdlc --- .../flight_director/flight_director.py | 17 +- app/content/messages/response_message.py | 103 --------- app/content/messages/smessages.py | 34 --- .../arduino/messages/messages.py | 73 ++++++ .../arduino/parts}/igniter.py | 63 +++--- .../arduino/parts}/servo.py | 60 +++-- .../arduino/sensors/orientation_arduino.py} | 37 ++- .../sensors/pressure/altitude_arduino.py} | 22 +- .../sensors/pressure/pressure_arduino.py} | 39 +--- .../pressure/pressure_sensor_arduino.py | 67 ++++++ .../sensors/pressure/temperature_arduino.py} | 23 +- app/content/microcontroller/arduino_serial.py | 211 +++++++----------- app/flight_executer.py | 2 +- app/logic/rocket_definition.py | 4 +- app/rockets/make_spatula.py | 39 ++-- 15 files changed, 343 insertions(+), 451 deletions(-) delete mode 100644 app/content/messages/response_message.py delete mode 100644 app/content/messages/smessages.py create mode 100644 app/content/microcontroller/arduino/messages/messages.py rename app/content/{sensors/arduino => microcontroller/arduino/parts}/igniter.py (63%) rename app/content/{sensors/arduino => microcontroller/arduino/parts}/servo.py (67%) rename app/content/{sensors/arduino/pressure.py => microcontroller/arduino/sensors/orientation_arduino.py} (69%) rename app/content/{sensors/arduino/altitude.py => microcontroller/arduino/sensors/pressure/altitude_arduino.py} (66%) rename app/content/{sensors/arduino/photoresistor.py => microcontroller/arduino/sensors/pressure/pressure_arduino.py} (51%) create mode 100644 app/content/microcontroller/arduino/sensors/pressure/pressure_sensor_arduino.py rename app/content/{sensors/arduino/temperaturesensor.py => microcontroller/arduino/sensors/pressure/temperature_arduino.py} (67%) diff --git a/app/content/flight_director/flight_director.py b/app/content/flight_director/flight_director.py index b9aa62d..2309bab 100644 --- a/app/content/flight_director/flight_director.py +++ b/app/content/flight_director/flight_director.py @@ -1,26 +1,19 @@ -from asyncio import Future, Task -import asyncio from datetime import datetime, timedelta -import threading -import time -from typing import Collection, Iterable, Tuple, Type, Union, cast +from typing import Iterable, Tuple, Type, Union from uuid import UUID, uuid4 -from dataclasses import dataclass from app.content.flight_director.abort_command import AbortCommand from app.content.flight_director.arm_director_command import ArmDirectorCommand from app.content.flight_director.start_countdown_command import StartCountDownCommand from app.content.general_commands.calibrate import CalibrateZeroCommand -from app.content.general_commands.enable import DisableCommand, EnableCommand from app.content.microcontroller.arduino_serial import ArduinoSerial from app.content.motor_commands.open import IgniteCommand, OpenCommand from app.content.sensors.android_native.acceleration_pyjinius import PyjiniusAccelerationSensor from app.content.sensors.android_native.gyroscope_pyjinius import PyjiniusGyroscopeSensor from app.content.sensors.android_native.inertial_reference_frame import InertialReferenceFrame -from app.content.sensors.arduino.igniter import IgniterSensor -from app.content.sensors.arduino.servo import ServoSensor -from app.logic.commands.command import Command, Command -from app.content.general_commands.enable import DisableCommand, EnableCommand, ResetCommand +from app.content.microcontroller.arduino.parts.igniter import IgniterSensor +from app.content.microcontroller.arduino.parts.servo import ServoSensor +from app.logic.commands.command import Command from app.logic.rocket_definition import Part, Rocket @@ -139,7 +132,7 @@ def run_arm(self, c: ArmDirectorCommand): if not self.calibrated: c.state = 'failed' - c.response_message = 'Sensors not yet calibrated' + c.response_message = 'sensors not yet calibrated' return if not self.arduino.connected: diff --git a/app/content/messages/response_message.py b/app/content/messages/response_message.py deleted file mode 100644 index b379134..0000000 --- a/app/content/messages/response_message.py +++ /dev/null @@ -1,103 +0,0 @@ - -from typing import Collection, Iterable, Tuple, Type, Union, cast - - -class AReceivedMessage: - partCode : float - - def __init__(self, partCode : bytes = 0x00): - self.partCode = partCode - -class CommunicationErrors: - result : dict - - def __init__(self): - self.result = dict() - self.result[0x00] = 'Success' - - self.result[0x01] = 'Incompatible Launch Phase' - - self.result[0x10] = 'Message Layout Error! Wrong Start Byte' - self.result[0x11] = 'Message Layout Error! Wrong Connection Byte' - self.result[0x12] = 'Message Layout Error! Wrong State Byte' - self.result[0x13] = 'Message Layout Error! WrongPartByte' - self.result[0x14] = 'Message Layout Error! WrongCommandByte' - self.result[0x15] = 'Message Layout Error! WrongEndByte' - - def __getitem__(self, item : bytes) -> str: - return self.result[item] - -class ResponseM(AReceivedMessage): - - commandCode : bytes - - resultCode : bytes - - def __init__(self, message : bytearray): - super().__init__(message[3]) - self.commandCode = message[4] - self.resultCode = message[5] - - def get_result(self) -> str: - ce = CommunicationErrors() - return ce[self.resultCode] - -class SensorDataMessage(AReceivedMessage): - data : float - partCode : float - dataPart : float - - def __init__(self, message : bytearray): - super().__init__(message[3]) - - dataSize = int(message[5]) - self.data = float(message[6: dataSize + 6]) - self.dataPart = message[4] - - def get_data(self): - return self.data - - def get_data_part(self): - return self.dataPart - -class ArduinoStateMessage(AReceivedMessage): - - def __init__(self, message : bytearray): - super().__init__() - - - - -class ResponseMessage: - message : bytearray - - - def __init__(self): - self.message = bytearray([]) - - def parse(self, received_msg : bytearray) -> Union[ResponseM, SensorDataMessage, ArduinoStateMessage, None]: - msg = None - for i in received_msg: - if len(self.message) and self.message[-1] != 0x7E and i == 0x7E: - self.message.append(i) - msg = self.checkCommandType() - self.message = bytearray([]) - else: - self.message.append(i) - - return msg - - - def checkCommandType(self) -> Union[ResponseM, SensorDataMessage, ArduinoStateMessage, None]: - if(len(self.message)): - print(self.message) - if self.message[2] == 0x00: - return ResponseM(self.message) - - elif self.message[2] == 0x01: - return ArduinoStateMessage(self.message) - - elif self.message[2] == 0x02: - return SensorDataMessage(self.message) - - return None diff --git a/app/content/messages/smessages.py b/app/content/messages/smessages.py deleted file mode 100644 index 9840901..0000000 --- a/app/content/messages/smessages.py +++ /dev/null @@ -1,34 +0,0 @@ -class Message: - partCode : bytes - commandCode : bytes - - def __init__(self, partCode: chr = 0x00, commandCode: chr = 0x00): - self.partCode = partCode - self.commandCode = commandCode - - def getMessage(self): - messageStart = messageEnd = 0x7E - direction = messageType = 0x00 - - message = [messageStart, direction, messageType, self.partCode, self.commandCode, messageEnd] - return bytearray(message) - - -class AMessageList: - partCode : chr - messageDict : dict() - - def __init__(self, partCode: chr = 0x00): - self.partCode = partCode - self.messageDict = dict() - - def addCommandMessage(self, commandName: str, commandCode: chr): - self.messageDict[commandName] = Message(self.partCode, commandCode) - - def sendCommand(self, messageNmae: str) -> bytearray : - return self.messageDict[messageNmae].getMessage() - - def __getitem__(self, key : str)-> bytearray: - return self.messageDict[key].getMessage() - - diff --git a/app/content/microcontroller/arduino/messages/messages.py b/app/content/microcontroller/arduino/messages/messages.py new file mode 100644 index 0000000..16192b5 --- /dev/null +++ b/app/content/microcontroller/arduino/messages/messages.py @@ -0,0 +1,73 @@ +from typing import Collection, Iterable, Tuple, Type, Union, cast + +class ResponseMessage: + arr: bytearray() + + def __init__(self, arr): + self.arr = arr + + def getPart(self) -> chr: + mask = 63; + + return self.arr[0] & mask; + + def getCommand(self) -> chr: + mask = 15; + + return (self.arr[2] >> 4) & mask + + def getIndex(self) -> chr: + return self.arr[1] + + def getResponseRequestByte(self) -> chr: + mask = 1 + return self.arr[0] >> 6 & mask + + def getResult(self) -> chr: + mask = 15; + + return self.arr[2] & mask; + + +class SensorData: + arr: bytearray() + + def __init__(self, arr): + self.arr = arr + + def getPart(self) -> chr: + mask = 127; + + return self.arr[0] & mask; + + def getType(self) -> chr: + mask = 15; + + return (self.arr[1] >> 4) & mask; + + def getPayloadLength(self) -> int: + mask = 15; + + return self.arr[1] & mask; + + def getData(self) -> list[int]: + arr = [] + for i in range(self.getPayloadLength()): + arr.append(int.from_bytes(self.arr[2 + i * 2: 4 + i * 2], 'little')) + + return arr + + +messageIndex = 1 +def sendCommand(partID : chr, commandID : chr, pl : chr = 0): + global messageIndex + arr = bytearray(0 for x in range(3)) + + arr[0] |= 1 << 7 + arr[0] |= partID + arr[1] |= messageIndex + arr[2] |= commandID << 4 + arr[2] |= pl + + messageIndex += 1 + return arr, messageIndex - 1 diff --git a/app/content/sensors/arduino/igniter.py b/app/content/microcontroller/arduino/parts/igniter.py similarity index 63% rename from app/content/sensors/arduino/igniter.py rename to app/content/microcontroller/arduino/parts/igniter.py index 4d280f0..7cc9286 100644 --- a/app/content/sensors/arduino/igniter.py +++ b/app/content/microcontroller/arduino/parts/igniter.py @@ -1,28 +1,21 @@ -from asyncio import Future, Task +from asyncio import Future from datetime import timedelta -from typing import Collection, Iterable, Tuple, Type, Union, cast +from typing import Iterable, Tuple, Type, Union from uuid import UUID -from dataclasses import dataclass -from app.content.general_commands.enable import DisableCommand, EnableCommand -from app.content.motor_commands.open import OpenCommand, CloseCommand, IgniteCommand -from app.content.sensors.arduino.servo import ServoSensor -from app.logic.commands.command import Command, Command -from app.content.general_commands.enable import DisableCommand, EnableCommand, ResetCommand +from app.content.motor_commands.open import IgniteCommand +from app.content.microcontroller.arduino.parts.servo import ServoSensor +from app.logic.commands.command import Command from app.content.microcontroller.arduino_serial import ArduinoSerial from app.logic.rocket_definition import Part, Rocket -from kivy.utils import platform - -from app.content.messages.smessages import AMessageList - class IgniterSensor(Part): type = 'Igniter' enabled: bool = True - min_update_period = timedelta(milliseconds=20) + min_update_period = timedelta(milliseconds=100) min_measurement_period = timedelta(milliseconds=1000) @@ -40,8 +33,11 @@ class IgniterSensor(Part): state: str - messageList : AMessageList + commandList : dict() + + commandProccessingDict : dict() + partID : chr def __init__(self, _id: UUID, name: str, parent: Union[Part, Rocket, None], arduino_parent: Union[ArduinoSerial, None], parachute: ServoSensor, start_enabled=True): self.arduino = arduino_parent @@ -50,11 +46,25 @@ def __init__(self, _id: UUID, name: str, parent: Union[Part, Rocket, None], ardu self.parachute = parachute super().__init__(_id, name, parent, list()) # type: ignore - self.messageList = AMessageList(0x02) - self.messageList.addCommandMessage('Ignite', 0x00) + self.partID = 2 + self.commandList = { 'Ignite' : 0 } + + self.commandProccessingDict = dict() + self.arduino.addCallback(self.partID, self.proccessCommand) + + def proccessCommand(self, index : int, result : int): + print(index, result) + command = self.commandProccessingDict[index] + if result == 0: + command.state = 'success' + command.response_message = 'Ignited' - self.messageList2 = AMessageList(0x01) - self.messageList2.addCommandMessage('Open', 0x01) + self.arduino.launchPhase = 'LiftOff' + else: + command.state = 'failed' + command.response_message = self.arduino.errorMessageDict[result] + + self.commandProccessingDict.pop(index) def get_accepted_commands(self) -> list[Type[Command]]: return [IgniteCommand] @@ -70,25 +80,20 @@ def update(self, commands: Iterable[Command], now, iteration): if c.state == 'received': self.last_command = c - self.last_ignite_future = self.arduino.send_message(self.messageList['Ignite']) + self.last_ignite_future = self.arduino.send_message(self.partID, self.commandList['Ignite']) self.last_ignited = now self.parachute_triggered = False + + self.commandProccessingDict[self.last_ignite_future.result()] = c c.state = 'processing' + if c.state == 'processing' and self.last_command != c: c.state = 'failed' c.response_message = 'Another ignite command was send, this command will no longer be processed' continue - if c.state == 'processing' and self.last_ignite_future is not None and self.last_ignite_future.done(): - exception = self.last_ignite_future.exception() - if exception is not None: - c.state = 'failed' - c.response_message = exception.args[0] - continue - c.state = 'success' - c.response_message = 'Igniter triggered' - self.arduino.launchPhase = 'Liftoff' + else: c.state = 'failed' # Part cannot handle this command @@ -97,7 +102,7 @@ def update(self, commands: Iterable[Command], now, iteration): if self.arduino is not None and self.last_ignited is not None and not self.parachute_triggered and (now - self.last_ignited) >= self.deploy_parachute_delay: self.parachute_triggered = True - self.arduino.send_message(self.messageList['Open']) + self.arduino.send_message(self.parachute.partID, self.parachute.commandList['Open']) # def add_command_to_queue(command_code: int, payload): diff --git a/app/content/sensors/arduino/servo.py b/app/content/microcontroller/arduino/parts/servo.py similarity index 67% rename from app/content/sensors/arduino/servo.py rename to app/content/microcontroller/arduino/parts/servo.py index 938e5e0..9b5a6ca 100644 --- a/app/content/sensors/arduino/servo.py +++ b/app/content/microcontroller/arduino/parts/servo.py @@ -5,22 +5,18 @@ from dataclasses import dataclass from app.content.general_commands.enable import DisableCommand, EnableCommand -from app.content.motor_commands.open import OpenCommand, CloseCommand, IgniteCommand -from app.logic.commands.command import Command, Command -from app.content.general_commands.enable import DisableCommand, EnableCommand, ResetCommand +from app.content.motor_commands.open import OpenCommand, CloseCommand +from app.logic.commands.command import Command from app.content.microcontroller.arduino_serial import ArduinoSerial from app.logic.rocket_definition import Part, Rocket -from kivy.utils import platform - -from app.content.messages.smessages import AMessageList class ServoSensor(Part): type = 'Servo' enabled: bool = True - min_update_period = timedelta(milliseconds=20) + min_update_period = timedelta(milliseconds=200) min_measurement_period = timedelta(milliseconds=1000) @@ -32,7 +28,11 @@ class ServoSensor(Part): last_command: Union[None, Command] = None - messageList : AMessageList + commandList : dict() + + commandProccessingDict : dict() + + partID : chr def __init__(self, _id: UUID, name: str, parent: Union[Part, Rocket, None], arduino_parent: Union[ArduinoSerial, None],start_enabled=True): self.arduino = arduino_parent @@ -40,13 +40,26 @@ def __init__(self, _id: UUID, name: str, parent: Union[Part, Rocket, None], ardu self.state = 'close' super().__init__(_id, name, parent, list()) # type: ignore - self.messageList = AMessageList(0x01) - self.messageList.addCommandMessage('Close', 0x00) - self.messageList.addCommandMessage('Open', 0x01) + self.partID = 1 + self.commandList = { 'Close' : 0, 'Open' : 1 } + self.commandProccessingDict = dict() + self.arduino.addCallback(self.partID, self.proccessCommand) + + def proccessCommand(self, index : int, result : int): + print(index, result) + command = self.commandProccessingDict[index] + if result == 0: + command.state = 'success' + command.response_message = 'Servo activated' + else: + command.state = 'failed' + command.response_message = self.arduino.errorMessageDict[result] + + self.commandProccessingDict.pop(index) def get_accepted_commands(self) -> list[Type[Command]]: - return [OpenCommand, CloseCommand] + return [DisableCommand, EnableCommand, OpenCommand, CloseCommand] def update(self, commands: Iterable[Command], now, iteration): @@ -62,36 +75,35 @@ def update(self, commands: Iterable[Command], now, iteration): c.response_message = 'Another ignite command was send, this command will no longer be processed' continue - if c.state == 'processing' and self.last_ignite_future is not None and self.last_ignite_future.done(): - exception = self.last_ignite_future.exception() - if exception is not None: - c.state = 'failed' - c.response_message = exception.args[0] - continue - if self.last_ignite_future.result() == "Success": - c.state = 'success' - c.response_message = 'Servo activated' + if c.state == 'processing': + print("jas") + continue + if isinstance(c, CloseCommand): if c.state == 'received': self.last_command = c - self.last_ignite_future = self.arduino.send_message(self.messageList["Close"]) + self.last_ignite_future = self.arduino.send_message(self.partID, self.commandList["Close"]) + + self.commandProccessingDict[self.last_ignite_future.result()] = c c.state = 'processing' elif isinstance(c, OpenCommand): if c.state == 'received': self.last_command = c - self.last_ignite_future = self.arduino.send_message(self.messageList["Open"]) + self.last_ignite_future = self.arduino.send_message(self.partID, self.commandList["Open"]) + + self.commandProccessingDict[self.last_ignite_future.result()] = c c.state = 'processing' + else: c.state = 'failed' # Part cannot handle this command continue - # def add_command_to_queue(command_code: int, payload): def get_measurement_shape(self) -> Iterable[Tuple[str, Type]]: return [ diff --git a/app/content/sensors/arduino/pressure.py b/app/content/microcontroller/arduino/sensors/orientation_arduino.py similarity index 69% rename from app/content/sensors/arduino/pressure.py rename to app/content/microcontroller/arduino/sensors/orientation_arduino.py index eca5ff5..c50deda 100644 --- a/app/content/sensors/arduino/pressure.py +++ b/app/content/microcontroller/arduino/sensors/orientation_arduino.py @@ -5,18 +5,12 @@ from dataclasses import dataclass from app.content.general_commands.enable import DisableCommand, EnableCommand -from app.content.motor_commands.open import OpenCommand, CloseCommand, IgniteCommand -from app.logic.commands.command import Command, Command -from app.content.general_commands.enable import DisableCommand, EnableCommand, ResetCommand +from app.logic.commands.command import Command from app.content.microcontroller.arduino_serial import ArduinoSerial from app.logic.rocket_definition import Part, Rocket -from kivy.utils import platform - -from app.content.messages.smessages import AMessageList - -class PressureSensor(Part): - type = 'Pressure' +class OrientationSensor(Part): + type = 'Orientation' enabled: bool = True @@ -26,12 +20,9 @@ class PressureSensor(Part): arduino: Union[ArduinoSerial, None] - - last_ignite_future: Union[Future, None] = None - - last_command: Union[None, Command] = None - - pressure : float + OrientationX : float + OrientationY : float + OrientationZ : float def __init__(self, _id: UUID, name: str, parent: Union[Part, Rocket, None], arduino_parent: Union[ArduinoSerial, None],start_enabled=True): self.arduino = arduino_parent @@ -39,8 +30,14 @@ def __init__(self, _id: UUID, name: str, parent: Union[Part, Rocket, None], ardu super().__init__(_id, name, parent, list()) # type: ignore - self.pressure = 0.0 + partID = 0x54 + self.OrientationX = self.OrientationY = self.OrientationZ = 0.0 + self.arduino.addCallback(partID, self.set_measurements) + def set_measurements(self, dataList : list[int]): + self.OrientationX = dataList[0] + self.OrientationY = dataList[1] + self.OrientationZ = dataList[2] def get_accepted_commands(self) -> list[Type[Command]]: return [EnableCommand, DisableCommand] @@ -57,13 +54,13 @@ def update(self, commands: Iterable[Command], now, iteration): self.enabled = False c.state = "success" - self.pressure = self.arduino.pressure - def get_measurement_shape(self) -> Iterable[Tuple[str, Type]]: return [ - ('pressure', float), + ('X', float), + ('Y', float), + ('Z', float), ] def collect_measurements(self, now, iteration) -> Iterable[Iterable[float]]: - return [[self.pressure]] + return [[self.OrientationX, self.OrientationY, self.OrientationZ]] diff --git a/app/content/sensors/arduino/altitude.py b/app/content/microcontroller/arduino/sensors/pressure/altitude_arduino.py similarity index 66% rename from app/content/sensors/arduino/altitude.py rename to app/content/microcontroller/arduino/sensors/pressure/altitude_arduino.py index 5c30bfb..895abac 100644 --- a/app/content/sensors/arduino/altitude.py +++ b/app/content/microcontroller/arduino/sensors/pressure/altitude_arduino.py @@ -5,15 +5,9 @@ from dataclasses import dataclass from app.content.general_commands.enable import DisableCommand, EnableCommand -from app.content.motor_commands.open import OpenCommand, CloseCommand, IgniteCommand -from app.logic.commands.command import Command, Command -from app.content.general_commands.enable import DisableCommand, EnableCommand, ResetCommand -from app.content.microcontroller.arduino_serial import ArduinoSerial +from app.logic.commands.command import Command from app.logic.rocket_definition import Part, Rocket -from kivy.utils import platform - -from app.content.messages.smessages import AMessageList class AltitudeSensor(Part): type = 'Altitude' @@ -24,30 +18,19 @@ class AltitudeSensor(Part): min_measurement_period = timedelta(milliseconds=1000) - arduino: Union[ArduinoSerial, None] - - - last_ignite_future: Union[Future, None] = None - - last_command: Union[None, Command] = None - altitude : float - def __init__(self, _id: UUID, name: str, parent: Union[Part, Rocket, None], arduino_parent: Union[ArduinoSerial, None],start_enabled=True): - self.arduino = arduino_parent + def __init__(self, _id: UUID, name: str, parent: Union[Part, Rocket, None],start_enabled=True): self.enabled = start_enabled super().__init__(_id, name, parent, list()) # type: ignore self.altitude = 0.0 - - def get_accepted_commands(self) -> list[Type[Command]]: return [EnableCommand, DisableCommand] def update(self, commands: Iterable[Command], now, iteration): - for c in commands: if isinstance(c, EnableCommand): @@ -58,7 +41,6 @@ def update(self, commands: Iterable[Command], now, iteration): self.enabled = False c.state = "success" - self.altitude = self.arduino.altitude def get_measurement_shape(self) -> Iterable[Tuple[str, Type]]: return [ diff --git a/app/content/sensors/arduino/photoresistor.py b/app/content/microcontroller/arduino/sensors/pressure/pressure_arduino.py similarity index 51% rename from app/content/sensors/arduino/photoresistor.py rename to app/content/microcontroller/arduino/sensors/pressure/pressure_arduino.py index e0c0293..67bea64 100644 --- a/app/content/sensors/arduino/photoresistor.py +++ b/app/content/microcontroller/arduino/sensors/pressure/pressure_arduino.py @@ -5,18 +5,12 @@ from dataclasses import dataclass from app.content.general_commands.enable import DisableCommand, EnableCommand -from app.content.motor_commands.open import OpenCommand, CloseCommand, IgniteCommand -from app.logic.commands.command import Command, Command -from app.content.general_commands.enable import DisableCommand, EnableCommand, ResetCommand -from app.content.microcontroller.arduino_serial import ArduinoSerial +from app.logic.commands.command import Command from app.logic.rocket_definition import Part, Rocket -from kivy.utils import platform -from app.content.messages.smessages import AMessageList - -class PhotoresistorSensor(Part): - type = 'Photoresistor' +class PressureSensor(Part): + type = 'Pressure' enabled: bool = True @@ -24,24 +18,14 @@ class PhotoresistorSensor(Part): min_measurement_period = timedelta(milliseconds=1000) - arduino: Union[ArduinoSerial, None] - - last_ignite_future: Union[Future, None] = None - - last_command: Union[None, Command] = None - + pressure : float - xOrientation : float - yOrientation : float - zOrientation : float - - def __init__(self, _id: UUID, name: str, parent: Union[Part, Rocket, None], arduino_parent: Union[ArduinoSerial, None],start_enabled=True): - self.arduino = arduino_parent + def __init__(self, _id: UUID, name: str, parent: Union[Part, Rocket, None], start_enabled=True): self.enabled = start_enabled super().__init__(_id, name, parent, list()) # type: ignore - self.xOrientation = self.yOrientation = self.zOrientation = 0.0 + self.pressure = 0.0 def get_accepted_commands(self) -> list[Type[Command]]: @@ -60,18 +44,11 @@ def update(self, commands: Iterable[Command], now, iteration): c.state = "success" - self.xOrientation = self.arduino.xOrientation - self.yOrientation = self.arduino.yOrientation - self.zOrientation = self.arduino.zOrientation - - def get_measurement_shape(self) -> Iterable[Tuple[str, Type]]: return [ - ('X', float), - ('Y', float), - ('Z', float), + ('pressure', float), ] def collect_measurements(self, now, iteration) -> Iterable[Iterable[float]]: - return [[self.xOrientation, self.yOrientation, self.zOrientation]] + return [[self.pressure]] diff --git a/app/content/microcontroller/arduino/sensors/pressure/pressure_sensor_arduino.py b/app/content/microcontroller/arduino/sensors/pressure/pressure_sensor_arduino.py new file mode 100644 index 0000000..cc24df4 --- /dev/null +++ b/app/content/microcontroller/arduino/sensors/pressure/pressure_sensor_arduino.py @@ -0,0 +1,67 @@ +from datetime import timedelta +from typing import Iterable, Tuple, Type, Union +from uuid import UUID + +from app.content.general_commands.enable import DisableCommand, EnableCommand +from app.logic.commands.command import Command +from app.content.microcontroller.arduino_serial import ArduinoSerial +from app.content.microcontroller.arduino.sensors.pressure.temperature_arduino import TemperatureSensor +from app.content.microcontroller.arduino.sensors.pressure.pressure_arduino import PressureSensor +from app.content.microcontroller.arduino.sensors.pressure.altitude_arduino import AltitudeSensor +from app.logic.rocket_definition import Part, Rocket + +class PressureArduinoSensor(Part): + type = 'PressureArduinoSensor' + + enabled: bool = True + + min_update_period = timedelta(milliseconds=20) + + min_measurement_period = timedelta(milliseconds=1000) + + sensorsList : list() + + def __init__(self, _id: UUID, name: str, parent: Union[Part, Rocket, None], arduino : ArduinoSerial, + temperatureSensor : TemperatureSensor, pressureSensor : PressureSensor, + altitudeSensor : AltitudeSensor, start_enabled=True): + + self.enabled = start_enabled + + super().__init__(_id, name, parent, list()) # type: ignore + + partID = 0x53 + + self.sensorsList = [] + self.sensorsList.append(temperatureSensor) + self.sensorsList.append(pressureSensor) + self.sensorsList.append(altitudeSensor) + + arduino.addCallback(partID, self.set_measurements) + + + def set_measurements(self, dataList : list[int]): + self.sensorsList[0].temperature = dataList[0] + self.sensorsList[1].pressure = dataList[1] + self.sensorsList[2].altitude = dataList[2] + + def get_accepted_commands(self) -> list[Type[Command]]: + return [EnableCommand, DisableCommand] + + def update(self, commands: Iterable[Command], now, iteration): + for c in commands: + + if isinstance(c, EnableCommand): + self.enabled = True + c.state = "success" + + elif isinstance(c, DisableCommand): + self.enabled = False + c.state = "success" + + + def get_measurement_shape(self) -> Iterable[Tuple[str, Type]]: + return [] + + def collect_measurements(self, now, iteration) -> Iterable[Iterable[float]]: + return [[]] + diff --git a/app/content/sensors/arduino/temperaturesensor.py b/app/content/microcontroller/arduino/sensors/pressure/temperature_arduino.py similarity index 67% rename from app/content/sensors/arduino/temperaturesensor.py rename to app/content/microcontroller/arduino/sensors/pressure/temperature_arduino.py index 9da3903..11bbb90 100644 --- a/app/content/sensors/arduino/temperaturesensor.py +++ b/app/content/microcontroller/arduino/sensors/pressure/temperature_arduino.py @@ -5,15 +5,9 @@ from dataclasses import dataclass from app.content.general_commands.enable import DisableCommand, EnableCommand -from app.content.motor_commands.open import OpenCommand, CloseCommand, IgniteCommand -from app.logic.commands.command import Command, Command -from app.content.general_commands.enable import DisableCommand, EnableCommand, ResetCommand -from app.content.microcontroller.arduino_serial import ArduinoSerial +from app.logic.commands.command import Command from app.logic.rocket_definition import Part, Rocket -from kivy.utils import platform - -from app.content.messages.smessages import AMessageList class TemperatureSensor(Part): type = 'Temperature' @@ -24,27 +18,15 @@ class TemperatureSensor(Part): min_measurement_period = timedelta(milliseconds=1000) - arduino: Union[ArduinoSerial, None] - - - last_ignite_future: Union[Future, None] = None - - last_command: Union[None, Command] = None - temperature : float - def __init__(self, _id: UUID, name: str, parent: Union[Part, Rocket, None], arduino_parent: Union[ArduinoSerial, None],start_enabled=True): - self.arduino = arduino_parent + def __init__(self, _id: UUID, name: str, parent: Union[Part, Rocket, None],start_enabled=True): self.enabled = start_enabled super().__init__(_id, name, parent, list()) # type: ignore self.temperature = 0.0 - - - - def get_accepted_commands(self) -> list[Type[Command]]: return [EnableCommand, DisableCommand] @@ -60,7 +42,6 @@ def update(self, commands: Iterable[Command], now, iteration): self.enabled = False c.state = "success" - self.temperature = self.arduino.temperature def get_measurement_shape(self) -> Iterable[Tuple[str, Type]]: return [ diff --git a/app/content/microcontroller/arduino_serial.py b/app/content/microcontroller/arduino_serial.py index 6bf5cde..715acea 100644 --- a/app/content/microcontroller/arduino_serial.py +++ b/app/content/microcontroller/arduino_serial.py @@ -2,15 +2,13 @@ import asyncio from datetime import timedelta import threading -from typing import Collection, Iterable, Tuple, Type, Union, cast +from typing import Collection, Iterable, Tuple, Type, Union from uuid import UUID from dataclasses import dataclass -from app.content.general_commands.enable import DisableCommand, EnableCommand -from app.content.motor_commands.open import OpenCommand, CloseCommand, IgniteCommand -from app.logic.commands.command import Command, Command +from app.logic.commands.command import Command from app.content.general_commands.enable import DisableCommand, EnableCommand, ResetCommand -from app.content.motor_commands.open import SetIgnitionPhaseCommand, SetLiftoffPhaseCommand, SetRecoveryPhaseCommand +from app.content.motor_commands.open import SetIgnitionPhaseCommand from app.content.motor_commands.open import SetPreparationPhaseCommand from app.logic.rocket_definition import Part, Rocket @@ -18,11 +16,9 @@ from kivy.utils import platform import tinyproto -from app.content.messages.response_message import ResponseM, ArduinoStateMessage, SensorDataMessage, ResponseMessage +from app.content.microcontroller.arduino.messages.messages import SensorData, ResponseMessage, sendCommand -from app.content.messages.smessages import AMessageList - if platform == 'android': from usb4a import usb from usbserial4a import serial4a @@ -45,8 +41,6 @@ def to_bytes(self): return [*self.index.to_bytes(), *self.command.to_bytes(), *self.payload_size.to_bytes(), *self.payload] - - class ArduinoSerial(Part): type = 'Microcontroller.Arduino.Serial' @@ -80,8 +74,6 @@ class ArduinoSerial(Part): hdlc: Union[tinyproto.Hdlc, None] - current_message = bytearray([]) - expected_next_response_part: Union[int, None] = None expected_next_response_command: Union[int, None] = None @@ -90,31 +82,19 @@ class ArduinoSerial(Part): logs = [] - part_activated: Union[bytes, None] - part_state: Union[str, None] - - commands_list = [] - - messageList : AMessageList - - temperature : float - - pressure : float + launchPhase : str - altitude : float + sensorsCallBack : dict() - xOrientation : float + partID : chr - yOrientation : float + commandProccessingDict : dict() - zOrientation : float + commandList : dict() - launchPhase : str + errorMessageDict : dict() def __init__(self, _id: UUID, name: str, parent: Union[Part, Rocket, None], start_enabled = True): - self.part_state = None - self.part_state = None - self.enabled = start_enabled super().__init__(_id, name, parent, list()) # type: ignore @@ -122,19 +102,45 @@ def __init__(self, _id: UUID, name: str, parent: Union[Part, Rocket, None], star self.hdlc = None - self.messageList = AMessageList(0x00) - self.messageList.addCommandMessage('Reset', 0x00) - self.messageList.addCommandMessage('Preparation', 0x01) - self.messageList.addCommandMessage('Ignition', 0x02) - self.messageList.addCommandMessage('Liftoff', 0x03) - self.messageList.addCommandMessage('Recovery', 0x04) + self.launchPhase = 'Preparation' + self.sensorsCallBack = dict() - self.temperature = self.pressure = self.altitude = 0.0 - self.xOrientation = self.yOrientation = self.zOrientation = 0.0 + self.partID = 0 + self.commandList = { 'Reset' : 0, 'Preparation' : 1, 'Ignition' : 2, 'LiftOff' : 3 } + + self.commandProccessingDict = dict() + self.addCallback(self.partID, self.proccessCommand) + + self.errorMessageDict = dict() + self.errorMessageDict[0] = "Success" + self.errorMessageDict[1] = "Failed : Incompatible Launch Phase" + self.errorMessageDict[2] = "Failed : Incorrect Part Byte" + self.errorMessageDict[3] = "Failed : Incorrect Command Byte" + self.errorMessageDict[4] = "Failed" - self.launchPhase = 'Preparation' + def proccessCommand(self, index: int, result: int): + print(index, result) + command = self.commandProccessingDict[index] + if result == 0: + command.state = 'success' + command.response_message = 'Command activated' + + if isinstance(command, SetIgnitionPhaseCommand): + self.launchPhase = 'Ignition' + + elif isinstance(command, SetPreparationPhaseCommand): + self.launchPhase = 'Preparation' + else: + command.state = 'failed' + command.response_message = self.arduino.errorMessageDict[result] + + self.commandProccessingDict.pop(index) + + def addCallback(self, key : chr, fun): + self.sensorsCallBack[key] = fun + def try_get_device_list(self): if platform == 'android': usb_device_list = usb.get_usb_device_list() @@ -145,7 +151,6 @@ def try_get_device_list(self): usb_device_list = list_ports.comports() self.device_name_list = [port.device for port in usb_device_list] - def try_connect_device_in_background(self, device_name: str): # Only one connect at a time @@ -173,8 +178,6 @@ def try_connect_last_device_background(self): self.try_connect_device_in_background(self.last_selected_device) - - async def try_connect_device(self, device_name: str) -> str: # Hack to have this running the background @@ -228,46 +231,36 @@ def read_msg_thread(self): self.hdlc = hdlc - def on_read(b: bytes): - - - if len(b) < 3: - print('skip') - return - - payload_size = int(b[2]) - - payload = b[3:-1] if payload_size > 0 else bytes() - - package = RssPacket(int(b[0]), int(b[1]), payload_size, payload) - - #print(package) - - self.last_message = package - - - message = ResponseMessage() + def on_read(a): + print(a) + + if a[0] >> 7 | 0 == 1: + response = ResponseMessage(a) + if response.getResponseRequestByte() == 1: + try: + self.sensorsCallBack[response.getPart()](response.getIndex(), response.getResult()) + except Exception as ex: + pass + else: + message, _ = sendCommand(self.partID, self.commandList[self.launchPhase]) + self.send_message_hdlc(message) + else: + sensorData = SensorData(a) + self.sensorsCallBack[sensorData.getPart()](sensorData.getData()) - hdlc.on_read = on_read hdlc.crc = 8 + hdlc.on_read = on_read hdlc.begin() - - try: while True: - #print(self.logs) with self.port_thread_lock: if not self.serial_port.is_open: break - received_msg = self.serial_port.read( - self.serial_port.in_waiting - ) - if received_msg: - response = message.parse(received_msg) - if response: - self.action(response) + received_msg = self.serial_port.read(1) + if received_msg: + hdlc.rx(received_msg) except Exception as ex: self.connected = False @@ -279,49 +272,8 @@ def on_read(b: bytes): raise ex - def action(self, response): - - if isinstance(response, ResponseM): - self.part_state = 'success' - - if self.response_future is None or self.response_future.done(): - return - - result = response.get_result() - if result == 'Success': - self.response_future.set_result(result) - else: - self.response_future.set_exception(Exception(result)) - - elif isinstance(response, SensorDataMessage): - dataPart = response.get_data_part() - if response.partCode == 0x53: - if dataPart == 0x01: - self.temperature = response.get_data() - - elif dataPart == 0x02: - self.pressure = response.get_data() - - elif dataPart == 0x03: - self.altitude = response.get_data() - - - elif response.partCode == 0x54: - if dataPart == 0x01: - self.xOrientation = response.get_data() - - elif dataPart == 0x02: - self.yOrientation = response.get_data() - - elif dataPart == 0x03: - self.zOrientation = response.get_data() - - elif isinstance(response, ArduinoStateMessage): - self.send_message_hdlc(self.messageList[self.launchPhase]) - - def get_accepted_commands(self) -> list[Type[Command]]: - return [EnableCommand, DisableCommand, ResetCommand, SetPreparationPhaseCommand, SetIgnitionPhaseCommand, SetLiftoffPhaseCommand, SetRecoveryPhaseCommand] + return [EnableCommand, DisableCommand, ResetCommand, SetPreparationPhaseCommand, SetIgnitionPhaseCommand] def send_message_hdlc(self, message: bytearray): if self.serial_port is None or self.hdlc is None: @@ -330,7 +282,7 @@ def send_message_hdlc(self, message: bytearray): self.hdlc.put(message) self.serial_port.write(self.hdlc.tx()) - def send_message(self, message : bytearray): + def send_message(self, partID : chr, commandID : chr): '''Sends the given message to the arduino and returns a future that will be completed if the command got processed. If the command did not get processed or the connection dies the future will throw''' @@ -341,6 +293,9 @@ def send_message(self, message : bytearray): future = asyncio.Future() self.response_future = future + message, index = sendCommand(partID, commandID) + future.set_result(index) + try: self.send_message_hdlc(message) except Exception as e: @@ -358,22 +313,6 @@ def update(self, commands: Iterable[Command], now, iteration): c.response_message = 'Another ignite command was send, this command will no longer be processed' continue - if c.state == 'processing' and self.last_ignite_future is not None and self.last_ignite_future.done(): - exception = self.last_ignite_future.exception() - if exception is not None: - c.state = 'failed' - c.response_message = exception.args[0] - continue - - if self.last_ignite_future.result() == "Success": - c.state = 'success' - c.response_message = 'success' - - if isinstance(c, SetIgnitionPhaseCommand): - self.launchPhase = 'Ignition' - elif isinstance(c, SetPreparationPhaseCommand): - self.launchPhase = 'Preparation' - if isinstance(c, EnableCommand): self.enabled = True @@ -383,20 +322,24 @@ def update(self, commands: Iterable[Command], now, iteration): c.state = "success" elif isinstance(c, ResetCommand): - self.send_message(self.messageList['Reset']) + self.send_message(self.partID, self.commandList['Reset']) c.state = "success" elif isinstance(c, SetPreparationPhaseCommand): if c.state == 'received': self.last_command = c - self.last_ignite_future = self.send_message(self.messageList['Preparation']) + self.last_ignite_future = self.send_message(self.partID, self.commandList['Preparation']) + + self.commandProccessingDict[self.last_ignite_future.result()] = c c.state = 'processing' elif isinstance(c, SetIgnitionPhaseCommand): if c.state == 'received': self.last_command = c - self.last_ignite_future = self.send_message(self.messageList['Ignition']) + self.last_ignite_future = self.send_message(self.partID, self.commandList['Ignition']) + + self.commandProccessingDict[self.last_ignite_future.result()] = c c.state = 'processing' else: diff --git a/app/flight_executer.py b/app/flight_executer.py index 7da2788..3750ddc 100755 --- a/app/flight_executer.py +++ b/app/flight_executer.py @@ -74,7 +74,7 @@ def draw_overview(self): self.clear_widgets() - self.add_widget(Label(text='Parts', size_hint=(1, 0.3))) + self.add_widget(Label(text='parts', size_hint=(1, 0.3))) for ui in self.part_uis: diff --git a/app/logic/rocket_definition.py b/app/logic/rocket_definition.py index 648a656..f7dc965 100755 --- a/app/logic/rocket_definition.py +++ b/app/logic/rocket_definition.py @@ -11,7 +11,7 @@ #Maybe # PART_CATEGORY_SENSOR = 'SENSOR' -# ''' Meant for any input data, i.e. sensors. Sensors will always be called first in the update order''' +# ''' Meant for any input data, i.e. sensors. sensors will always be called first in the update order''' # PART_CATEGORY_DIRECTOR = 'DIRECTOR' @@ -79,7 +79,7 @@ class Part(ABC): def __init__(self, _id: UUID, name: str, parent: Union[Self, Rocket, None], dependencies: Iterable[Self]): ''' - :param dependencies: Parts that will be updated before this part + :param dependencies: parts that will be updated before this part ''' self._id = _id self.name = name diff --git a/app/rockets/make_spatula.py b/app/rockets/make_spatula.py index 8471c43..8782300 100755 --- a/app/rockets/make_spatula.py +++ b/app/rockets/make_spatula.py @@ -1,35 +1,27 @@ # from app.content.measurement_sinks.api_measurement_sink_ui import ApiMeasurementSinkUI -from app.content.flight_director.flight_director import FlightDirector from app.content.measurement_sinks.api_measurement_sink import ApiMeasurementSink from app.content.microcontroller.arduino_serial import ArduinoSerial from app.content.microcontroller.arduino_serial_monitor_ui import ArduinoSerialMonitorUI from app.content.microcontroller.arduino_serial_select_ui import ArduinoSerialSelectUI from app.content.sensors.android_native.gyroscope_pyjinius import PyjiniusGyroscopeSensor from app.content.sensors.android_native.inertial_reference_frame import InertialReferenceFrame -from app.content.sensors.plyer.acceleration_plyer import PlyerAccelerationSensor from app.content.sensors.android_native.acceleration_pyjinius import PyjiniusAccelerationSensor from app.content.sensors.plyer.framerate import FramerateSensor from app.content.sensors.plyer.gps_plyer import PlyerGPSSensor -from app.content.sensors.plyer.temperature_plyer import PlyerTemperatureSensor -from app.content.sensors.plyer.barometer_plyer import PlyerBarometerSensor -from app.content.sensors.plyer.gyroscope_plyer import PlyerGyroscopeSensor -from app.content.sensors.plyer.light_plyer import PlyerLightSensor -from app.content.sensors.plyer.gravity_plyer import PlyerGravitySensor from app.content.sensors.plyer.battery_plyer import PlyerBatterySensor -from app.content.sensors.arduino.servo import ServoSensor -from app.content.sensors.arduino.igniter import IgniterSensor -from app.content.sensors.arduino.temperaturesensor import TemperatureSensor -from app.content.sensors.arduino.pressure import PressureSensor -from app.content.sensors.arduino.altitude import AltitudeSensor +from app.content.microcontroller.arduino.parts.servo import ServoSensor +from app.content.microcontroller.arduino.parts.igniter import IgniterSensor -from app.content.sensors.arduino.photoresistor import PhotoresistorSensor +from app.content.microcontroller.arduino.sensors.pressure.temperature_arduino import TemperatureSensor +from app.content.microcontroller.arduino.sensors.pressure.pressure_arduino import PressureSensor +from app.content.microcontroller.arduino.sensors.pressure.altitude_arduino import AltitudeSensor +from app.content.microcontroller.arduino.sensors.pressure.pressure_sensor_arduino import PressureArduinoSensor +from app.content.microcontroller.arduino.sensors.orientation_arduino import OrientationSensor -from app.content.sensors.plyer.spatial_orientation_plyer import PlyerSpatialOrientationSensor - -from app.flight_config import FlightConfig +from app.flight_config import FlightConfig from app.logic.rocket_definition import Rocket # from app.ui.part_ui import PartUi @@ -63,13 +55,20 @@ def make_spatula() -> FlightConfig: inertialFrame = InertialReferenceFrame(acc, gyro, UUID('27f5d5e0-5fa9-4ae1-88af-8477d80960d7'), 'Intertial Reference Frame', rocket) # # Serial communication + # Arduino parts arduino_serial = ArduinoSerial(UUID('cd170fff-0138-4820-8e97-969eb3f2f287'), 'Serial Port', rocket) parachute = ServoSensor(UUID('9f86acb1-9795-46fc-b083-e6451f214d1f'), 'Servo', rocket, arduino_serial) igniter = IgniterSensor(UUID('f309669d-6bd7-4ee3-90a5-45a0e1bdd60e'), 'Igniter', rocket, arduino_serial, parachute) - temperature = TemperatureSensor(UUID('ac93964a-6bb0-11ee-b962-0242ac120002'), 'Temperature', rocket, arduino_serial) - photoresistor = PhotoresistorSensor(UUID('158314cc-6d1f-11ee-b962-0242ac120002'), 'Orientation', rocket, arduino_serial) - pressure = PressureSensor(UUID('eedd649e-78c7-11ee-b962-0242ac120002'), 'Pressure', rocket, arduino_serial) - altitude = AltitudeSensor(UUID('f526cb42-78c7-11ee-b962-0242ac120002'), 'Altitude', rocket, arduino_serial) + + # Arduino sensors + orientation = OrientationSensor(UUID('158314cc-6d1f-11ee-b962-0242ac120002'), 'Orientation', rocket, arduino_serial) + + # Pressure arduino sensor + temperature = TemperatureSensor(UUID('ac93964a-6bb0-11ee-b962-0242ac120002'), 'Temperature', rocket) + pressure = PressureSensor(UUID('eedd649e-78c7-11ee-b962-0242ac120002'), 'Pressure', rocket) + altitude = AltitudeSensor(UUID('f526cb42-78c7-11ee-b962-0242ac120002'), 'Altitude', rocket) + pressureArduino = PressureArduinoSensor(UUID('8ed5e972-8cb3-11ee-b9d1-0242ac120002'), 'Pressure Sensor', rocket, + arduino_serial, temperature, pressure, altitude) # FlightDirector(UUID('37155a2c-c51d-41b7-9dae-67d640d8c284'), 'Flight Director', rocket, arduino_serial, igniter, parachute, acc, gyro, inertialFrame) From 7c6d6e2ff7bfd149095f5ee15b4ffe50e077176f Mon Sep 17 00:00:00 2001 From: Andrej Volckov <125829671+AndrejVD@users.noreply.github.com> Date: Tue, 28 Nov 2023 02:23:04 +0000 Subject: [PATCH 4/5] proccessCommand method fix --- .../microcontroller/arduino/parts/igniter.py | 20 ++------ .../microcontroller/arduino/parts/servo.py | 21 ++------ app/content/microcontroller/arduino_serial.py | 49 +++++++++++-------- 3 files changed, 38 insertions(+), 52 deletions(-) diff --git a/app/content/microcontroller/arduino/parts/igniter.py b/app/content/microcontroller/arduino/parts/igniter.py index 7cc9286..41a2cd6 100644 --- a/app/content/microcontroller/arduino/parts/igniter.py +++ b/app/content/microcontroller/arduino/parts/igniter.py @@ -35,8 +35,6 @@ class IgniterSensor(Part): commandList : dict() - commandProccessingDict : dict() - partID : chr def __init__(self, _id: UUID, name: str, parent: Union[Part, Rocket, None], arduino_parent: Union[ArduinoSerial, None], parachute: ServoSensor, start_enabled=True): @@ -49,22 +47,12 @@ def __init__(self, _id: UUID, name: str, parent: Union[Part, Rocket, None], ardu self.partID = 2 self.commandList = { 'Ignite' : 0 } - self.commandProccessingDict = dict() self.arduino.addCallback(self.partID, self.proccessCommand) - def proccessCommand(self, index : int, result : int): - print(index, result) - command = self.commandProccessingDict[index] - if result == 0: - command.state = 'success' - command.response_message = 'Ignited' - - self.arduino.launchPhase = 'LiftOff' - else: - command.state = 'failed' - command.response_message = self.arduino.errorMessageDict[result] + def proccessCommand(self, command : Command): + command.response_message = 'Ignited' - self.commandProccessingDict.pop(index) + self.arduino.launchPhase = 'LiftOff' def get_accepted_commands(self) -> list[Type[Command]]: return [IgniteCommand] @@ -84,7 +72,7 @@ def update(self, commands: Iterable[Command], now, iteration): self.last_ignited = now self.parachute_triggered = False - self.commandProccessingDict[self.last_ignite_future.result()] = c + self.arduino.commandProccessingDict[self.last_ignite_future.result()] = c c.state = 'processing' diff --git a/app/content/microcontroller/arduino/parts/servo.py b/app/content/microcontroller/arduino/parts/servo.py index 9b5a6ca..f1e69f3 100644 --- a/app/content/microcontroller/arduino/parts/servo.py +++ b/app/content/microcontroller/arduino/parts/servo.py @@ -30,8 +30,6 @@ class ServoSensor(Part): commandList : dict() - commandProccessingDict : dict() - partID : chr def __init__(self, _id: UUID, name: str, parent: Union[Part, Rocket, None], arduino_parent: Union[ArduinoSerial, None],start_enabled=True): @@ -42,21 +40,12 @@ def __init__(self, _id: UUID, name: str, parent: Union[Part, Rocket, None], ardu self.partID = 1 self.commandList = { 'Close' : 0, 'Open' : 1 } - self.commandProccessingDict = dict() self.arduino.addCallback(self.partID, self.proccessCommand) - def proccessCommand(self, index : int, result : int): - print(index, result) - command = self.commandProccessingDict[index] - if result == 0: - command.state = 'success' - command.response_message = 'Servo activated' - else: - command.state = 'failed' - command.response_message = self.arduino.errorMessageDict[result] - - self.commandProccessingDict.pop(index) + def proccessCommand(self, command : Command): + command.response_message = 'Servo activated' + print("ssssssssss") def get_accepted_commands(self) -> list[Type[Command]]: return [DisableCommand, EnableCommand, OpenCommand, CloseCommand] @@ -87,7 +76,7 @@ def update(self, commands: Iterable[Command], now, iteration): self.last_command = c self.last_ignite_future = self.arduino.send_message(self.partID, self.commandList["Close"]) - self.commandProccessingDict[self.last_ignite_future.result()] = c + self.arduino.commandProccessingDict[self.last_ignite_future.result()] = c c.state = 'processing' elif isinstance(c, OpenCommand): @@ -96,7 +85,7 @@ def update(self, commands: Iterable[Command], now, iteration): self.last_command = c self.last_ignite_future = self.arduino.send_message(self.partID, self.commandList["Open"]) - self.commandProccessingDict[self.last_ignite_future.result()] = c + self.arduino.commandProccessingDict[self.last_ignite_future.result()] = c c.state = 'processing' diff --git a/app/content/microcontroller/arduino_serial.py b/app/content/microcontroller/arduino_serial.py index 715acea..69ebdab 100644 --- a/app/content/microcontroller/arduino_serial.py +++ b/app/content/microcontroller/arduino_serial.py @@ -120,23 +120,14 @@ def __init__(self, _id: UUID, name: str, parent: Union[Part, Rocket, None], star - def proccessCommand(self, index: int, result: int): - print(index, result) - command = self.commandProccessingDict[index] - if result == 0: - command.state = 'success' - command.response_message = 'Command activated' - - if isinstance(command, SetIgnitionPhaseCommand): - self.launchPhase = 'Ignition' - - elif isinstance(command, SetPreparationPhaseCommand): - self.launchPhase = 'Preparation' - else: - command.state = 'failed' - command.response_message = self.arduino.errorMessageDict[result] + def proccessCommand(self, command : Command): + command.response_message = 'Command activated' + + if isinstance(command, SetIgnitionPhaseCommand): + self.launchPhase = 'Ignition' - self.commandProccessingDict.pop(index) + elif isinstance(command, SetPreparationPhaseCommand): + self.launchPhase = 'Preparation' def addCallback(self, key : chr, fun): self.sensorsCallBack[key] = fun @@ -236,14 +227,32 @@ def on_read(a): if a[0] >> 7 | 0 == 1: response = ResponseMessage(a) + if response.getResponseRequestByte() == 1: - try: - self.sensorsCallBack[response.getPart()](response.getIndex(), response.getResult()) - except Exception as ex: - pass + index = response.getIndex() + result = response.getResult() + + + if index in self.commandProccessingDict: + print(index, result) + + command = self.commandProccessingDict[index] + + if result == 0: + command.state = 'success' + self.sensorsCallBack[response.getPart()](command) + + else: + command.state = 'failed' + command.response_message = self.errorMessageDict[result] + + self.commandProccessingDict.pop(index) + + else: message, _ = sendCommand(self.partID, self.commandList[self.launchPhase]) self.send_message_hdlc(message) + else: sensorData = SensorData(a) self.sensorsCallBack[sensorData.getPart()](sensorData.getData()) From 565fadcf521d81506bc0f539373f5aaf46195dda Mon Sep 17 00:00:00 2001 From: AndrejVD <125829671+AndrejVD@users.noreply.github.com> Date: Tue, 5 Dec 2023 05:37:49 +0000 Subject: [PATCH 5/5] update speed set to 50 ms --- app/content/microcontroller/arduino_serial.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/content/microcontroller/arduino_serial.py b/app/content/microcontroller/arduino_serial.py index 69ebdab..a201369 100644 --- a/app/content/microcontroller/arduino_serial.py +++ b/app/content/microcontroller/arduino_serial.py @@ -49,7 +49,7 @@ class ArduinoSerial(Part): connected: bool = False - min_update_period = timedelta(milliseconds=1000) + min_update_period = timedelta(milliseconds=50) min_measurement_period = timedelta(milliseconds=1000)