diff --git a/python/SerialMuxProt/examples/serial/__main__.py b/python/SerialMuxProt/examples/serial/__main__.py index 49ab1ad..a5629b7 100644 --- a/python/SerialMuxProt/examples/serial/__main__.py +++ b/python/SerialMuxProt/examples/serial/__main__.py @@ -31,14 +31,14 @@ import sys import time from serial_client import SerialClient -from SerialMuxProt import SerialMuxProt +from SerialMuxProt import Server ################################################################################ # Variables ################################################################################ g_socket = SerialClient("COM3", 115200) -smp_server = SerialMuxProt(10, g_socket) +smp_server = Server(10, g_socket) START_TIME = round(time.time()*1000) ################################################################################ diff --git a/python/SerialMuxProt/examples/serial/serial_client.py b/python/SerialMuxProt/examples/serial/serial_client.py index a54a555..49df4a6 100644 --- a/python/SerialMuxProt/examples/serial/serial_client.py +++ b/python/SerialMuxProt/examples/serial/serial_client.py @@ -29,6 +29,7 @@ ################################################################################ import serial +from SerialMuxProt import Stream ################################################################################ # Variables @@ -39,7 +40,7 @@ ################################################################################ -class SerialClient: +class SerialClient(Stream): """ Class for Serial Communication """ @@ -77,7 +78,7 @@ def disconnect_from_server(self) -> None: self.__serial.close() - def write(self, payload : bytearray) -> int: + def write(self, payload: bytearray) -> int: """ Sends Data to the Server. Parameters diff --git a/python/SerialMuxProt/src/SerialMuxProt/__init__.py b/python/SerialMuxProt/src/SerialMuxProt/__init__.py new file mode 100644 index 0000000..933cbcf --- /dev/null +++ b/python/SerialMuxProt/src/SerialMuxProt/__init__.py @@ -0,0 +1,32 @@ +"""__init__""" # pylint: disable=invalid-name + +# MIT License +# +# Copyright (c) 2023 - 2024 Gabryel Reyes +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# + + +################################################################################ +# Imports +################################################################################ + +from .server import Server +from .stream import Stream diff --git a/python/SerialMuxProt/src/SerialMuxProt/SerialMuxProt.py b/python/SerialMuxProt/src/SerialMuxProt/server.py similarity index 95% rename from python/SerialMuxProt/src/SerialMuxProt/SerialMuxProt.py rename to python/SerialMuxProt/src/SerialMuxProt/server.py index eae9650..3b8d48c 100644 --- a/python/SerialMuxProt/src/SerialMuxProt/SerialMuxProt.py +++ b/python/SerialMuxProt/src/SerialMuxProt/server.py @@ -30,7 +30,7 @@ from dataclasses import dataclass from struct import Struct -from socket_client import SocketClient +from .stream import Stream ################################################################################ # Variables @@ -151,10 +151,10 @@ def __init__(self) -> None: self.receive_frame = Frame() -class SerialMuxProt: +class Server: """ SerialMuxProt Server """ - def __init__(self, max_configured_channels: int, stream: SocketClient) -> None: + def __init__(self, max_configured_channels: int, stream: Stream) -> None: self.__max_configured_channels = max_configured_channels self.__stream = stream self.__rx_data = RxData() @@ -167,7 +167,7 @@ def process(self, current_timestamp: int) -> None: Parameters ---------- - current_timestamp : int + current_timestamp: int Time in milliseconds. """ @@ -183,9 +183,9 @@ def send_data(self, channel_name: str, payload: bytearray) -> bool: Parameters: ---------- - channel_name : str + channel_name: str Channel to send frame to. - payload : bytearray + payload: bytearray Byte buffer to be sent. Returns: @@ -219,9 +219,9 @@ def create_channel(self, name: str, dlc: int) -> int: Parameters: ---------- - name : str + name: str Name of the channel. It will not be checked if the name already exists. - dlc : int + dlc: int Length of the payload of this channel. Returns: @@ -254,9 +254,9 @@ def subscribe_to_channel(self, name: str, callback) -> None: Parameters: ---------- - name : str + name: str Name of the Channel to suscribe to. - callback : function + callback: function Callback to return the incoming data. """ @@ -276,7 +276,7 @@ def get_tx_channel_number(self, channel_name: str) -> int: Parameters: ----------- - channel_name : str + channel_name: str Name of channel Returns: @@ -298,7 +298,7 @@ def __heartbeat(self, current_timestamp: int) -> None: Parameters ---------- - current_timestamp : int + current_timestamp: int Time in milliseconds. """ @@ -347,7 +347,8 @@ def __process_rx(self) -> None: dlc = self.__rx_data.receive_frame.dlc # DLC = 0 means that the channel does not exist. - if (0 != dlc) and (SerialMuxProtConstants.MAX_RX_ATTEMPTS >= self.__rx_data.rx_attempts): + if (0 != dlc) and \ + (SerialMuxProtConstants.MAX_RX_ATTEMPTS >= self.__rx_data.rx_attempts): remaining_payload_bytes = self.__rx_data.received_bytes - \ SerialMuxProtConstants.HEADER_LEN expected_bytes = dlc - remaining_payload_bytes @@ -363,7 +364,8 @@ def __process_rx(self) -> None: self.__rx_data.received_bytes += rcvd # Frame has been received. - if (0 != dlc) and ((SerialMuxProtConstants.HEADER_LEN + dlc) == self.__rx_data.received_bytes): + if (0 != dlc) and \ + ((SerialMuxProtConstants.HEADER_LEN + dlc) == self.__rx_data.received_bytes): # Check validity if self.__is_frame_valid(self.__rx_data.receive_frame) is True: @@ -371,6 +373,7 @@ def __process_rx(self) -> None: self.__rx_data.receive_frame.unpack_payload() # Differenciate between Control and Data Channels. + # pylint: disable=line-too-long if SerialMuxProtConstants.CONTROL_CHANNEL_NUMBER == self.__rx_data.receive_frame.channel: self.__callback_control_channel( self.__rx_data.receive_frame.payload) @@ -422,10 +425,10 @@ def __get_tx_channel_dlc(self, channel_number: int) -> int: Parameters ---------- - channel_number : int + channel_number: int Channel number to check. - is_tx_channel : bool + is_tx_channel: bool Is the Channel a TX Channel? If false, will return value for an RX Channel instead. Returns @@ -448,7 +451,7 @@ def __is_frame_valid(self, frame: Frame) -> bool: Parameters ---------- - frame : Frame + frame: Frame Frame to be checked Returns: @@ -463,7 +466,7 @@ def __checksum(self, raw_frame: bytearray) -> int: Parameters: ---------- - raw_frame : Frame + raw_frame: Frame Frame to calculate checksum for Returns: @@ -484,9 +487,9 @@ def __send(self, channel_number: int, payload: bytearray) -> bool: Parameters: ---------- - channel_number : int + channel_number: int Channel to send frame to. - payload : bytearray + payload: bytearray Payload to send Returns: @@ -524,13 +527,14 @@ def __cmd_sync(self, payload: bytearray) -> None: Parameters: ----------- - payload : bytearray + payload: bytearray Command Data of received frame """ response = bytearray( SerialMuxProtConstants.CONTROL_CHANNEL_PAYLOAD_LENGTH) - response[SerialMuxProtConstants.CONTROL_CHANNEL_COMMAND_INDEX] = SerialMuxProtConstants.Commands.SYNC_RSP + response[SerialMuxProtConstants.CONTROL_CHANNEL_COMMAND_INDEX] =\ + SerialMuxProtConstants.Commands.SYNC_RSP response[SerialMuxProtConstants.CONTROL_CHANNEL_PAYLOAD_INDEX:] = payload self.__send(SerialMuxProtConstants.CONTROL_CHANNEL_NUMBER, response) @@ -540,7 +544,7 @@ def __cmd_sync_rsp(self, payload: bytearray) -> None: Parameters: ----------- - payload : bytearray + payload: bytearray Command Data of received frame """ @@ -562,13 +566,14 @@ def __cmd_scrb(self, payload: bytearray) -> None: Parameters: ----------- - payload : bytearray + payload: bytearray Command Data of received frame """ response = bytearray( SerialMuxProtConstants.CONTROL_CHANNEL_PAYLOAD_LENGTH) - response[SerialMuxProtConstants.CONTROL_CHANNEL_COMMAND_INDEX] = SerialMuxProtConstants.Commands.SCRB_RSP + response[SerialMuxProtConstants.CONTROL_CHANNEL_COMMAND_INDEX] = \ + SerialMuxProtConstants.Commands.SCRB_RSP # Parse name channel_name = str(payload[5:], "ascii").strip('\x00') @@ -590,7 +595,7 @@ def __cmd_scrb_rsp(self, payload: bytearray) -> None: Parameters: ----------- - payload : bytearray + payload: bytearray Command Data of received frame """ @@ -630,7 +635,7 @@ def __callback_control_channel(self, payload: bytearray) -> None: Parameters: ----------- - payload : bytearray + payload: bytearray Payload of received frame """ if len(payload) != SerialMuxProtConstants.CONTROL_CHANNEL_PAYLOAD_LENGTH: diff --git a/python/SerialMuxProt/src/SerialMuxProt/stream.py b/python/SerialMuxProt/src/SerialMuxProt/stream.py new file mode 100644 index 0000000..0e4e3e0 --- /dev/null +++ b/python/SerialMuxProt/src/SerialMuxProt/stream.py @@ -0,0 +1,93 @@ +""" Stream interface for SerialMuxProt. """ + +# MIT License +# +# Copyright (c) 2023 - 2024 Gabryel Reyes +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# + + +################################################################################ +# Imports +################################################################################ + +from abc import ABC, abstractmethod + +################################################################################ +# Variables +################################################################################ + +################################################################################ +# Classes +################################################################################ + + +class Stream(ABC): + """ + Stream Interface for SerialMuxProt. + """ + + @abstractmethod + def available(self) -> int: + """ Get the number of bytes available in the stream. + + Returns: + -------- + Number of bytes available in the stream. + """ + + @abstractmethod + def read_bytes(self, length: int) -> tuple[int, bytearray]: + """ Read a number of bytes from the stream. + + Parameters: + ----------- + length : int + Number of bytes to read. + + Returns + ---------- + Tuple: + - int: Number of bytes received. + - bytearray: Received data. + """ + + @abstractmethod + def write(self, payload: bytearray) -> int: + """ + Write a bytearray to the stream. + + Parameters: + ----------- + payload: bytearray + Data to write to the stream. + + Returns: + -------- + Number of bytes written. + """ + +################################################################################ +# Functions +################################################################################ + +################################################################################ +# Main +################################################################################