Python Forum
managing command codes for external controller box
Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
managing command codes for external controller box
#1
I am trying to create a library to encode commands sent over TCP to a controller box and decode the responses.
To makes things simple, each command corresponds to a hex code with a number of arguments, to which the controller box outputs response.

The aim of the code below is to implement the encoding/decoding of bytes exchanged with the controller with the following constraints:
  • Use easily identifiable command name instead of an obscure hex code
  • Provide under the hood the correct format for encoding/decoding of bytes exchanged with the controller box
  • Perform additional operations on the decoded bytes (formatting, convert, etc) if needed

The first two points are easy enough, however the 3rd point requires to write specific decoding methods for a majority of commands.
I have written 2 different approaches, which don't really satisfy me some various reasons. The first example would be the simplest if I did not have to implement specific decoding. The 2nd is my preferred method in this case, although I am not sure using subclassing like this is the way to go. Any thoughts or suggestions?



[Example 1: one class]
In this example, a Command is instanciated with one of the command names: the codes dictionary in the __init__ gives the correct format codes to use with struct.pack/unpack.
The main drawback is that all the specific decoding methods will have to be declared in the command class, likely resulting in a code that is harder to read and to maintain.

CLOSE_LNK = 0x25
GET_VERSION = 0x2A5
READ_NB_AXES = 0x34D

class Command:
    def __init__(self, code):
        codes = {CLOSE_LNK: {},
                 GET_VERSION: {'in_fmt': '<?', 'out_fmt': '< 4I'},
                 READ_NB_AXES: {'out_fmt': '< IBI', 'custom_decode': self.dec_0x34D}
                 # list goes on...
                 }
        properties = codes[code]

        self.command = code
        self.in_fmt = properties.get('in_fmt')
        self.out_fmt = properties.get('out_fmt')
        self.c_encode = properties.get('custom_encode')
        self.c_decode = properties.get('custom_decode')

    def encode(self, *args):
        # cls._check_argnb(*args)
        if self.c_encode is not None:
            return self.c_encode(*args)
        if self.in_fmt is None:
            return b''
        return struct.pack(self.in_fmt, *args)

    def decode(self, in_bytes):
        if self.c_decode is not None:
            return self.c_decode(in_bytes)
        if self.out_fmt is None:
            return b''
        return struct.unpack(self.out_fmt, in_bytes)

    # define custom encode/decode methods
    def dec_0x34D(self, in_bytes):
        return struct.unpack(self.out_fmt, in_bytes + b'\x00')
    # .....
    # .....

if __name__=="__main__":
    a = Command(GET_VERSION)
    print(a.encode(True))
    print(a.decode(b'\x12\x2D\x00\x12\x2D\x00\x12\x2D\x00\x12\x2D\x00\x12\x2D\x00\x12'))
[Example 2: subclassing]
Here I am making a lot of subclasses, one per command type. In this case I can easily reimplement the encode and decode methods to suit my needs, and it's easier to add and remove commands.
class Command:

    @classmethod
    def encode(cls, *args):
        return struct.pack(cls._in_fmt, *args)

    @classmethod
    def decode(cls, in_bytes):
        return struct.unpack(cls._out_fmt, in_bytes)

    @classmethod
    def get_id(cls):
        return cls._id

class CLOSE_LNK(Command):
    _id = 0x25

    @classmethod
    def encode(cls):
        return b''

    @classmethod
    def decode(cls, in_bytes):
        return b''

class GET_VERSION(Command):
    _id = 0x2A5
    _in_fmt = '<?'
    _out_fmt = '< 4I'
    _argnb = 1

class READ_NB_AXES(Command):
    _id = 0x34D
    _out_fmt = '< IBI'

    @classmethod
    def encode(cls):
        return b''

    @classmethod
    def decode(cls, in_bytes):
        out = super().decode(in_bytes + b'\x00')
        return out


if __name__=="__main__":
    print(GET_VERSION.encode(True))
    print(GET_VERSION.decode(b'\x12\x2D\x00\x12\x2D\x00\x12\x2D\x00\x12\x2D\x00\x12\x2D\x00\x12'))
Reply


Possibly Related Threads…
Thread Author Replies Views Last Post
  Python and DMX (aka, light controller) ScottAF 4 2,548 Apr-06-2023, 07:09 PM
Last Post: ScottAF
  managing new windows JonWayn 1 1,705 Sep-22-2022, 05:26 PM
Last Post: Larz60+
  How can flask find the controller? 3lnyn0 3 1,288 Jul-08-2022, 10:05 AM
Last Post: Larz60+
  Controller Buzzer as Distance Decreases barkster 6 2,037 Nov-01-2021, 03:26 PM
Last Post: barkster
  Managing Objects JoeDainton123 1 1,665 May-15-2021, 03:18 PM
Last Post: Yoriz
  Auto re-pair / re-sync Controller via Script? User3000 2 2,281 Nov-30-2020, 11:42 AM
Last Post: User3000
  Tuning PID controller Sancho_Pansa 4 4,281 Nov-09-2020, 07:51 AM
Last Post: Sancho_Pansa
  Managing dependencies with pipenv t4keheart 6 2,877 Aug-05-2020, 12:39 AM
Last Post: t4keheart
  Xbox Controller arki 0 1,686 Jun-30-2020, 10:32 AM
Last Post: arki
  Please help! Akai APC20 midi controller script versusliveact 0 2,095 Feb-14-2020, 05:37 AM
Last Post: versusliveact

Forum Jump:

User Panel Messages

Announcements
Announcement #1 8/1/2020
Announcement #2 8/2/2020
Announcement #3 8/6/2020