Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Python message class generation #169

Open
wants to merge 52 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
52 commits
Select commit Hold shift + click to select a range
1c936ae
Added class for message fields
Feb 20, 2023
57ecb97
Adapted PprzMessage methods to underlying
Feb 20, 2023
1673536
Added uninstall and reinstall rules
Feb 20, 2023
ee0f047
Fixed the unistall command
Feb 20, 2023
87d5437
Added access to PprzMessageField underlying object
spacewolfXfr Feb 20, 2023
13af13b
Put conversion constants as classwide constants
spacewolfXfr Feb 20, 2023
94d30d6
Added more typing
spacewolfXfr Feb 20, 2023
ac73fbb
Added support for Python statically generated interface to messages
spacewolfXfr Feb 20, 2023
0005dd9
Added messages' and fields' description to generated files
spacewolfXfr Feb 21, 2023
b4c04d7
Added the Python lib and the generated messages when building 'pymess…
spacewolfXfr Feb 21, 2023
df0be00
Added better type and Enum management in generic PprzMessageField
spacewolfXfr Feb 21, 2023
228de28
Added support for statically generated field classes
spacewolfXfr Feb 21, 2023
93a9a39
Put one file per message instead of one file per message class
spacewolfXfr Feb 22, 2023
5b12422
Gather info about illegal characters in 'values' field enum
spacewolfXfr Feb 22, 2023
6d44034
Removed unused variable
Feb 23, 2023
05b3d56
Enforce constants in child classes
Feb 23, 2023
9ad7bdb
Added fields in parser to manage broadcast messages
Feb 23, 2023
95c3cf1
Added post-install for Python generated messages
Feb 23, 2023
0ac425c
Fixed subclasses __init__
spacewolfXfr Feb 23, 2023
4e29c61
Removed __getattr__ redefinition as it is self non-terminating recursive
spacewolfXfr Feb 23, 2023
fc9740c
Adapted 'ivy' module to use statically defined messages if generated
spacewolfXfr Feb 23, 2023
3d521d7
Adjusted Makefile for installation of Python Pprzlink library
spacewolfXfr Feb 23, 2023
f75fe5a
Reverted, because now it works
spacewolfXfr Feb 23, 2023
9054cc4
Assert proper type when changing field value
Mar 1, 2023
dcac23a
Provide value instead of field instance
Mar 1, 2023
6ec8a83
Explicit call to Python
Mar 1, 2023
aea4bfa
Simplification using dictionnary structure
Mar 1, 2023
ab3fffc
Added typing and parsing capabilities
Mar 1, 2023
c90df87
Added install for all message classes
Mar 17, 2023
7e2a508
Install to user site instead
Mar 17, 2023
8c4ad25
Proper string management
Mar 17, 2023
968b5a3
Added last resort check for `messages.xml`
Mar 17, 2023
730c324
Denote raw C-type for messages in generated
Mar 17, 2023
b444878
Added name check (alphanumeric and '+-_')
Mar 17, 2023
2705ed0
Allow python interpreter selection
Mar 17, 2023
4b06231
Typo fix (use concat instead of set notation)
Mar 17, 2023
1577867
Added building message directly from dict
May 12, 2023
4e40444
Pushed the GVF_PARAMETRIC messages
May 12, 2023
ec5182d
Added GVF_PARAMETRIC related messages
May 17, 2023
2e65711
Merge branch 'Python_message_class_generation' of https://github.com/…
May 17, 2023
870cdef
Adjusted GVF messages for easier logging
Jul 25, 2023
a22a166
Messages for internal K gains GVF
Aug 1, 2023
b46b8e7
GVF messaging update
Aug 11, 2023
f7b5dc9
Merge remote-tracking branch 'refs/remotes/origin/Python_message_clas…
Aug 11, 2023
3a756f7
Merge pull request #1 from paparazzi/master
spacewolfXfr Oct 30, 2023
d187b25
Fixed missing import in ivy.py
Nov 16, 2023
0d14ade
Fixed missing import
Nov 23, 2023
e551fbe
My custom messages
Jun 26, 2024
6d43b83
Revert "My custom messages"
Jun 26, 2024
9e12e82
Use a class for message fields, and expose unit, values enum, alt_uni…
Jun 26, 2024
ce8ca1c
Merged with added exposed fields
Jun 26, 2024
831f882
Fixed duplicate imports
Jun 26, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 37 additions & 7 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
Q ?=@

MAKE = make
PY ?= python3

PPRZLINK_MSG_VERSION ?= 1.0
PPRZLINK_LIB_VERSION ?= 2.0
Expand All @@ -37,6 +38,11 @@ UNITS_XML ?= message_definitions/common/units.xml
MESSAGES_INSTALL ?= $(PREFIX)/var
MESSAGES_INCLUDE ?= $(MESSAGES_INSTALL)/include/pprzlink
MESSAGES_LIB ?= $(MESSAGES_INSTALL)/share/pprzlink/src
LIB_PYTHON ?= $(MESSAGES_INSTALL)/python
LIB_PYTHON_PPRZ ?= $(LIB_PYTHON)/pprzlink
MESSAGES_PYTHON ?= $(LIB_PYTHON_PPRZ)/generated
ALERT_PYTHON ?= $(MESSAGES_PYTHON)/alert
GROUND_PYTHON ?= $(MESSAGES_PYTHON)/ground
TELEMETRY_H ?= $(MESSAGES_INCLUDE)/messages.h
DATALINK_H ?= $(MESSAGES_INCLUDE)/dl_protocol.h
INTERMCU_H ?= $(MESSAGES_INCLUDE)/intermcu_msg.h
Expand Down Expand Up @@ -71,19 +77,43 @@ libpprzlink-install:
pre_messages_dir:
$(Q)test -d $(MESSAGES_INCLUDE) || mkdir -p $(MESSAGES_INCLUDE)
$(Q)test -d $(MESSAGES_LIB) || mkdir -p $(MESSAGES_LIB)
$(Q)test -d $(MESSAGE_PYTHON) || mkdir -p $(MESSAGE_PYTHON)

post_messages_install:
@echo 'Copy extra lib files'
$(Q)cp $(MESSAGES_XML) $(MESSAGES_DTD) $(UNITS_XML) $(MESSAGES_INSTALL)
$(Q)Q=$(Q) MESSAGES_INCLUDE=$(MESSAGES_INCLUDE) MESSAGES_LIB=$(MESSAGES_LIB) $(MAKE) -C lib/v$(PPRZLINK_LIB_VERSION)/C install

post_messages_python_install:
@echo 'Copy extra Python lib files'
$(Q)cp -a lib/v$(PPRZLINK_LIB_VERSION)/python/. $(LIB_PYTHON)
$(shell echo "__all__ = ['telemetry','datalink','intermcu','alert','ground']" >> $(MESSAGES_PYTHON)/__init__.py)

pygen_messages: pre_messages_dir
@echo 'Generate C messages (Python) at location $(MESSAGES_INCLUDE)'
$(Q)./tools/generator/gen_messages.py $(VALIDATE_FLAG) --protocol $(PPRZLINK_LIB_VERSION) --messages $(PPRZLINK_MSG_VERSION) --lang C -o $(TELEMETRY_H) $(MESSAGES_XML) telemetry
$(Q)./tools/generator/gen_messages.py $(VALIDATE_FLAG) --protocol $(PPRZLINK_LIB_VERSION) --messages $(PPRZLINK_MSG_VERSION) --lang C -o $(DATALINK_H) $(MESSAGES_XML) datalink
$(Q)./tools/generator/gen_messages.py $(VALIDATE_FLAG) --protocol $(PPRZLINK_LIB_VERSION) --messages $(PPRZLINK_MSG_VERSION) --lang C -o $(INTERMCU_H) $(MESSAGES_XML) intermcu
$(Q) $(PY) ./tools/generator/gen_messages.py $(VALIDATE_FLAG) --protocol $(PPRZLINK_LIB_VERSION) --messages $(PPRZLINK_MSG_VERSION) --lang C -o $(TELEMETRY_H) $(MESSAGES_XML) telemetry
$(Q) $(PY) ./tools/generator/gen_messages.py $(VALIDATE_FLAG) --protocol $(PPRZLINK_LIB_VERSION) --messages $(PPRZLINK_MSG_VERSION) --lang C -o $(DATALINK_H) $(MESSAGES_XML) datalink
$(Q) $(PY) ./tools/generator/gen_messages.py $(VALIDATE_FLAG) --protocol $(PPRZLINK_LIB_VERSION) --messages $(PPRZLINK_MSG_VERSION) --lang C -o $(INTERMCU_H) $(MESSAGES_XML) intermcu

pygen_python_messages: pre_messages_dir
@echo 'Generate Python at location $(MESSAGE_PYTHON)'
$(Q) $(PY) ./tools/generator/gen_messages.py $(VALIDATE_FLAG) --protocol $(PPRZLINK_LIB_VERSION) --messages $(PPRZLINK_MSG_VERSION) --lang Python -o $(MESSAGES_PYTHON) $(MESSAGES_XML) telemetry
$(Q) $(PY) ./tools/generator/gen_messages.py $(VALIDATE_FLAG) --protocol $(PPRZLINK_LIB_VERSION) --messages $(PPRZLINK_MSG_VERSION) --lang Python -o $(MESSAGES_PYTHON) $(MESSAGES_XML) datalink
$(Q) $(PY) ./tools/generator/gen_messages.py $(VALIDATE_FLAG) --protocol $(PPRZLINK_LIB_VERSION) --messages $(PPRZLINK_MSG_VERSION) --lang Python -o $(MESSAGES_PYTHON) $(MESSAGES_XML) intermcu
$(Q) $(PY) ./tools/generator/gen_messages.py $(VALIDATE_FLAG) --protocol $(PPRZLINK_LIB_VERSION) --messages $(PPRZLINK_MSG_VERSION) --lang Python -o $(MESSAGES_PYTHON) $(MESSAGES_XML) alert
$(Q) $(PY) ./tools/generator/gen_messages.py $(VALIDATE_FLAG) --protocol $(PPRZLINK_LIB_VERSION) --messages $(PPRZLINK_MSG_VERSION) --lang Python -o $(MESSAGES_PYTHON) $(MESSAGES_XML) ground


pymessages: pygen_messages pygen_python_messages post_messages_install post_messages_python_install

libpprzlink-pygen-python-install: pygen_python_messages post_messages_python_install
$(Q)cp $(MESSAGES_XML) $(LIB_PYTHON_PPRZ)
ifdef $(DESTDIR)
$(Q)Q=$(Q) DESTDIR=$(DESTDIR)/python $(MAKE) -C $(LIB_PYTHON) install
else
$(Q)Q=$(Q) $(MAKE) -C $(LIB_PYTHON) install
endif

pymessages: pygen_messages post_messages_install

clean :
$(Q)$(MAKE) -C tools/generator clean
Expand All @@ -93,8 +123,8 @@ uninstall: clean
$(Q)rm -rf var bin build $(MESSAGES_INCLUDE) $(MESSAGES_LIB)

validate_messages:
$(Q)./tools/generator/gen_messages.py --only-validate $(MESSAGES_XML) telemetry
$(Q)./tools/generator/gen_messages.py --only-validate $(MESSAGES_XML) datalink
$(Q)./tools/generator/gen_messages.py --only-validate $(MESSAGES_XML) intermcu
$(Q) $(PY) ./tools/generator/gen_messages.py --only-validate $(MESSAGES_XML) telemetry
$(Q) $(PY) ./tools/generator/gen_messages.py --only-validate $(MESSAGES_XML) datalink
$(Q) $(PY) ./tools/generator/gen_messages.py --only-validate $(MESSAGES_XML) intermcu

.PHONY: libpprzlink++-install libpprzlink++ libpprzlink libpprzlink-install pre_messages_dir post_messages_install pygen_messages pymessages clean uninstall validate_messages
12 changes: 11 additions & 1 deletion lib/v2.0/python/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,10 @@
# <http://www.gnu.org/licenses/>.
#

PY ?= python3

MAKE = make
DESTDIR ?=$(shell $(PY) -m site --user-site)

all:

Expand All @@ -29,5 +32,12 @@ install:
$(Q)test -d $(DESTDIR) || mkdir -p $(DESTDIR)
$(Q)cp -r pprzlink $(DESTDIR)

.PHONY: all install
uninstall:
$(Q)rm -rf $(DESTDIR)/pprzlink

reinstall:
$(Q)make uninstall
$(Q)make install

.PHONY: all install uninstall reinstall

72 changes: 56 additions & 16 deletions lib/v2.0/python/pprzlink/ivy.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,38 @@
from ivy.std_api import *
from ivy.ivy import IvyIllegalStateError
import logging
import os
import os,sys
import re
import platform
import typing
import string
import sys

from pprzlink.message import PprzMessage
from pprzlink import messages_xml_map
from pprzlink.request_uid import RequestUIDFactory

try:
import pprzlink.generated
from pprzlink.generated import *
import inspect

STATIC_MSG_CLASSES = True

def pprzmessage_subclass_perdicate(o) -> bool:
__pattern = "PprzMessage_"
return inspect.isclass(o) and o.__name__[:len(__pattern)] == __pattern

msg_str_to_class:typing.Dict[str,typing.Type] = dict()
for submodule_name in pprzlink.generated.__all__:
for name, obj in inspect.getmembers(sys.modules[pprzlink.generated.__name__ + "." + submodule_name],pprzmessage_subclass_perdicate):
msg_str_to_class[name.split('_',maxsplit=1)[1]] = obj

except ImportError:
from pprzlink import messages_xml_map
STATIC_MSG_CLASSES = False
msg_str_to_class = None



if os.getenv('IVY_BUS') is not None:
IVY_BUS = os.getenv('IVY_BUS')
Expand All @@ -43,17 +67,25 @@ class IvyMessagesInterface(object):
"""
This class is the interface between the paparazzi messages and the Ivy bus.
"""
def __init__(self, agent_name=None, start_ivy=True, verbose=False, ivy_bus=IVY_BUS):
def __init__(self, agent_name:typing.Optional[str]=None, start_ivy:bool=True, verbose:bool=False, ivy_bus=IVY_BUS):
if agent_name is None:
agent_name = "IvyMessagesInterface %i" % os.getpid()
else:

for c in agent_name:
try:
assert c in string.ascii_letters + string.digits + "+-_"
except AssertionError:
raise ValueError(f"Invalid name: {agent_name}\n(Contains {c} which is neither a letter, a digit or any of '+','-','_')")
self.agent_name = agent_name
self.verbose = verbose
self._ivy_bus = ivy_bus
self._running = False

# make sure all messages are parsed before we start creating them in callbacks
# the message parsing should really be redone...
messages_xml_map.parse_messages()
if not STATIC_MSG_CLASSES:
# make sure all messages are parsed before we start creating them in callbacks
# the message parsing should really be redone...
messages_xml_map.parse_messages()

# bindings with associated callback functions
self.bindings = {}
Expand Down Expand Up @@ -166,7 +198,7 @@ def unsubscribe(self, bind_id):
self.unbind(bind_id)

@staticmethod
def parse_pprz_msg(ivy_msg):
def parse_pprz_msg(ivy_msg:str):
"""
Parse an Ivy message into a PprzMessage.

Expand Down Expand Up @@ -200,13 +232,22 @@ def parse_pprz_msg(ivy_msg):
payload = data.group(3)
request_id = None
# check which message class it is
try:
msg_class, msg_name = messages_xml_map.find_msg_by_name(msg_name)
except ValueError:
logger.error("Ignoring unknown message " + ivy_msg)
return
if not STATIC_MSG_CLASSES:
try:
msg_class, msg_name = messages_xml_map.find_msg_by_name(msg_name)
except ValueError:
logger.error("Ignoring unknown message " + ivy_msg)
return
msg = PprzMessage(msg_class, msg_name)
else:
try:
msg:PprzMessage = msg_str_to_class[msg_name.upper()]()
msg_class = msg.msg_class
except KeyError:
logger.error("Ignoring unknown message " + ivy_msg)
return

msg = PprzMessage(msg_class, msg_name)

msg.ivy_string_to_payload(payload)
# pass non-telemetry messages with ac_id 0 or ac_id attrib value
if msg_class == "telemetry":
Expand All @@ -220,14 +261,13 @@ def parse_pprz_msg(ivy_msg):
return None
else:
if 'ac_id' in msg.fieldnames:
ac_id_idx = msg.fieldnames.index('ac_id')
ac_id = msg.fieldvalues[ac_id_idx]
ac_id = msg['ac_id']
else:
ac_id = 0
# finally call the callback, passing the aircraft id, request id (might be None) and parsed message
return ac_id, request_id, msg

def send_raw_datalink(self, msg):
def send_raw_datalink(self, msg:PprzMessage):
"""
Send a PprzMessage of datalink msg_class embedded in RAW_DATALINK message

Expand Down
Loading