Skip to content

Commit

Permalink
Renamed and improved SerialMux for Python.
Browse files Browse the repository at this point in the history
Uses abstract classes for interfaces
  • Loading branch information
gabryelreyes committed Jul 16, 2024
1 parent bd7fd42 commit 061fb38
Show file tree
Hide file tree
Showing 5 changed files with 162 additions and 31 deletions.
4 changes: 2 additions & 2 deletions python/SerialMuxProt/examples/serial/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)

################################################################################
Expand Down
5 changes: 3 additions & 2 deletions python/SerialMuxProt/examples/serial/serial_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
################################################################################

import serial
from SerialMuxProt import Stream

################################################################################
# Variables
Expand All @@ -39,7 +40,7 @@
################################################################################


class SerialClient:
class SerialClient(Stream):
"""
Class for Serial Communication
"""
Expand Down Expand Up @@ -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
Expand Down
32 changes: 32 additions & 0 deletions python/SerialMuxProt/src/SerialMuxProt/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
"""__init__""" # pylint: disable=invalid-name

# MIT License
#
# Copyright (c) 2023 - 2024 Gabryel Reyes <[email protected]>
#
# 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
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@

from dataclasses import dataclass
from struct import Struct
from socket_client import SocketClient
from .stream import Stream

################################################################################
# Variables
Expand Down Expand Up @@ -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()
Expand All @@ -167,7 +167,7 @@ def process(self, current_timestamp: int) -> None:
Parameters
----------
current_timestamp : int
current_timestamp: int
Time in milliseconds.
"""
Expand All @@ -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:
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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.
"""

Expand All @@ -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:
Expand All @@ -298,7 +298,7 @@ def __heartbeat(self, current_timestamp: int) -> None:
Parameters
----------
current_timestamp : int
current_timestamp: int
Time in milliseconds.
"""
Expand Down Expand Up @@ -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
Expand All @@ -363,14 +364,16 @@ 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:
channel_array_index = self.__rx_data.receive_frame.channel - 1
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)
Expand Down Expand Up @@ -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
Expand All @@ -448,7 +451,7 @@ def __is_frame_valid(self, frame: Frame) -> bool:
Parameters
----------
frame : Frame
frame: Frame
Frame to be checked
Returns:
Expand All @@ -463,7 +466,7 @@ def __checksum(self, raw_frame: bytearray) -> int:
Parameters:
----------
raw_frame : Frame
raw_frame: Frame
Frame to calculate checksum for
Returns:
Expand All @@ -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:
Expand Down Expand Up @@ -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)
Expand All @@ -540,7 +544,7 @@ def __cmd_sync_rsp(self, payload: bytearray) -> None:
Parameters:
-----------
payload : bytearray
payload: bytearray
Command Data of received frame
"""

Expand All @@ -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')
Expand All @@ -590,7 +595,7 @@ def __cmd_scrb_rsp(self, payload: bytearray) -> None:
Parameters:
-----------
payload : bytearray
payload: bytearray
Command Data of received frame
"""

Expand Down Expand Up @@ -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:
Expand Down
93 changes: 93 additions & 0 deletions python/SerialMuxProt/src/SerialMuxProt/stream.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
""" Stream interface for SerialMuxProt. """

# MIT License
#
# Copyright (c) 2023 - 2024 Gabryel Reyes <[email protected]>
#
# 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
################################################################################

0 comments on commit 061fb38

Please sign in to comment.