2016-01-31 2 views
0

Я создаю проект python, который получает байты из последовательного порта. Байты - это ответы на отправленные команды (также через последовательный порт). Ответы не имеют идентификационных меток, т. Е. Из только байтов, я не знаю, какой именно ответ команды. Разумеется, декодер должен заранее знать, какая команда является ответом.Deserializing byte stream to objects

Я хотел бы иметь последовательность входящих байтов, представленную в виде вложенного объекта, указывающую кадр, заголовок, полезную нагрузку, декодированную полезную нагрузку и т. Д. Я бы предпочел нажимать 1 байт за раз на декодер и позвонить ему обратный вызов, когда он получил достаточное количество байтов для полного объекта (или errorCallback, если есть ошибки или таймаут).

Фактический кадр имеет начальный байт и конечный байт. Он имеет заголовок с несколькими байтами (некоторый id, статус команды (в основном, ok/fail)), один - байт длины данных. За этим следуют данные, за которыми следует контрольная сумма (один байт). Данные являются ответом на команду.

Ответ предсказуем в том, что предыдущие байты определяют, что означают будущие байты.

Пример ответа:

аа: 00: 0C: 00: 01: 00: 00: d3: 8d: d4: 5с: 50: 01: 04: е0: 6e: бб

Сломался :

aa: start frame 
    00: id 
    0c: data length (incl status): 12 bytes 
    00: command status (byte 1) 
     01: 1 data frame (byte 2) 
      00:00: flags of first data frame (byte 3-4) 
      d3:8d:d4:5c:50:01:04:e0: first data (aa and bb could be in it) (byte 5-12) 
    6e: checksum (includes all bytes except start, checksum, end bytes) 
bb: end frame 

Это является последовательным коммуникационным портом, байты может быть потерян (и дополнительными производства), и я ожидаю использовать тайм-аут для обработки сброса (никаких ответов не ожидается без первой команды отсылается).

Мне действительно хотелось бы, чтобы объектно-ориентированный подход, при котором декодер создавал объект, который при сериализации приводил бы к повторной последовательности байтов. Я использую python 2.7, но на самом деле любой объектно-ориентированный язык мог бы (пока я мог бы преобразовать его в python).

Я просто не уверен, как структурировать декодер, чтобы сделать его аккуратным. Я ищу полное решение, просто что-то, что заставит меня двигаться в правильном направлении (правильное направление здесь несколько субъективно).

+0

У вас больше шансов получить положительный ответ на ваш вопрос, если вы разместите какой-либо код. Не беспокойтесь, если он не опрятен или не использует какие-либо продвинутые трюки. Не только публикация какого-либо кода показывает, что вы сделали честную попытку самостоятельно решить проблему, плита кода - очень полезный способ показать именно то, что вы пытаетесь сделать. –

+0

Вы хотите сказать, что просто хотите проанализировать входящие ответы фиксированной длины на объекты (с атрибутами), которые можно преобразовать обратно в байты? – TisteAndii

+0

У меня был какой-то код, но он был ужасен и на самом деле не работал должным образом. Кроме того, это не соответствовало моему желанию сделать его модульным. Ответ ниже TisteAndii на самом деле лучше, чем у меня. – galmok

ответ

1

Я не совсем понимаю, что вы хотите сделать, но если вы хотите получить ответы фиксированной длиной от каких-либо устройств и сделать их атрибуты каким-либо объекта, что-то вроде этого будет хорошо ?:

START_FRAME = 0xAA 
END_FRAME = 0xBB 
TIMEOUT = 2 

class Response: 
def __init__(self, response): 
    if (len(response) - 6) % 11 == 0 and response[0] == START_FRAME and response[-1] == END_FRAME: # verify that its a valid response 
     self.header = {} # build header 
     self.header['id'] = response[1] 
     self.header['length'] = response[2] 
     self.header['status'] = response[3] 
     self.r_checksum = response[-2] # get checksum from response 
     self.checksum = self.header['id']^self.header['length']^self.header['status'] # verify the checksum 
     self.payload = response[4:-2] # get raw payload slice 
     self.decode(self.payload) # parse payload 
     if self.r_checksum == self.checksum: # final check 
      self.parsed = True 
     else: 
      self.parsed = False 
    else: # if response didnt follow the pattern 
     self.parsed = False 
def decode(self, packet): # decode payload 
    self.frm_count = packet[0] # get number of data frames 
    self.checksum ^= self.frm_count 
    self.decoded = [] # hold decoded payload 
    frames = packet[1:] 
    for c in range(self.frm_count): # iterate through data frames 
     flags = frames[(c*10):(c*10 + 2)] 
     for f in flags: 
      self.checksum ^= f 
     data = frames[(c*10 + 2):(c+1)*10] 
     for d in data: 
      self.checksum ^= d 
     self.decoded.append({'frame': c+1, 'flags': flags, 'data':data}) 
def serialize(): # reconstruct response 
    res = bytearray() 
    res.append(START_FRAME) 
    res.extend([self.header['id'], self.header['length'], self.header['status']]) 
    res.extend(self.payload) 
    res.extend([self.checksum, END_FRAME]) 
    return res 

response = bytearray() 
ser = serial.Serial('COM3', 9600) # timeout is 2 seconds 
last_read = time.clock() 
while time.clock() - last_read < TIMEOUT: 
    while ser.inWaiting() > 0: 
     response.append(ser.read()) 
     last_read = time.clock() 
decoded_res = Response(response) 
if decoded_res.parsed: 
    # do stuff 
else: 
    print('Invalid response!') 

Этот код предполагает, что может быть более одного кадра данных, причем кадрам данных, которым предшествует байт, указывает количество кадров данных. Анализ пакета по сравнению с временем, полученным для последовательных коммуникаций (даже при 115200 бодах). Я думаю, что все это примерно O (n).

+0

Вы предпочтителен правильно для показанного ответа. Я получаю различные ответы, которые нужно декодировать по-своему, в зависимости от отправленной команды.Ваш код можно расширить, чтобы учесть это, добавив дополнительные методы декодирования (по одному для каждой команды). Я думаю, что ваше предложение для меня достаточно хорошо, хотя для класса требуется полный кадр, чтобы начать декодирование. Я надеялся на более ориентированный на поток подход. Использование тайм-аутов для разбиения байтового потока на пакеты может оказаться ненадежным (и медленным). Надеюсь, это не проблема. Большое спасибо! – galmok

+0

Возможно, вы могли бы расширить то, что вы подразумеваете под потоком? Не нужно, чтобы кадр был полным до того, как вы это почувствуете, поскольку кадр может оказаться неполным/недействительным? Вы могли бы сократить период ожидания (если ваша скорость в бодах достаточно высока) и даже избавиться от time.sleep() вместо использования time.clock() или так, чтобы измерить интервалы, а не тратить секунды, ничего не делая. Позвольте мне отредактировать код – TisteAndii

+0

Мое беспокойство в основном происходит, если ответы возвращаются, т. Е. Пакеты не могут быть разделены, если посмотреть только на тайм-аут (так как их не будет). Но я думаю, что могу дать небольшую задержку после каждого ответа перед отправкой нового запроса на устройство. Мне нужно как можно быстрее отправить столько запросов (и получить как можно больше ответов). Время ожидания 1-2 символов должно быть доступно. По ориентированным на поток потокам я думаю о способе отправки бесконечных байтов потока в класс и вызывать его обратный вызов, когда он имеет полный пакет. Дополнительные байты приведут к более обратным вызовам. Ваш код является хорошей отправной точкой. – galmok