diff --git a/scxml_converter/src/scxml_converter/scxml_entries/scxml_executable_entries.py b/scxml_converter/src/scxml_converter/scxml_entries/scxml_executable_entries.py index 16345ebf..50202649 100644 --- a/scxml_converter/src/scxml_converter/scxml_entries/scxml_executable_entries.py +++ b/scxml_converter/src/scxml_converter/scxml_entries/scxml_executable_entries.py @@ -122,6 +122,18 @@ def __init__(self, event: str, params: Optional[List[ScxmlParam]] = None): def get_tag_name() -> str: return "send" + def from_xml_tree(xml_tree: ET.Element) -> "ScxmlSend": + """Create a ScxmlSend object from an XML tree.""" + assert xml_tree.tag == ScxmlSend.get_tag_name(), \ + f"Error: SCXML send: XML tag name is not {ScxmlSend.get_tag_name()}." + event = xml_tree.attrib["event"] + params = [] + for param_xml in xml_tree: + params.append(ScxmlParam.from_xml_tree(param_xml)) + if len(params) == 0: + params = None + return ScxmlSend(event, params) + def check_validity(self) -> bool: valid_event = isinstance(self._event, str) and len(self._event) > 0 valid_params = True @@ -160,6 +172,16 @@ def __init__(self, name: str, expr: str): def get_tag_name() -> str: return "assign" + def from_xml_tree(xml_tree: ET.Element) -> "ScxmlAssign": + """Create a ScxmlAssign object from an XML tree.""" + assert xml_tree.tag == ScxmlAssign.get_tag_name(), \ + f"Error: SCXML assign: XML tag name is not {ScxmlAssign.get_tag_name()}." + name = xml_tree.attrib.get("location") + assert name is not None and len(name) > 0, "Error: SCXML assign: name is not valid." + expr = xml_tree.attrib.get("expr") + assert expr is not None and len(expr) > 0, "Error: SCXML assign: expr is not valid." + return ScxmlAssign(name, expr) + def check_validity(self) -> bool: # TODO: Check that the location to assign exists in the data-model valid_name = isinstance(self.name, str) and len(self.name) > 0 diff --git a/scxml_converter/src/scxml_converter/scxml_entries/scxml_param.py b/scxml_converter/src/scxml_converter/scxml_entries/scxml_param.py index ece3a45e..ceb734e0 100644 --- a/scxml_converter/src/scxml_converter/scxml_entries/scxml_param.py +++ b/scxml_converter/src/scxml_converter/scxml_entries/scxml_param.py @@ -24,17 +24,36 @@ class ScxmlParam: """This class represents a single parameter.""" + def __init__(self, name: str, *, expr: Optional[str] = None, location: Optional[str] = None): self._name = name self._expr = expr self._location = location + def get_tag_name() -> str: + return "param" + + def from_xml_tree(xml_tree: ET.Element) -> "ScxmlParam": + """Create a ScxmlParam object from an XML tree.""" + assert xml_tree.tag == ScxmlParam.get_tag_name(), \ + f"Error: SCXML param: XML tag name is not {ScxmlParam.get_tag_name()}." + name = xml_tree.attrib.get("name") + assert name is not None and len(name) > 0, "Error: SCXML param: name is not valid." + expr = xml_tree.attrib.get("expr") + location = xml_tree.attrib.get("location") + assert not (expr is not None and location is not None), \ + "Error: SCXML param: expr and location are both set." + assert expr is not None or location is not None, \ + "Error: SCXML param: expr and location are both unset." + return ScxmlParam(name, expr=expr, location=location) + def check_validity(self) -> bool: valid_name = len(self._name) > 0 if not valid_name: print("Error: SCXML param: name is not valid") valid_expr = isinstance(self._expr, str) and len(self._expr) > 0 and self._location is None - valid_location = isinstance(self._location, str) and len(self._location) > 0 and self._expr is None + valid_location = isinstance(self._location, str) and len( + self._location) > 0 and self._expr is None # Print possible errors if self._expr is not None: if not isinstance(self._expr, str) or len(self._expr) == 0: @@ -51,7 +70,7 @@ def check_validity(self) -> bool: def as_xml(self) -> ET.Element: assert self.check_validity(), "SCXML: found invalid param." - xml_param = ET.Element('param', {"name": self._name}) + xml_param = ET.Element(ScxmlParam.get_tag_name(), {"name": self._name}) if self._expr is not None: xml_param.set("expr", self._expr) if self._location is not None: diff --git a/scxml_converter/src/scxml_converter/scxml_entries/scxml_ros_entries.py b/scxml_converter/src/scxml_converter/scxml_entries/scxml_ros_entries.py index 881bb51d..cd1a48f8 100644 --- a/scxml_converter/src/scxml_converter/scxml_entries/scxml_ros_entries.py +++ b/scxml_converter/src/scxml_converter/scxml_entries/scxml_ros_entries.py @@ -180,6 +180,12 @@ def __init__(self, timer: RosTimeRate, target: str, body: Optional[ScxmlExecutio self._body = body assert self.check_validity(), "Error: SCXML rate callback: invalid parameters." + def get_tag_name() -> str: + return "ros_rate_callback" + + def from_xml_tree(xml_tree: ET.Element) -> ScxmlTransition: + raise NotImplementedError("Not implemented yet.") + def check_validity(self) -> bool: valid_timer = isinstance(self._timer_name, str) and len(self._timer_name) > 0 valid_target = isinstance(self._target, str) and len(self._target) > 0 @@ -213,6 +219,12 @@ def __init__( self._body = body assert self.check_validity(), "Error: SCXML topic callback: invalid parameters." + def get_tag_name() -> str: + return "ros_topic_callback" + + def from_xml_tree(xml_tree: ET.Element) -> ScxmlTransition: + raise NotImplementedError("Not implemented yet.") + def check_validity(self) -> bool: valid_topic = isinstance(self._topic, str) and len(self._topic) > 0 valid_target = isinstance(self._target, str) and len(self._target) > 0 @@ -243,6 +255,19 @@ def __init__(self, name: str, expr: str): self._expr = expr assert self.check_validity(), "Error: SCXML topic publish field: invalid parameters." + def get_tag_name() -> str: + return "field" + + def from_xml_tree(xml_tree: ET.Element) -> "RosField": + """Create a RosField object from an XML tree.""" + assert xml_tree.tag == RosField.get_tag_name(), \ + f"Error: SCXML topic publish field: XML tag name is not {RosField.get_tag_name()}" + name = xml_tree.attrib.get("name") + expr = xml_tree.attrib.get("expr") + assert name is not None and expr is not None, \ + "Error: SCXML topic publish field: 'name' or 'expr' attribute not found in input xml." + return RosField(name, expr) + def check_validity(self) -> bool: valid_name = isinstance(self._name, str) and len(self._name) > 0 valid_expr = isinstance(self._expr, str) and len(self._expr) > 0 @@ -254,21 +279,41 @@ def check_validity(self) -> bool: def as_xml(self) -> ET.Element: assert self.check_validity(), "Error: SCXML topic publish field: invalid parameters." - xml_field = ET.Element("field", {"name": self._name, "expr": self._expr}) + xml_field = ET.Element(RosField.get_tag_name(), {"name": self._name, "expr": self._expr}) return xml_field class RosTopicPublish(ScxmlSend): """Object representing the shipping of a ROS msg through a topic.""" - def __init__(self, topic: RosTopicPublisher, fields: Optional[List[RosField]] = None): - self._topic = topic.get_topic_name() + def __init__(self, topic: Union[RosTopicPublisher, str], + fields: Optional[List[RosField]] = None): + if isinstance(topic, RosTopicPublisher): + self._topic = topic.get_topic_name() + else: + # Used for generating ROS entries from xml file + assert isinstance(topic, str), "Error: SCXML topic publish: invalid topic type." + self._topic = topic self._fields = fields assert self.check_validity(), "Error: SCXML topic publish: invalid parameters." def get_tag_name() -> str: return "ros_topic_publish" + def from_xml_tree(xml_tree: ET.Element) -> ScxmlSend: + """Create a RosTopicPublish object from an XML tree.""" + assert xml_tree.tag == RosTopicPublish.get_tag_name(), \ + f"Error: SCXML topic publish: XML tag name is not {RosTopicPublish.get_tag_name()}" + topic_name = xml_tree.attrib.get("topic") + assert topic_name is not None, \ + "Error: SCXML topic publish: 'topic' attribute not found in input xml." + fields = [] + for field_xml in xml_tree: + fields.append(RosField.from_xml_tree(field_xml)) + if len(fields) == 0: + fields = None + return RosTopicPublish(topic_name, fields) + def check_validity(self) -> bool: valid_topic = isinstance(self._topic, str) and len(self._topic) > 0 valid_fields = self._fields is None or \