diff --git a/sw/ground_segment/python/messages_app/messagesframe.py b/sw/ground_segment/python/messages_app/messagesframe.py index d36fe44a7c..d51b26c02e 100644 --- a/sw/ground_segment/python/messages_app/messagesframe.py +++ b/sw/ground_segment/python/messages_app/messagesframe.py @@ -14,7 +14,7 @@ sys.path.append(PPRZ_SRC + "/sw/lib/python") PPRZ_HOME = getenv("PAPARAZZI_HOME", PPRZ_SRC) from ivy_msg_interface import IvyMessagesInterface -import messages_xml_map +from pprz_msg.message import PprzMessage WIDTH = 450 LABEL_WIDTH = 166 @@ -23,18 +23,16 @@ HEIGHT = 800 BORDER = 1 -class Message: +class Message(PprzMessage): def __init__(self, class_name, name): - messages_xml_map.parse_messages() - self.field_value = [] - self.field_names = messages_xml_map.message_dictionary[class_name][name] + super(Message, self).__init__(class_name, name) self.field_controls = [] self.index = None self.last_seen = time.clock() self.name = name -class Aircraft: +class Aircraft(object): def __init__(self, ac_id): self.ac_id = ac_id self.messages = {} @@ -42,7 +40,7 @@ class Aircraft: class MessagesFrame(wx.Frame): - def message_recv(self, msg_class, msg_name, ac_id, values): + def message_recv(self, msg_class, msg_name, ac_id, msg): """Handle incoming messages Callback function for IvyMessagesInterface @@ -53,8 +51,8 @@ class MessagesFrame(wx.Frame): :type msg_name: str :param ac_id: aircraft id :type ac_id: int - :param values: message values - :type values: list + :param msg: message + :type msg: PprzMessage """ # only show messages of the requested class if msg_class != self.msg_class: @@ -62,7 +60,7 @@ class MessagesFrame(wx.Frame): if ac_id in self.aircrafts and msg_name in self.aircrafts[ac_id].messages: if time.time() - self.aircrafts[ac_id].messages[msg_name].last_seen < 0.2: return - wx.CallAfter(self.gui_update, msg_class, msg_name, ac_id, values) + wx.CallAfter(self.gui_update, msg_class, msg_name, ac_id, msg) def find_page(self, book, name): if book.GetPageCount() < 1: @@ -119,7 +117,7 @@ class MessagesFrame(wx.Frame): messages_book = aircraft.messages_book aircraft.messages[name] = Message(msg_class, name) field_panel = wx.Panel(messages_book) - grid_sizer = wx.FlexGridSizer(len(aircraft.messages[name].field_names), 2) + grid_sizer = wx.FlexGridSizer(len(aircraft.messages[name].get_fieldnames()), 2) index = self.find_page(messages_book, name) messages_book.InsertPage(index, field_panel, name, imageId=1) @@ -129,7 +127,7 @@ class MessagesFrame(wx.Frame): for message_name in aircraft.messages: aircraft.messages[message_name].index = self.find_page(messages_book, message_name) - for field_name in aircraft.messages[name].field_names: + for field_name in aircraft.messages[name].get_fieldnames(): name_text = wx.StaticText(field_panel, -1, field_name) size = name_text.GetSize() size.x = LABEL_WIDTH @@ -147,7 +145,7 @@ class MessagesFrame(wx.Frame): field_panel.SetSizer(grid_sizer) field_panel.Layout() - def gui_update(self, msg_class, msg_name, ac_id, values): + def gui_update(self, msg_class, msg_name, ac_id, msg): if ac_id not in self.aircrafts: self.add_new_aircraft(ac_id) @@ -159,8 +157,8 @@ class MessagesFrame(wx.Frame): aircraft.messages_book.SetPageImage(aircraft.messages[msg_name].index, 1) self.aircrafts[ac_id].messages[msg_name].last_seen = time.time() - for index in range(0, len(values)): - aircraft.messages[msg_name].field_controls[index].SetLabel(values[index]) + for index in range(0, len(msg.get_fieldvalues())): + aircraft.messages[msg_name].field_controls[index].SetLabel(msg.get_field(index)) def __init__(self, msg_class="telemetry"): wx.Frame.__init__(self, id=-1, parent=None, name=u'MessagesFrame', size=wx.Size(WIDTH, HEIGHT), style=wx.DEFAULT_FRAME_STYLE, title=u'Messages') diff --git a/sw/lib/python/ivy_msg_interface.py b/sw/lib/python/ivy_msg_interface.py index 62130146ae..f44a1c606f 100644 --- a/sw/lib/python/ivy_msg_interface.py +++ b/sw/lib/python/ivy_msg_interface.py @@ -6,9 +6,17 @@ import os import sys import re +# if PAPARAZZI_SRC not set, then assume the tree containing this +# file is a reasonable substitute +PPRZ_SRC = os.getenv("PAPARAZZI_SRC", os.path.normpath(os.path.join(os.path.dirname(os.path.abspath(__file__)), + '../../../../'))) +sys.path.append(PPRZ_SRC + "/sw/lib/python") -class IvyMessagesInterface(): - def __init__(self, callback, init=True, verbose=True, bind_regex="(.*)"): +from pprz_msg.message import PprzMessage + + +class IvyMessagesInterface(object): + def __init__(self, callback, init=True, verbose=False, bind_regex='(.*)'): self.callback = callback self.ivy_id = 0 self.verbose = verbose @@ -27,7 +35,7 @@ class IvyMessagesInterface(): except: pass - def init_ivy(self, init, bind_regex): + def init_ivy(self, init=True, bind_regex='(.*)'): if init: IvyInit("Messages %i" % os.getpid(), "READY", 0, lambda x,y: y, lambda x,y: y) logging.getLogger('Ivy').setLevel(logging.WARN) @@ -56,13 +64,16 @@ class IvyMessagesInterface(): # check which message class it is # pass non-telemetry messages with ac_id 0 - if data[0] in ["ground", "ground_dl", "dl"]: + if data[0] in ["sim", "ground_dl", "dl"]: + if self.verbose: + print("ignoring message " + ' '.join(data)) + sys.stdout.flush() + return + elif data[0] in ["ground"]: msg_class = data[0] msg_name = data[1] ac_id = 0 values = list(filter(None, data[2:])) - elif data[0] == "sim": - return else: try: ac_id = int(data[0]) @@ -74,4 +85,6 @@ class IvyMessagesInterface(): msg_class = "telemetry" msg_name = data[1] values = list(filter(None, data[2:])) - self.callback(msg_class, msg_name, ac_id, values) + msg = PprzMessage(msg_class, msg_name) + msg.set_values(values) + self.callback(msg_class, msg_name, ac_id, msg) diff --git a/sw/lib/python/pprz_msg/__init__.py b/sw/lib/python/pprz_msg/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/sw/lib/python/pprz_msg/message.py b/sw/lib/python/pprz_msg/message.py new file mode 100644 index 0000000000..202ed6955f --- /dev/null +++ b/sw/lib/python/pprz_msg/message.py @@ -0,0 +1,62 @@ +""" +Paparazzi message representation + +""" + +from __future__ import print_function +import sys +import json +import messages_xml_map + + +class PprzMessageError(Exception): + def __init__(self, message, inner_exception=None): + self.message = message + self.inner_exception = inner_exception + self.exception_info = sys.exc_info() + def __str__(self): + return self.message + + +class PprzMessage(object): + """base Paparazzi message class""" + def __init__(self, class_name, name): + self._class_name = class_name + self._name = name + self._id = messages_xml_map.get_msg_id(class_name, name) + self._fieldnames = messages_xml_map.get_msg_fields(class_name, name) + self._fieldvalues = [] + + def get_fieldnames(self): + return self._fieldnames + + def get_fieldvalues(self): + return self._fieldvalues + + def get_field(self, idx): + return self._fieldvalues[idx] + + def set_values(self, values): + if len(values) == len(self._fieldnames): + self._fieldvalues = values + else: + raise PprzMessageError("Error: fields not matching") + + def __str__(self): + ret = '%s.%s {' % (self._class_name, self._name) + for idx, f in enumerate(self._fieldnames): + ret += '%s : %s, ' % (f, self._fieldvalues[idx]) + ret = ret[0:-2] + '}' + return ret + + def to_dict(self, payload_only=False): + d = {} + if not payload_only: + d['msgname'] = self._name + d['msgclass'] = self._class_name + for idx, f in enumerate(self._fieldnames): + d[f] = self._fieldvalues[idx] + return d + + def to_json(self, payload_only=False): + return json.dumps(self.to_dict(payload_only)) diff --git a/sw/lib/python/messages_xml_map.py b/sw/lib/python/pprz_msg/messages_xml_map.py similarity index 80% rename from sw/lib/python/messages_xml_map.py rename to sw/lib/python/pprz_msg/messages_xml_map.py index c24d7c5cc5..1a8cdb1294 100755 --- a/sw/lib/python/messages_xml_map.py +++ b/sw/lib/python/pprz_msg/messages_xml_map.py @@ -7,7 +7,7 @@ import os # if PAPARAZZI_HOME not set, then assume the tree containing this # file is a reasonable substitute PPRZ_HOME = os.getenv("PAPARAZZI_HOME", os.path.normpath(os.path.join(os.path.dirname(os.path.abspath(__file__)), - '../../../'))) + '../../../..'))) default_messages_file = '%s/conf/messages.xml' % PPRZ_HOME @@ -61,6 +61,30 @@ def parse_messages(messages_file=default_messages_file): message_dictionary_types[class_name][message_id].append(the_field.attrib['type']) +def get_msg_fields(msg_class, msg_name): + if not message_dictionary: + parse_messages() + if msg_class in message_dictionary: + if msg_name in message_dictionary[msg_class]: + return message_dictionary[msg_class][msg_name] + else: + print("Error: msg_name %s not found in msg_class %s." % (msg_name, msg_class)) + else: + print("Error: msg_class %s not found." % msg_class) + return [] + + +def get_msg_id(msg_class, msg_name): + if not message_dictionary: + parse_messages() + try: + return message_dictionary_name_id[msg_class][msg_name] + except KeyError: + print("Error: msg_name %s not found in msg_class %s." % (msg_name, msg_class)) + return 0 + + + def test(): import argparse parser = argparse.ArgumentParser()