X-Git-Url: https://sigrok.org/gitweb/?a=blobdiff_plain;f=decoders%2Fpjon%2Fpd.py;h=b23cfb846ab0cbe301f8db69a06520189e8ecf53;hb=c32de941d4eafc6b773b3d8e26e6befe88d580b7;hp=ac18e402030b3565887641ffe464f017b110bbfc;hpb=12bbd67047b5ec024feeea2faef998336cfa043f;p=libsigrokdecode.git diff --git a/decoders/pjon/pd.py b/decoders/pjon/pd.py index ac18e40..b23cfb8 100644 --- a/decoders/pjon/pd.py +++ b/decoders/pjon/pd.py @@ -22,11 +22,6 @@ # 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 @@ -79,7 +74,7 @@ class Decoder(srd.Decoder): license = 'gplv2+' inputs = ['pjon_link'] outputs = [] - tags = ['Embedded'] + tags = ['Embedded/industrial'] annotations = ( ('rx_info', 'Receiver ID'), ('hdr_cfg', 'Header config'), @@ -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,23 @@ 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. + # TODO Include the service ID (port number) as well? + 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 +181,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 +195,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 +206,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): @@ -216,7 +237,7 @@ class Decoder(srd.Decoder): text.append('svc_id' if self.cfg_port else '-') # port aka service ID text.append('ack_mode' if self.cfg_async_ack else '-') # async response text.append('ack' if self.cfg_sync_ack else '-') # synchronous response - text.append('tx_info' if self.cfg_tx_info else '-') + text.append('tx_info' if self.cfg_tx_info else '-') # sender address text.append('bus_id' if self.cfg_shared else '-') # "shared" vs "local" text = ' '.join(text) bits = '{:08b}'.format(b) @@ -235,11 +256,12 @@ class Decoder(srd.Decoder): # Get the size of variable width fields, to calculate the size # of the packet overhead (the part that is not the payload data). # This lets us derive the payload length when we later receive - # the packet length. + # the frame's total length. u8_fmt = '>B' 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 +299,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: @@ -318,7 +340,7 @@ class Decoder(srd.Decoder): # The wire communicates the total packet length. Some of it is # overhead (non-payload data), while its volume is variable in - # size (dpends on the header configuration). + # size (depends on the header configuration). # # Derive the payload size from previously observed flags. Update # the previously registered field description (the second last @@ -360,12 +382,13 @@ class Decoder(srd.Decoder): # across meta and end checksums in a frame's fields. caption = 'META' if is_meta else 'END' crc_len = 8 if is_meta else 32 if self.cfg_crc32 else 8 + crc_bytes = crc_len // 8 crc_fmt = '{:08x}' if crc_len == 32 else '{:02x}' have_text = crc_fmt.format(have) # Check received against expected checksum. Emit warnings. warn_texts = [] - data = self.frame_bytes[:-1] + data = self.frame_bytes[:-crc_bytes] want = calc_crc32(data) if crc_len == 32 else calc_crc8(data) if want != have: want_text = crc_fmt.format(want) @@ -383,7 +406,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 +418,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 +511,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 +537,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: