Guest User

MoteinoBeta

a guest
Feb 21st, 2016
24
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. import serial
  2. import threading
  3. import time
  4. __author__ = 'SteinarrHrafn'
  5.  
  6. """
  7. Example usage:
  8.  
  9.  
  10. from MoteinoBeta import MoteinoNetwork
  11.  
  12.  
  13. class MyNetwork(MoteinoNetwork):
  14.    def __init__(self):
  15.        MoteinoNetwork.__init__(self, port='COM50', baudrate=9600)
  16.  
  17.    def recieve(self, diction): #  overwrite this to respond
  18.        print diction
  19.  
  20.  
  21. mynetwork = MyNetwork()
  22. mynetwork.add_device('TestDevice', 0, "int Command;int Something;")
  23. mynetwork.start_listening()
  24.  
  25. mynetwork.send({'Send2': 'TestDevice', 'Command': 123, 'Something': 456})
  26. """
  27.  
  28.  
  29.  
  30. def dprint(s):
  31.     if True:
  32.         print s
  33.  
  34.  
  35. def _hexprints(n):
  36.     if n > 255:
  37.         raise ValueError('n too big for _hexprints')
  38.     if n > 15:
  39.         return hex(n)[2:]
  40.     else:
  41.         return '0' + hex(n)[2:]
  42.  
  43.  
  44. def _hex2dec(s):
  45.     if len(s) > 2:
  46.         raise ValueError('s too long for _hex2dec')
  47.     else:
  48.         # debug_print("S: " + s)
  49.         return int(s, base=16)
  50.  
  51.  
  52. class Byte:
  53.     """
  54.    A class describing the byte datatype
  55.    """
  56.     NofBytes = 1
  57.  
  58.     def __init__(self):
  59.         pass
  60.  
  61.     @staticmethod
  62.     def hexprints(i=0):
  63.         if i > 2**8:
  64.             raise Exception("Of stor tala til ad komast fyrir i byte")
  65.         return _hexprints(i)
  66.  
  67.     @staticmethod
  68.     def hex2dec(s):
  69.         return _hex2dec(s)
  70.  
  71.  
  72. class Int:
  73.     """
  74.    A class describing the int datatype         ATTENTION! this is actually unsigned int
  75.    """
  76.     NofBytes = 2
  77.  
  78.     def __init__(self):
  79.         pass
  80.  
  81.     @staticmethod
  82.     def hexprints(i=0):
  83.         if i > 2**16:
  84.             raise Exception("Of stor tala til ad komast fyrir i int")
  85.         return _hexprints(i % 256) + _hexprints(i/2**8)
  86.  
  87.     @staticmethod
  88.     def hex2dec(s):
  89.         return _hex2dec(s[2:4])*2**8 + _hex2dec(s[:2])
  90.  
  91.  
  92. class Array:
  93.     """
  94.    A class to describe the array datatype, requires the subtype to be defined
  95.    """
  96.     def __init__(self, subtype, n):
  97.         self.SubType = subtype
  98.         self.N = n
  99.         self.NofBytes = subtype.NofBytes*n
  100.  
  101.     def hexprints(self, l=list()):
  102.         returner = str()
  103.         while len(l) < self.N:
  104.             l.append(0)
  105.         for L in l:
  106.             returner += self.SubType.hexprints(L)
  107.         return returner
  108.  
  109.     def hex2dec(self, s):
  110.         returner = list()
  111.         for i in range(self.N):
  112.             returner.append(self.SubType.hex2dec(s[:self.SubType.NofBytes*2]))
  113.             s = s[self.SubType.NofBytes:]
  114.         return returner
  115.  
  116. # a dictionary of known datatypes to more easily call them
  117. types = {
  118.     'byte': Byte,
  119.     'int': Int,
  120. }
  121.  
  122.  
  123. class Struct:
  124.     """
  125.    This is a class for parsing a struct through a serial port
  126.    example:
  127.  
  128.            mystruct = Struct(  "int a;"
  129.                                "int b;")
  130.  
  131.            send_this_2_serial = mystruct.encode({'a': 1, 'b': 2])
  132.  
  133.            incoming = mystruct.decode(str_from_serial)
  134.  
  135.    """
  136.     def __init__(self, structstring):
  137.         self.Parts = list()
  138.         self.NofBytes = 0
  139.         lines = structstring.rstrip(';').split(';')  # remove the last ';' and split by the other ones
  140.         for line in lines:
  141.             temp = line.split()  # split by whitespaces
  142.             if not len(temp) == 2:  # each line should always contain 2 words
  143.                 raise ValueError(temp)
  144.             if '[' in temp[1]:  # if we are dealing with an array
  145.                 ttemp = temp[1].split('[')
  146.                 self.Parts.append((Array(types[temp[0]], int(ttemp[1][:-1])), ttemp[0]))
  147.             else:
  148.                 self.Parts.append((types[temp[0]], temp[1]))
  149.  
  150.     def encode(self, values_dict):
  151.         """
  152.        This function will encode the struct into a HEX string.
  153.        Not all values of the struct must be contained in values_dict,
  154.        those that are not present will be assumed to be 0
  155.  
  156.        :param values_dict: dict
  157.        :return: str
  158.        """
  159.         returner = str()
  160.         for (Type, Name) in self.Parts:
  161.             if Name in values_dict:
  162.                 returner += Type.hexprints(values_dict[Name])
  163.             else:
  164.                 returner += Type.hexprints()  # hexprints() assumes value is 0
  165.         return returner
  166.  
  167.     def decode(self, s):
  168.         """
  169.        This function will decode the struct recieved as a HEX string and return
  170.        a dict with the corresponding values.
  171.        The input string must be sufficiently long to contain the entire struct
  172.  
  173.        :param s: str
  174.        :return: dict
  175.        """
  176.         returner = dict()
  177.         # debug_print("S: \"" + s + "\"")
  178.         # debug_print("Parts: " + str(self.Parts))
  179.         for (Type, Name) in self.Parts:
  180.             returner[Name] = Type.hex2dec(s[:2*Type.NofBytes])
  181.             s = s[2*Type.NofBytes:]
  182.         return returner
  183.  
  184.  
  185. class Device:
  186.  
  187.     def __init__(self, parent, _id, structstring):
  188.         self.ID = _id
  189.         self.Struct = Struct(structstring)
  190.         self.LastSent = dict()
  191.         self.Parent = parent
  192.  
  193.     def send2radio(self, diction, expect_response=False):
  194.         """
  195.        :param diction: dict
  196.        :param expect_response: bool
  197.        :return:
  198.        """
  199.         self.Parent.ResponseExpected = expect_response
  200.         self.Parent.send2radio(send2id=self.ID, payload=self.Struct.encode(diction))
  201.         self.LastSent = diction
  202.  
  203.     def send2parent(self, payload):
  204.         """
  205.        :param payload: string
  206.        :return: None
  207.        """
  208.         d = self.Struct.decode(payload)
  209.         d['SenderID'] = self.ID
  210.         d['Sender'] = self.Parent.inv_IDs[self.ID]
  211.         self.Parent.recieve(d)
  212.  
  213.     def get_status(self):
  214.         self.send2radio({'Command': self.Parent.Commands['Status']}, expect_response=True)
  215.  
  216.  
  217. class Send2Parent(threading.Thread):
  218.     """
  219.    This is the thread that interprets the struct recieved by the moteino network
  220.    and runs the recieve function.
  221.    """
  222.     def __init__(self, parent, incoming):
  223.         threading.Thread.__init__(self)
  224.         self.Incoming = incoming
  225.         self.Parent = parent
  226.  
  227.     def run(self):
  228.         # debug_print("Recieved from BaseMoteino:  " + self.Incoming)
  229.  
  230.         # The first byte from the hex string is the sender ID.
  231.         # We use that to get a pointer to the sender (an instance of the Device class)
  232.         sender = self.Parent.devices[self.Parent.inv_IDs[_hex2dec(self.Incoming[:2])]]
  233.  
  234.         # Information about ACK/no ACK is sent with: "FFF0" for no ACK and "FFF1" for ACK
  235.  
  236.         if self.Incoming[2:5] == "FFF":
  237.             # print("ACK info recieved")
  238.             if self.Incoming[5] == "0":
  239.                 self.Parent.NoACK({'Event': 'MoteinoNoAck',
  240.                                    'LastSent': dict(sender.LastSent)})
  241.                 if self.Parent.RadioIsBusy:
  242.                     self.Parent.RadioIsBusy = False
  243.             else:
  244.                 #  We recieved an ack and are very happy about it
  245.                 if self.Parent.ResponseExpected:
  246.                     self.Parent.RadioIsBusy = True
  247.                 else:
  248.                     self.Parent.RadioIsBusy = False
  249.         else:
  250.             sender.send2parent(self.Incoming[2:])
  251.  
  252.  
  253. class ListeningThread(threading.Thread):
  254.     """
  255.    A thread that listens to the Serial port. When something (that ends with a newline) is recieved
  256.    the thread will start up the Send2Parent thread and go back to listening to the Serial port
  257.    """
  258.     def __init__(self, parent):
  259.         threading.Thread.__init__(self)
  260.         self.Parent = parent
  261.         self.Listen2 = parent.Serial
  262.  
  263.     def run(self):
  264.         while True:
  265.             incoming = self.Listen2.readline().rstrip('\n')  # nota [:-1]?
  266.             fire = Send2Parent(self.Parent, incoming)
  267.             fire.start()
  268.  
  269.  
  270. class MoteinoNetwork:
  271.     """
  272.    This is the class that user should inteface with for example:
  273.  
  274.  
  275.    class MyNetwork(MoteinoNetwork):
  276.        def __init__(self):
  277.            MoteinoNetwork.__init__(self, port='COM50', baudrate=9600)
  278.  
  279.        def recieve(self, diction):
  280.            print "We just recieved:"
  281.            print diction
  282.            print "from the moteino network"
  283.  
  284.  
  285.    mynetwork = MyNetwork()
  286.    mynetwork.add_device('TestDevice', 0, "int Command;int Something;")
  287.    mynetwork.start_listening()
  288.    mynetwork.send({'Send2': 'TestDevice', 'Command': 123, 'Something': 456})
  289.  
  290.    """
  291.  
  292.     def __init__(self,
  293.                  port,
  294.                  baudrate=115200,
  295.                  parity=serial.PARITY_NONE,
  296.                  stopbits=serial.STOPBITS_ONE,
  297.                  bytesize=serial.EIGHTBITS):
  298.  
  299.         self.Serial = serial.Serial(port=port, baudrate=baudrate, parity=parity, stopbits=stopbits, bytesize=bytesize)
  300.         self.SerialLock = threading.Lock()
  301.  
  302.         self.RadioIsBusy = False
  303.         self.ResponseExpected = False
  304.  
  305.         self.devices = dict()
  306.         self.IDs = dict()
  307.         self.inv_IDs = dict()
  308.  
  309.     def _wait_for_radio(self, max_wait=500):
  310.         counter = 1
  311.         # dprint("Waiting for radio....")
  312.         t = time.time()
  313.         while self.RadioIsBusy:
  314.             counter += 1
  315.             time.sleep(0.01)
  316.             if counter > (max_wait/10):
  317.                 break
  318.         dprint("I waited for radio for " + str((time.time() - t)*1000) + " ms")
  319.  
  320.     def send2radio(self, send2id, payload):
  321.         with self.SerialLock:
  322.             # debug_print("Waiting for radio....")
  323.             self._wait_for_radio()
  324.             self.Serial.write(_hexprints(send2id) + payload + '\n')
  325.             self.RadioIsBusy = True
  326.             # debug_print("Sending2Radio: " + _hexprints(send2id) + payload + '\n')
  327.  
  328.     def add_device(self, name, _id, structstring):
  329.         self.IDs[name] = _id
  330.         self.inv_IDs[_id] = name
  331.         self.devices[name] = Device(parent=self,
  332.                                     _id=_id,
  333.                                     structstring=structstring)
  334.  
  335.     def recieve(self, diction):
  336.         print diction
  337.  
  338.     def send(self, diction, expect_response=False):
  339.         """
  340.        This function should be called from top level script to send someting.
  341.        Input parameter diction is a dict that contains what should be sent.
  342.        The structure of diction depends on what device will recieve but diction
  343.        must contain the key 'Send2'
  344.  
  345.        :param diction: dict
  346.        :param expect_response: bool
  347.        :return: Nothing
  348.        """
  349.         if 'Send2' not in diction:
  350.             raise ValueError("Parameter diction di not contain a key \'Send2\'")
  351.  
  352.         self.devices[diction['Send2']].send2radio(diction, expect_response=expect_response)
  353.  
  354.     def start_listening(self):  # starts a thread that listens to the serial port
  355.         serial_listening_thread = ListeningThread(self)
  356.         serial_listening_thread.start()
  357.  
  358.     def get_all_statuses(self):
  359.         for d in self.devices.values():
  360.             d.get_status()
RAW Paste Data