3
\$\begingroup\$

I'm trying to design a protocol for sending/receiving serial data.

The basic plan is to allow for text data to be entered in the command prompt, translate that to a hex string and send it out over the serial connection.

The hex protocol with the serial device is as follows:

byte[0]: address of the serial device peripheral we are interested in
byte[1]: command to send
byte[2]: optional parameter
byte[3]: optional parameter

I like the idea of dictionaries to do that command line translation so that I can do something like this.

commands = {
    ...
    'ping': [0x01, 0x01, 0x00, 0x00],
    ...
}

input = raw_input("<< ")

if input in commands:
    serial.write(commands[input])
else:
    print "Unknown command" + input

This seems fine for simple commands like ping but I'm not sure what to do when it comes to implementing more complex commands like pinging a specified peripheral

>> ping host

My first idea is to tokenize the input and use nested dictionaries to hold the commands.

commands = {
    'ping': {
        'default': [0x01, 0x01, 0x00, 0x00],
        'host': [0x01, 0x01, 0x00, 0x00],
        'sensor': [0x02, 0x01, 0x00, 0x00]
    }
}

I haven't looked up how to tokenize a string yet so I'll use pseudo for that here.

input = raw_input(">> ")
tokens = tokenize(input)

if tokens[0] in commands:
    if tokens[1] is not '':
        if tokens[1] in commands[tokens[0]]:
            if tokens[2] is not '':
                ...
            else:
                serial.write(commands[tokens[0]][tokens[1]])
        else:
            print "Unknown Address" + tokens[1]
    else:
        serial.write(commands[token[0]]['default']
else:
    print "Unknown command" + tokens[0]

I feel like I may be headed in the wrong direction here though, I'm worried the dictionary will become somewhat unwieldy, any thoughts? How would you do a lookup table of this sort?


I've somewhat implemented what would be the final solution for this style of lookup.

First I define my protocol dictionary

myProtocol.py

class MyProtocol:

    ''' Handshaking '''
    _zero = 0x00
    _start = 0xA9
    _stop = 0x9A
    _ack = 0xB8
    _nack = 0x8B
    _err = 0xEE

    handshake = {
        'zero'  : _zero,
        'start' : _start,
        'stop'  : _stop,
        'ack'   : _ack,
        'nack'  : _nack,
        'err'   : _err
    }

    ''' Address '''
    _host   = 0x01 # target device
    _sdcard = 0x03

    addresses = {
        'host'   : _host
    }

    ''' Common Commands '''
    _ping = 0x01 # request an ack
    _get  = 0x02 # retrieve data
    _set  = 0x03 # modify settings

    ''' Command Strings '''
    command = {
        # default ping
        'ping' : [_host, _ping, _zero, _zero],
        # commands issued to the device itself
        'host' : {
            'ping' : [_host, _ping, _zero, _zero],
            'get'  : {
                'date' : [_host, _get, 0x01, _zero]
            },
            'set'  : {
                'date' : [_host, _set, 0x01, 0x01], # to be followed by a message with the date
            }
        }
    };

Next, the program to read user input and transform it into a hex command. This code is ignoring the actual serial communication for now, just receiving input and printing out the associated command (for valid input).

serialCom.py

def processInput(inList, dictionary):
    # recursively process a list of input
    head = inList[0]
    tail = inList[1:]

    if head == 'exit':
        print "received exit command"
        ser.close()
        exit()

    # is this a valid token?
    if head in dictionary:
        print head + " ",

        # are there more commands to process?
        if len(tail) > 0:
            processInput(tail, dictionary[head])
        else:
            # have we found a command
            if isinstance(dictionary[head], list):
                print "\n  command found:",
                for i in dictionary[head]:
                    print "%#04X" % i,
                print
            elif isinstance(dictionary[head], dict):
                print ":: insufficient arguments"
                print "  requires:"
                for i in sorted(dictionary.keys()): #dictionary[head].keys():
                    # print the possible arguments
                    print "    " + i
            else:
                # this shouldn't happen, the command dictionary is corrupt
                print ":: invalid command dictionary"
    else:
        print ":: invalid command[" + head + "]"
        print "  possible commands:"
        for i in sorted(dictionary.keys()):
            # print the possible commands
            print "    " + i

while 1:
    # get keyboard input
    command = raw_input("<< ")

    token = command.split()

    processInput(token, MyProtocol.command)
\$\endgroup\$
  • 2
    \$\begingroup\$ Welcome to Code Review! It looks like you have the beginnings of an interesting question, but first you must have a version of the code that works. \$\endgroup\$ – 200_success Sep 24 '14 at 22:10
2
\$\begingroup\$

Maybe my approach is crude, but the first thought I had was to use a simple dictionary with key as "ping host". If user separates command and parameters with multiple spaces or tabs, that too can be handled by preprocessing the command before indexing the dictionary.

Another approach that may be more pythonic would be to convert the input into a tuple and use that tuple to index the dictionary.

Maybe there is a specific reason why you require hierarchy, which is not obvious to me.

\$\endgroup\$
  • \$\begingroup\$ Thanks for the answer. It actually never occurred to me to use spaces within the key. \$\endgroup\$ – user3817250 Nov 3 '14 at 14:51

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.