]> sigrok.org Git - libsigrokdecode.git/commitdiff
modbus: Make C->S and S->C configurable, add framegap option.
authorAndrew Gregory <redacted>
Wed, 5 Dec 2018 15:31:34 +0000 (23:31 +0800)
committerUwe Hermann <redacted>
Sun, 30 Jun 2019 20:45:22 +0000 (22:45 +0200)
Change client->server and server->client to be separately configurable,
allowing decoding at both the server (where client->server is RX and
server->client is TX) and client (where client->server is TX and
server->client is RX) ends of the link. It also allows monitoring of the
bus on a single channel (where client->server and server->client are both
RX (or TX)).

When I tried to decode a bus capture, I found that when the transmitter was
turned off it generated a false start bit, which in turn resulted in a false
trailing byte from the UART decoder. This narrowed the inter-frame gap to
the point where the Modbus decoder failed to recognise a new frame. The
result was only the first frame of the capture decoded - all the rest of the
frames failed to decode. I had to reduce the frame gap to allow subsequent
frames to decode, and so made it a configurable option that defaults to the
existing gap.

Lastly, I fixed a call to puti() that incorrectly included the annotation
prefix.

decoders/modbus/pd.py

index d5b39ca0725f0079f8d58462c0fa518edd823ebd..d5cb811ef1794c226851fb3c6d8bde16c52568f3 100644 (file)
@@ -22,6 +22,7 @@ from math import ceil
 
 RX = 0
 TX = 1
 
 RX = 0
 TX = 1
+rxtx_channels = ('RX', 'TX')
 
 class No_more_data(Exception):
     '''This exception is a signal that we should stop parsing an ADU as there
 
 class No_more_data(Exception):
     '''This exception is a signal that we should stop parsing an ADU as there
@@ -124,18 +125,18 @@ class Modbus_ADU:
                 self.annotation_prefix + 'error',
                 'Message too short or not finished')
             self.hasError = True
                 self.annotation_prefix + 'error',
                 'Message too short or not finished')
             self.hasError = True
-        if self.hasError and self.parent.options['channel'] == 'RX':
-            # If we are on RX mode (so client->server and server->client
-            # messages can be seperated) we like to mark blocks containing
-            # errors. We don't do this in TX mode, because then we interpret
-            # each frame as both a client->server and server->client frame, and
+        if self.hasError and self.parent.options['scchannel'] != self.parent.options['cschannel']:
+            # If we are decoding different channels (so client->server and
+            # server->client messages can be separated) we like to mark blocks
+            # containing errors. We don't do this when decoding the same
+            # channel as both a client->server and server->client frame, and
             # one of those is bound to contain an error, making highlighting
             # frames useless.
             self.parent.puta(data[0].start, data[-1].end,
                              'error-indication', 'Frame contains error')
         if len(data) > 256:
             try:
             # one of those is bound to contain an error, making highlighting
             # frames useless.
             self.parent.puta(data[0].start, data[-1].end,
                              'error-indication', 'Frame contains error')
         if len(data) > 256:
             try:
-                self.puti(len(data) - 1, self.annotation_prefix + 'error',
+                self.puti(len(data) - 1, 'error',
                     'Modbus data frames are limited to 256 bytes')
             except No_more_data:
                 pass
                     'Modbus data frames are limited to 256 bytes')
             except No_more_data:
                 pass
@@ -843,8 +844,11 @@ class Decoder(srd.Decoder):
         ('error-indicator', 'Errors in frame', (14,)),
     )
     options = (
         ('error-indicator', 'Errors in frame', (14,)),
     )
     options = (
-        {'id': 'channel', 'desc': 'Server -> client channel', 'default': 'RX',
-            'values': ('RX', 'TX')},
+        {'id': 'scchannel', 'desc': 'Server -> client channel',
+            'default': rxtx_channels[0], 'values': rxtx_channels},
+        {'id': 'cschannel', 'desc': 'Client -> server channel',
+            'default': rxtx_channels[1], 'values': rxtx_channels},
+        {'id': 'framegap', 'desc': 'Inter-frame bit gap', 'default': '28'},
     )
 
     def __init__(self):
     )
 
     def __init__(self):
@@ -908,7 +912,7 @@ class Decoder(srd.Decoder):
         # somewhere between seems fine.
         # A character is 11 bits long, so (3.5 + 1.5)/2 * 11 ~= 28
         # TODO: Display error for too short or too long.
         # somewhere between seems fine.
         # A character is 11 bits long, so (3.5 + 1.5)/2 * 11 ~= 28
         # TODO: Display error for too short or too long.
-        if (ss - ADU.last_read) <= self.bitlength * 28:
+        if (ss - ADU.last_read) <= self.bitlength * int(self.options['framegap']):
             ADU.add_data(ss, es, data)
         else:
             # It's been too long since the last part of the ADU!
             ADU.add_data(ss, es, data)
         else:
             # It's been too long since the last part of the ADU!
@@ -927,9 +931,7 @@ class Decoder(srd.Decoder):
 
         # Decide what ADU(s) we need this packet to go to.
         # Note that it's possible to go to both ADUs.
 
         # Decide what ADU(s) we need this packet to go to.
         # Note that it's possible to go to both ADUs.
-        if rxtx == TX:
-            self.decode_adu(ss, es, data, 'Cs')
-        if rxtx == TX and self.options['channel'] == 'TX':
-            self.decode_adu(ss, es, data, 'Sc')
-        if rxtx == RX and self.options['channel'] == 'RX':
+        if rxtx_channels[rxtx] == self.options['scchannel']:
             self.decode_adu(ss, es, data, 'Sc')
             self.decode_adu(ss, es, data, 'Sc')
+        if rxtx_channels[rxtx] == self.options['cschannel']:
+            self.decode_adu(ss, es, data, 'Cs')