]> sigrok.org Git - libsigrokdecode.git/commitdiff
pjon: show "communication relations", and nits
authorGerhard Sittig <redacted>
Thu, 2 Jul 2020 19:15:00 +0000 (21:15 +0200)
committerGerhard Sittig <redacted>
Thu, 2 Jul 2020 19:15:00 +0000 (21:15 +0200)
Do track the RX and TX information, including their bus IDs. Present bus
numbers as dotted quads. Emit another summary annotation for completed
frames which presents receiver, transmitter, payload, and ACK details at
even higher zoom levels. Rename the last remaining "init CRC" instance
for consistency.

decoders/pjon/pd.py

index ac18e402030b3565887641ffe464f017b110bbfc..fdc9de785efba1e2982d2028a11322173dbb48ae 100644 (file)
 # specification, which can use different link layers.
 
 # TODO
-# - Handle RX and TX identifiers in separate routines, including the
-#   optional bus identifers. Track those addresses, add formatters for
-#   them, and emit "communication relation" details.
-# - Check for correct endianess in variable width fields. The spec says
-#   "network order", that's what this implementation uses.
 # - Check for the correct order of optional fields (the spec is not as
 #   explicit on these details as I'd expect).
 # - Check decoder's robustness, completeness, and correctness when more
@@ -111,7 +106,13 @@ class Decoder(srd.Decoder):
         self.reset_frame()
 
     def reset_frame(self):
+        self.frame_ss = None
+        self.frame_es = None
+        self.frame_rx_id = None
+        self.frame_tx_id = None
+        self.frame_payload_text = None
         self.frame_bytes = None
+        self.frame_has_ack = None
         self.ack_bytes = None
         self.ann_ss = None
         self.ann_es = None
@@ -125,7 +126,22 @@ class Decoder(srd.Decoder):
     def frame_flush(self):
         if not self.frame_bytes:
             return
-        # TODO Emit "communication relation" details here?
+        if not self.frame_ss or not self.frame_es:
+            return
+
+        # Emit "communication relation" details.
+        text = []
+        if self.frame_rx_id is not None:
+            text.append("RX {}".format(self.frame_rx_id[-1]))
+        if self.frame_tx_id is not None:
+            text.append("TX {}".format(self.frame_tx_id[-1]))
+        if self.frame_payload_text is not None:
+            text.append("DATA {}".format(self.frame_payload_text))
+        if self.frame_has_ack is not None:
+            text.append("ACK {:02x}".format(self.frame_has_ack))
+        if text:
+            text = " - ".join(text)
+            self.putg(self.frame_ss, self.frame_es, ANN_RELATION, [text])
 
     def handle_field_get_desc(self, idx = None):
         '''Lookup description of a PJON frame field.'''
@@ -164,10 +180,13 @@ class Decoder(srd.Decoder):
         self.field_desc_idx = 0
         self.field_desc_got = 0
 
+        self.frame_ss = None
+        self.frame_es = None
         self.frame_rx_id = None
         self.frame_is_broadcast = None
         self.frame_tx_id = None
         self.frame_payload = None
+        self.frame_payload_text = None
         self.frame_has_ack = None
 
     def handle_field_rx_id(self, b):
@@ -175,10 +194,6 @@ class Decoder(srd.Decoder):
 
         b = b[0]
 
-        # Track RX info for communication relation emission.
-        self.frame_rx_id = b
-        self.frame_is_broadcast = b == 0
-
         # Provide text presentation, caller emits frame field annotation.
         if b == 255: # "not assigned"
             id_txt = 'NA'
@@ -190,6 +205,11 @@ class Decoder(srd.Decoder):
             'RX_ID {}'.format(id_txt),
             '{}'.format(id_txt),
         ]
+
+        # Track RX info for communication relation emission.
+        self.frame_rx_id = (b, id_txt)
+        self.frame_is_broadcast = b == 0
+
         return texts
 
     def handle_field_config(self, b):
@@ -240,6 +260,7 @@ class Decoder(srd.Decoder):
         u16_fmt = '>H'
         u32_fmt = '>L'
         len_fmt = u16_fmt if self.cfg_len16 else u8_fmt
+        bus_fmt = '>4B'
         crc_fmt = u32_fmt if self.cfg_crc32 else u8_fmt
         self.cfg_overhead = 0
         self.cfg_overhead += struct.calcsize(u8_fmt) # receiver ID
@@ -277,13 +298,13 @@ class Decoder(srd.Decoder):
         # condition checks, and raise awareness for incomplete sequences
         # during maintenance.
         self.handle_field_add_desc(len_fmt, self.handle_field_pkt_len, ANN_PKT_LEN)
-        self.handle_field_add_desc(u8_fmt, self.handle_field_init_crc, ANN_META_CRC)
+        self.handle_field_add_desc(u8_fmt, self.handle_field_meta_crc, ANN_META_CRC)
         if self.cfg_shared:
-            self.handle_field_add_desc(u32_fmt, ['RX_BUS {:08x}', '{:08x}'], ANN_ANON_DATA)
+            self.handle_field_add_desc(bus_fmt, self.handle_field_rx_bus, ANN_ANON_DATA)
         if self.cfg_tx_info:
             if self.cfg_shared:
-                self.handle_field_add_desc(u32_fmt, ['TX_BUS {:08x}', '{:08x}'], ANN_ANON_DATA)
-            self.handle_field_add_desc(u8_fmt, ['TX_ID {:d}', '{:d}'], ANN_ANON_DATA)
+                self.handle_field_add_desc(bus_fmt, self.handle_field_tx_bus, ANN_ANON_DATA)
+            self.handle_field_add_desc(u8_fmt, self.handle_field_tx_id, ANN_ANON_DATA)
         if self.cfg_port:
             self.handle_field_add_desc(u16_fmt, ['PORT {:d}', '{:d}'], ANN_ANON_DATA)
         if self.cfg_pkt_id:
@@ -383,7 +404,7 @@ class Decoder(srd.Decoder):
         ]
         return texts
 
-    def handle_field_init_crc(self, b):
+    def handle_field_meta_crc(self, b):
         '''Process initial CRC (meta) field of a PJON frame.'''
         # Caller provides a list of values. We want a single scalar.
         b = b[0]
@@ -395,12 +416,72 @@ class Decoder(srd.Decoder):
         b = b[0]
         return self.handle_field_common_crc(b, False)
 
+    def handle_field_common_bus(self, b):
+        '''Common handling of bus ID details. Used for RX and TX.'''
+        bus_id = b[:4]
+        bus_num = struct.unpack('>L', bytearray(bus_id))
+        bus_txt = '.'.join(['{:d}'.format(b) for b in bus_id])
+        return bus_num, bus_txt
+
+    def handle_field_rx_bus(self, b):
+        '''Process receiver bus ID field of a PJON frame.'''
+
+        # When we get here, there always should be an RX ID already.
+        bus_num, bus_txt = self.handle_field_common_bus(b[:4])
+        rx_txt = "{} {}".format(bus_txt, self.frame_rx_id[-1])
+        self.frame_rx_id = (bus_num, self.frame_rx_id[0], rx_txt)
+
+        # Provide text representation for frame field, caller emits
+        # the annotation.
+        texts = [
+            'RX_BUS {}'.format(bus_txt),
+            bus_txt,
+        ]
+        return texts
+
+    def handle_field_tx_bus(self, b):
+        '''Process transmitter bus ID field of a PJON frame.'''
+
+        # The TX ID field is optional, as is the use of bus ID fields.
+        # In the TX info case the TX bus ID is seen before the TX ID.
+        bus_num, bus_txt = self.handle_field_common_bus(b[:4])
+        self.frame_tx_id = (bus_num, None, bus_txt)
+
+        # Provide text representation for frame field, caller emits
+        # the annotation.
+        texts = [
+            'TX_BUS {}'.format(bus_txt),
+            bus_txt,
+        ]
+        return texts
+
+    def handle_field_tx_id(self, b):
+        '''Process transmitter ID field of a PJON frame.'''
+
+        b = b[0]
+
+        id_txt = "{:d}".format(b)
+        if self.frame_tx_id is None:
+            self.frame_tx_id = (b, id_txt)
+        else:
+            tx_txt = "{} {}".format(self.frame_tx_id[-1], id_txt)
+            self.frame_tx_id = (self.frame_tx_id[0], b, tx_txt)
+
+        # Provide text representation for frame field, caller emits
+        # the annotation.
+        texts = [
+            'TX_ID {}'.format(id_txt),
+            id_txt,
+        ]
+        return texts
+
     def handle_field_payload(self, b):
         '''Process payload data field of a PJON frame.'''
 
+        text = ' '.join(['{:02x}'.format(v) for v in b])
         self.frame_payload = b[:]
+        self.frame_payload_text = text
 
-        text = ' '.join(['{:02x}'.format(v) for v in b])
         texts = [
             'PAYLOAD {}'.format(text),
             text,
@@ -428,6 +509,8 @@ class Decoder(srd.Decoder):
             self.reset_frame()
             self.frame_bytes = []
             self.handle_field_seed_desc()
+            self.frame_ss = ss
+            self.frame_es = es
             return
 
         # Use IDLE as another (earlier) trigger to flush frames. Also
@@ -452,6 +535,7 @@ class Decoder(srd.Decoder):
         # which corresponds to its most recently seen leader.
         if ptype == 'DATA_BYTE':
             b = pdata
+            self.frame_es = es
 
             # Are we collecting response bytes (ACK)?
             if self.ack_bytes is not None: