]> sigrok.org Git - libsigrokdecode.git/blob - decoders/modbus/pd.py
d5cb811ef1794c226851fb3c6d8bde16c52568f3
[libsigrokdecode.git] / decoders / modbus / pd.py
1 ##
2 ## This file is part of the libsigrokdecode project.
3 ##
4 ## Copyright (C) 2015 Bart de Waal <bart@waalamo.com>
5 ##
6 ## This program is free software; you can redistribute it and/or modify
7 ## it under the terms of the GNU General Public License as published by
8 ## the Free Software Foundation; either version 3 of the License, or
9 ## (at your option) any later version.
10 ##
11 ## This program is distributed in the hope that it will be useful,
12 ## but WITHOUT ANY WARRANTY; without even the implied warranty of
13 ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 ## GNU General Public License for more details.
15 ##
16 ## You should have received a copy of the GNU General Public License
17 ## along with this program; if not, see <http://www.gnu.org/licenses/>.
18 ##
19
20 import sigrokdecode as srd
21 from math import ceil
22
23 RX = 0
24 TX = 1
25 rxtx_channels = ('RX', 'TX')
26
27 class No_more_data(Exception):
28     '''This exception is a signal that we should stop parsing an ADU as there
29     is no more data to parse.'''
30     pass
31
32 class Data:
33     '''The Data class is used to hold the bytes from the serial decode.'''
34     def __init__(self, start, end, data):
35         self.start = start
36         self.end = end
37         self.data = data
38
39 class Modbus_ADU:
40     '''An Application Data Unit is what Modbus calls one message.
41     Protocol decoders are supposed to keep track of state and then provide
42     decoded data to the backend as it reads it. In Modbus' case, the state is
43     the ADU up to that point. This class represents the state and writes the
44     messages to the backend.
45     This class is for the common infrastructure between CS and SC. It should
46     not be used directly, only inhereted from.'''
47
48     def __init__(self, parent, start, write_channel, annotation_prefix):
49         self.data = [] # List of all the data received up to now
50         self.parent = parent # Reference to the decoder object
51         self.start = start
52         self.last_read = start # The last moment parsed by this ADU object
53         self.write_channel = write_channel
54         self.last_byte_put = -1
55         self.annotation_prefix = annotation_prefix
56         # Any Modbus message needs to be at least 4 bytes long. The Modbus
57         # function may make this longer.
58         self.minimum_length = 4
59
60         # This variable is used by an external function to determine when the
61         # next frame should be started.
62         self.startNewFrame = False
63
64         # If there is an error in a frame, we'd like to highlight it. Keep
65         # track of errors.
66         self.hasError = False
67
68     def add_data(self, start, end, data):
69         '''Let the frame handle another piece of data.
70         start: start of this data
71         end: end of this data
72         data: data as received from the UART decoder'''
73         ptype, rxtx, pdata = data
74         self.last_read = end
75         if ptype == 'DATA':
76             self.data.append(Data(start, end, pdata[0]))
77             self.parse() # parse() is defined in the specific type of ADU.
78
79     def puti(self, byte_to_put, annotation, message):
80         '''This class keeps track of how much of the data has already been
81         annotated. This function tells the parent class to write message, but
82         only if it hasn't written about this bit before.
83         byte_to_put: Only write if it hasn't yet written byte_to_put. It will
84                      write from the start of self.last_byte_put+1 to the end
85                      of byte_to_put.
86         annotation: Annotation to write to, without annotation_prefix.
87         message: Message to write.'''
88         if byte_to_put > len(self.data) - 1:
89             # If the byte_to_put hasn't been read yet.
90             raise No_more_data
91
92         if annotation == 'error':
93             self.hasError = True
94
95         if byte_to_put > self.last_byte_put:
96             self.parent.puta(
97                 self.data[self.last_byte_put + 1].start,
98                 self.data[byte_to_put].end,
99                 self.annotation_prefix + annotation,
100                 message)
101             self.last_byte_put = byte_to_put
102             raise No_more_data
103
104     def putl(self, annotation, message, maximum=None):
105         '''Puts the last byte on the stack with message. The contents of the
106         last byte will be applied to message using format.'''
107         last_byte_address = len(self.data) - 1
108         if maximum is not None and last_byte_address > maximum:
109             return
110         self.puti(last_byte_address, annotation,
111                   message.format(self.data[-1].data))
112
113     def close(self, message_overflow):
114         '''Function to be called when next message is started. As there is
115         always space between one message and the next, we can use that space
116         for errors at the end.'''
117         # TODO: Figure out how to make this happen for last message.
118         data = self.data
119         if len(data) < self.minimum_length:
120             if len(data) == 0:
121                 # Sometimes happens with noise, safe to ignore.
122                 return
123             self.parent.puta(
124                 data[self.last_byte_put].end, message_overflow,
125                 self.annotation_prefix + 'error',
126                 'Message too short or not finished')
127             self.hasError = True
128         if self.hasError and self.parent.options['scchannel'] != self.parent.options['cschannel']:
129             # If we are decoding different channels (so client->server and
130             # server->client messages can be separated) we like to mark blocks
131             # containing errors. We don't do this when decoding the same
132             # channel as both a client->server and server->client frame, and
133             # one of those is bound to contain an error, making highlighting
134             # frames useless.
135             self.parent.puta(data[0].start, data[-1].end,
136                              'error-indication', 'Frame contains error')
137         if len(data) > 256:
138             try:
139                 self.puti(len(data) - 1, 'error',
140                     'Modbus data frames are limited to 256 bytes')
141             except No_more_data:
142                 pass
143
144     def check_crc(self, byte_to_put):
145         '''Check the CRC code, data[byte_to_put] is the 2nd byte of the CRC.'''
146         crc_byte1, crc_byte2 = self.calc_crc(byte_to_put)
147         data = self.data
148         if data[-2].data == crc_byte1 and data[-1].data == crc_byte2:
149             self.puti(byte_to_put, 'crc', 'CRC correct')
150         else:
151             self.puti(byte_to_put, 'error',
152                 'CRC should be {} {}'.format(crc_byte1, crc_byte2))
153
154     def half_word(self, start):
155         '''Return the half word (16 bit) value starting at start bytes in. If
156         it goes out of range it raises the usual errors.'''
157         if (start + 1) > (len(self.data) - 1):
158             # If there isn't enough length to access data[start + 1].
159             raise No_more_data
160         return self.data[start].data * 0x100 + self.data[start + 1].data
161
162     def calc_crc(self, last_byte):
163         '''Calculate the CRC, as described in the spec.
164         The last byte of the CRC should be data[last_byte].'''
165         if last_byte < 3:
166             # Every Modbus ADU should be as least 4 long, so we should never
167             # have to calculate a CRC on something shorter.
168             raise Exception('Could not calculate CRC: message too short')
169
170         result = 0xFFFF
171         magic_number = 0xA001 # As defined in the modbus specification.
172         for byte in self.data[:last_byte - 1]:
173             result = result ^ byte.data
174             for i in range(8):
175                 LSB = result & 1
176                 result = result >> 1
177                 if (LSB): # If the LSB is true.
178                     result = result ^ magic_number
179         byte1 = result & 0xFF
180         byte2 = (result & 0xFF00) >> 8
181         return (byte1, byte2)
182
183     def parse_write_single_coil(self):
184         '''Parse function 5, write single coil.'''
185         self.minimum_length = 8
186
187         self.puti(1, 'function', 'Function 5: Write Single Coil')
188
189         address = self.half_word(2)
190         self.puti(3, 'address',
191             'Address 0x{:X} / {:d}'.format(address, address + 10000))
192
193         raw_value = self.half_word(4)
194         value = 'Invalid Coil Value'
195         if raw_value == 0x0000:
196             value = 'Coil Value OFF'
197         elif raw_value == 0xFF00:
198             value = 'Coil Value ON'
199         self.puti(5, 'data', value)
200
201         self.check_crc(7)
202
203     def parse_write_single_register(self):
204         '''Parse function 6, write single register.'''
205         self.minimum_length = 8
206
207         self.puti(1, 'function', 'Function 6: Write Single Register')
208
209         address = self.half_word(2)
210         self.puti(3, 'address',
211             'Address 0x{:X} / {:d}'.format(address, address + 30000))
212
213         value = self.half_word(4)
214         value_formatted = 'Register Value 0x{0:X} / {0:d}'.format(value)
215         self.puti(5, 'data', value_formatted)
216
217         self.check_crc(7)
218
219     def parse_diagnostics(self):
220         '''Parse function 8, diagnostics. This function has many subfunctions,
221         but they are all more or less the same.'''
222         self.minimum_length = 8
223
224         self.puti(1, 'function', 'Function 8: Diagnostics')
225
226         diag_subfunction = {
227             0: 'Return Query data',
228             1: 'Restart Communications Option',
229             2: 'Return Diagnostics Register',
230             3: 'Change ASCII Input Delimiter',
231             4: 'Force Listen Only Mode',
232             10: 'Clear Counters and Diagnostic Register',
233             11: 'Return Bus Message Count',
234             12: 'Return Bus Communication Error Count',
235             13: 'Return Bus Exception Error Count',
236             14: 'Return Slave Message Count',
237             15: 'Return Slave No Response Count',
238             16: 'Return Slave NAK Count',
239             17: 'Return Slave Busy Count',
240             18: 'Return Bus Character Overrun Count',
241             20: 'Return Overrun Counter and Flag',
242         }
243         subfunction = self.half_word(2)
244         subfunction_name = diag_subfunction.get(subfunction,
245                                                 'Reserved subfunction')
246         self.puti(3, 'data',
247             'Subfunction {}: {}'.format(subfunction, subfunction_name))
248
249         diagnostic_data = self.half_word(4)
250         self.puti(5, 'data',
251             'Data Field: {0} / 0x{0:04X}'.format(diagnostic_data))
252
253         self.check_crc(7)
254
255     def parse_mask_write_register(self):
256         '''Parse function 22, Mask Write Register.'''
257         self.minimum_length = 10
258         data = self.data
259
260         self.puti(1, 'function', 'Function 22: Mask Write Register')
261
262         address = self.half_word(2)
263         self.puti(3, 'address',
264             'Address 0x{:X} / {:d}'.format(address, address + 30001))
265
266         self.half_word(4) # To make sure we don't oveflow data.
267         and_mask_1 = data[4].data
268         and_mask_2 = data[5].data
269         self.puti(5, 'data',
270             'AND mask: {:08b} {:08b}'.format(and_mask_1, and_mask_2))
271
272         self.half_word(6) # To make sure we don't oveflow data.
273         or_mask_1 = data[6].data
274         or_mask_2 = data[7].data
275         self.puti(7, 'data',
276             'OR mask: {:08b} {:08b}'.format(or_mask_1, or_mask_2))
277
278         self.check_crc(9)
279
280     def parse_not_implemented(self):
281         '''Explicitly mark certain functions as legal functions, but not
282         implemented in this parser. This is due to the author not being able to
283         find anything (hardware or software) that supports these functions.'''
284         # TODO: Implement these functions.
285
286         # Mentioning what function it is is no problem.
287         function = self.data[1].data
288         functionname = {
289             20: 'Read File Record',
290             21: 'Write File Record',
291             24: 'Read FIFO Queue',
292             43: 'Read Device Identification/Encapsulated Interface Transport',
293         }[function]
294         self.puti(1, 'function',
295             'Function {}: {} (not supported)'.format(function, functionname))
296
297         # From there on out we can keep marking it unsupported.
298         self.putl('data', 'This function is not currently supported')
299
300 class Modbus_ADU_SC(Modbus_ADU):
301     '''SC stands for Server -> Client.'''
302     def parse(self):
303         '''Select which specific Modbus function we should parse.'''
304         data = self.data
305
306         # This try-catch is being used as flow control.
307         try:
308             server_id = data[0].data
309             if 1 <= server_id <= 247:
310                 message = 'Slave ID: {}'.format(server_id)
311             else:
312                 message = 'Slave ID {} is invalid'
313             self.puti(0, 'server-id', message)
314
315             function = data[1].data
316             if function == 1 or function == 2:
317                 self.parse_read_bits()
318             elif function == 3 or function == 4 or function == 23:
319                 self.parse_read_registers()
320             elif function == 5:
321                 self.parse_write_single_coil()
322             elif function == 6:
323                 self.parse_write_single_register()
324             elif function == 7:
325                 self.parse_read_exception_status()
326             elif function == 8:
327                 self.parse_diagnostics()
328             elif function == 11:
329                 self.parse_get_comm_event_counter()
330             elif function == 12:
331                 self.parse_get_comm_event_log()
332             elif function == 15 or function == 16:
333                 self.parse_write_multiple()
334             elif function == 17:
335                 self.parse_report_server_id()
336             elif function == 22:
337                 self.parse_mask_write_register()
338             elif function in {21, 21, 24, 43}:
339                 self.parse_not_implemented()
340             elif function > 0x80:
341                 self.parse_error()
342             else:
343                 self.puti(1, 'error',
344                           'Unknown function: {}'.format(data[1].data))
345                 self.putl('error', 'Unknown function')
346
347             # If the message gets here without raising an exception, the
348             # message goes on longer than it should.
349             self.putl('error', 'Message too long')
350
351         except No_more_data:
352             # Just a message saying we don't need to parse anymore this round.
353             pass
354
355     def parse_read_bits(self):
356         self.mimumum_length = 5
357
358         data = self.data
359         function = data[1].data
360
361         if function == 1:
362             self.puti(1, 'function', 'Function 1: Read Coils')
363         else:
364             self.puti(1, 'function', 'Function 2: Read Discrete Inputs')
365
366         bytecount = self.data[2].data
367         self.minimum_length = 5 + bytecount # 3 before data, 2 CRC.
368         self.puti(2, 'length', 'Byte count: {}'.format(bytecount))
369
370         # From here on out, we expect registers on 3 and 4, 5 and 6 etc.
371         # So registers never start when the length is even.
372         self.putl('data', '{:08b}', bytecount + 2)
373         self.check_crc(bytecount + 4)
374
375     def parse_read_registers(self):
376         self.mimumum_length = 5
377
378         data = self.data
379
380         function = data[1].data
381         if function == 3:
382             self.puti(1, 'function', 'Function 3: Read Holding Registers')
383         elif function == 4:
384             self.puti(1, 'function', 'Function 4: Read Input Registers')
385         elif function == 23:
386             self.puti(1, 'function', 'Function 23: Read/Write Multiple Registers')
387
388         bytecount = self.data[2].data
389         self.minimum_length = 5 + bytecount # 3 before data, 2 CRC.
390         if bytecount % 2 == 0:
391             self.puti(2, 'length', 'Byte count: {}'.format(bytecount))
392         else:
393             self.puti(2, 'error',
394                 'Error: Odd byte count ({})'.format(bytecount))
395
396         # From here on out, we expect registers on 3 and 4, 5 and 6 etc.
397         # So registers never start when the length is even.
398         if len(data) % 2 == 1:
399             register_value = self.half_word(-2)
400             self.putl('data', '0x{0:04X} / {0}'.format(register_value),
401                       bytecount + 2)
402         else:
403             raise No_more_data
404
405         self.check_crc(bytecount + 4)
406
407     def parse_read_exception_status(self):
408         self.mimumum_length = 5
409
410         self.puti(1, 'function', 'Function 7: Read Exception Status')
411         exception_status = self.data[2].data
412         self.puti(2, 'data',
413                   'Exception status: {:08b}'.format(exception_status))
414         self.check_crc(4)
415
416     def parse_get_comm_event_counter(self):
417         self.mimumum_length = 8
418
419         self.puti(1, 'function', 'Function 11: Get Comm Event Counter')
420
421         status = self.half_word(2)
422         if status == 0x0000:
423             self.puti(3, 'data', 'Status: not busy')
424         elif status == 0xFFFF:
425             self.puti(3, 'data', 'Status: busy')
426         else:
427             self.puti(3, 'error', 'Bad status: 0x{:04X}'.format(status))
428
429         count = self.half_word(4)
430         self.puti(5, 'data', 'Event Count: {}'.format(count))
431         self.check_crc(7)
432
433     def parse_get_comm_event_log(self):
434         self.mimumum_length = 11
435         self.puti(1, 'function', 'Function 12: Get Comm Event Log')
436
437         data = self.data
438
439         bytecount = data[2].data
440         self.puti(2, 'length', 'Bytecount: {}'.format(bytecount))
441         # The bytecount is the length of everything except the slaveID,
442         # function code, bytecount and CRC.
443         self.mimumum_length = 5 + bytecount
444
445         status = self.half_word(3)
446         if status == 0x0000:
447             self.puti(4, 'data', 'Status: not busy')
448         elif status == 0xFFFF:
449             self.puti(4, 'data', 'Status: busy')
450         else:
451             self.puti(4, 'error', 'Bad status: 0x{:04X}'.format(status))
452
453         event_count = self.half_word(5)
454         self.puti(6, 'data', 'Event Count: {}'.format(event_count))
455
456         message_count = self.half_word(7)
457         self.puti(8, 'data', 'Message Count: {}'.format(message_count))
458
459         self.putl('data', 'Event: 0x{:02X}'.format(data[-1].data),
460                   bytecount + 2)
461
462         self.check_crc(bytecount + 4)
463
464     def parse_write_multiple(self):
465         '''Function 15 and 16 are almost the same, so we can parse them both
466         using one function.'''
467         self.mimumum_length = 8
468
469         function = self.data[1].data
470         if function == 15:
471             data_unit = 'Coils'
472             max_outputs = 0x07B0
473             long_address_offset = 10001
474         elif function == 16:
475             data_unit = 'Registers'
476             max_outputs = 0x007B
477             long_address_offset = 30001
478
479         self.puti(1, 'function',
480             'Function {}: Write Multiple {}'.format(function, data_unit))
481
482         starting_address = self.half_word(2)
483         # Some instruction manuals use a long form name for addresses, this is
484         # listed here for convienience.
485         address_name = long_address_offset + starting_address
486         self.puti(3, 'address',
487             'Start at address 0x{:X} / {:d}'.format(starting_address,
488                                                     address_name))
489
490         quantity_of_outputs = self.half_word(4)
491         if quantity_of_outputs <= max_outputs:
492             self.puti(5, 'data',
493                 'Write {} {}'.format(quantity_of_outputs, data_unit))
494         else:
495             self.puti(5, 'error',
496                 'Bad value: {} {}. Max is {}'.format(quantity_of_outputs,
497                                                      data_unit, max_outputs))
498
499         self.check_crc(7)
500
501     def parse_report_server_id(self):
502         # Buildup of this function:
503         # 1 byte serverID
504         # 1 byte function (17)
505         # 1 byte bytecount
506         # 1 byte serverID (counts for bytecount)
507         # 1 byte Run Indicator Status (counts for bytecount)
508         # bytecount - 2 bytes of device specific data (counts for bytecount)
509         # 2 bytes of CRC
510         self.mimumum_length = 7
511         data = self.data
512         self.puti(1, 'function', 'Function 17: Report Server ID')
513
514         bytecount = data[2].data
515         self.puti(2, 'length', 'Data is {} bytes long'.format(bytecount))
516
517         self.puti(3, 'data', 'serverID: {}'.format(data[3].data))
518
519         run_indicator_status = data[4].data
520         if run_indicator_status == 0x00:
521             self.puti(4, 'data', 'Run Indicator status: Off')
522         elif run_indicator_status == 0xFF:
523             self.puti(4, 'data', 'Run Indicator status: On')
524         else:
525             self.puti(4, 'error',
526                 'Bad Run Indicator status: 0x{:X}'.format(run_indicator_status))
527
528         self.putl('data', 'Device specific data: {}, "{}"'.format(data[-1].data,
529                   chr(data[-1].data)), 2 + bytecount)
530
531         self.check_crc(4 + bytecount)
532
533     def parse_error(self):
534         '''Parse a Modbus error message.'''
535         self.mimumum_length = 5
536         # The function code of an error is always 0x80 above the function call
537         # that caused it.
538         functioncode = self.data[1].data - 0x80
539
540         functions = {
541             1: 'Read Coils',
542             2: 'Read Discrete Inputs',
543             3: 'Read Holding Registers',
544             4: 'Read Input Registers',
545             5: 'Write Single Coil',
546             6: 'Write Single Register',
547             7: 'Read Exception Status',
548             8: 'Diagnostic',
549             11: 'Get Com Event Counter',
550             12: 'Get Com Event Log',
551             15: 'Write Multiple Coils',
552             16: 'Write Multiple Registers',
553             17: 'Report Slave ID',
554             20: 'Read File Record',
555             21: 'Write File Record',
556             22: 'Mask Write Register',
557             23: 'Read/Write Multiple Registers',
558             24: 'Read FIFO Queue',
559             43: 'Read Device Identification/Encapsulated Interface Transport',
560         }
561         functionname = '{}: {}'.format(functioncode,
562             functions.get(functioncode, 'Unknown function'))
563         self.puti(1, 'function',
564                   'Error for function {}'.format(functionname))
565
566         error = self.data[2].data
567         errorcodes = {
568             1: 'Illegal Function',
569             2: 'Illegal Data Address',
570             3: 'Illegal Data Value',
571             4: 'Slave Device Failure',
572             5: 'Acknowledge',
573             6: 'Slave Device Busy',
574             8: 'Memory Parity Error',
575             10: 'Gateway Path Unavailable',
576             11: 'Gateway Target Device failed to respond',
577         }
578         errorname = '{}: {}'.format(error, errorcodes.get(error, 'Unknown'))
579         self.puti(2, 'data', 'Error {}'.format(errorname))
580         self.check_crc(4)
581
582 class Modbus_ADU_CS(Modbus_ADU):
583     '''CS stands for Client -> Server.'''
584     def parse(self):
585         '''Select which specific Modbus function we should parse.'''
586         data = self.data
587
588         # This try-catch is being used as flow control.
589         try:
590             server_id = data[0].data
591             message = ''
592             if server_id == 0:
593                 message = 'Broadcast message'
594             elif 1 <= server_id <= 247:
595                 message = 'Slave ID: {}'.format(server_id)
596             elif 248 <= server_id <= 255:
597                 message = 'Slave ID: {} (reserved address)'.format(server_id)
598             self.puti(0, 'server-id', message)
599
600             function = data[1].data
601             if function >= 1 and function <= 4:
602                 self.parse_read_data_command()
603             if function == 5:
604                 self.parse_write_single_coil()
605             if function == 6:
606                 self.parse_write_single_register()
607             if function in {7, 11, 12, 17}:
608                 self.parse_single_byte_request()
609             elif function == 8:
610                 self.parse_diagnostics()
611             if function in {15, 16}:
612                 self.parse_write_multiple()
613             elif function == 22:
614                 self.parse_mask_write_register()
615             elif function == 23:
616                 self.parse_read_write_registers()
617             elif function in {21, 21, 24, 43}:
618                 self.parse_not_implemented()
619             else:
620                 self.puti(1, 'error',
621                           'Unknown function: {}'.format(data[1].data))
622                 self.putl('error', 'Unknown function')
623
624             # If the message gets here without raising an exception, the
625             # message goes on longer than it should.
626             self.putl('error', 'Message too long')
627
628         except No_more_data:
629             # Just a message saying we don't need to parse anymore this round.
630             pass
631
632     def parse_read_data_command(self):
633         '''Interpret a command to read x units of data starting at address, ie
634         functions 1, 2, 3 and 4, and write the result to the annotations.'''
635         data = self.data
636         self.minimum_length = 8
637
638         function = data[1].data
639         functionname = {1: 'Read Coils',
640                         2: 'Read Discrete Inputs',
641                         3: 'Read Holding Registers',
642                         4: 'Read Input Registers',
643                         }[function]
644
645         self.puti(1, 'function',
646                   'Function {}: {}'.format(function, functionname))
647
648         starting_address = self.half_word(2)
649         # Some instruction manuals use a long form name for addresses, this is
650         # listed here for convienience.
651         # Example: holding register 60 becomes 30061.
652         address_name = 10000 * function + 1 + starting_address
653         self.puti(3, 'address',
654             'Start at address 0x{:X} / {:d}'.format(starting_address,
655                                                     address_name))
656
657         self.puti(5, 'length',
658                   'Read {:d} units of data'.format(self.half_word(4)))
659         self.check_crc(7)
660
661     def parse_single_byte_request(self):
662         '''Some Modbus functions have no arguments, this parses those.'''
663         function = self.data[1].data
664         function_name = {7: 'Read Exception Status',
665                          11: 'Get Comm Event Counter',
666                          12: 'Get Comm Event Log',
667                          17: 'Report Slave ID',
668                          }[function]
669         self.puti(1, 'function',
670                   'Function {}: {}'.format(function, function_name))
671
672         self.check_crc(3)
673
674     def parse_write_multiple(self):
675         '''Function 15 and 16 are almost the same, so we can parse them both
676         using one function.'''
677         self.mimumum_length = 9
678
679         function = self.data[1].data
680         if function == 15:
681             data_unit = 'Coils'
682             max_outputs = 0x07B0
683             ratio_bytes_data = 1/8
684             long_address_offset = 10001
685         elif function == 16:
686             data_unit = 'Registers'
687             max_outputs = 0x007B
688             ratio_bytes_data = 2
689             long_address_offset = 30001
690
691         self.puti(1, 'function',
692             'Function {}: Write Multiple {}'.format(function, data_unit))
693
694         starting_address = self.half_word(2)
695         # Some instruction manuals use a long form name for addresses, this is
696         # listed here for convienience.
697         address_name = long_address_offset + starting_address
698         self.puti(3, 'address',
699             'Start at address 0x{:X} / {:d}'.format(starting_address,
700                                                     address_name))
701
702         quantity_of_outputs = self.half_word(4)
703         if quantity_of_outputs <= max_outputs:
704             self.puti(5, 'length',
705                 'Write {} {}'.format(quantity_of_outputs, data_unit))
706         else:
707             self.puti(5, 'error',
708                 'Bad value: {} {}. Max is {}'.format(quantity_of_outputs,
709                                                      data_unit, max_outputs))
710         proper_bytecount = ceil(quantity_of_outputs * ratio_bytes_data)
711
712         bytecount = self.data[6].data
713         if bytecount == proper_bytecount:
714             self.puti(6, 'length', 'Byte count: {}'.format(bytecount))
715         else:
716             self.puti(6, 'error',
717                 'Bad byte count, is {}, should be {}'.format(bytecount,
718                                                              proper_bytecount))
719         self.mimumum_length = bytecount + 9
720
721         self.putl('data', 'Value 0x{:X}', 6 + bytecount)
722
723         self.check_crc(bytecount + 8)
724
725     def parse_read_file_record(self):
726         self.puti(1, 'function', 'Function 20: Read file records')
727
728         data = self.data
729
730         bytecount = data[2].data
731
732         self.minimum_length = 5 + bytecount
733         # 1 for serverID, 1 for function, 1 for bytecount, 2 for CRC.
734
735         if 0x07 <= bytecount <= 0xF5:
736             self.puti(2, 'length', 'Request is {} bytes long'.format(bytecount))
737         else:
738             self.puti(2, 'error',
739                 'Request claims to be {} bytes long, legal values are between'
740                 ' 7 and 247'.format(bytecount))
741
742         current_byte = len(data) - 1
743         # Function 20 is a number of sub-requests, the first starting at 3,
744         # the total length of the sub-requests is bytecount.
745         if current_byte <= bytecount + 2:
746             step = (current_byte - 3) % 7
747             if step == 0:
748                 if data[current_byte].data == 6:
749                     self.puti(current_byte, 'data', 'Start sub-request')
750                 else:
751                     self.puti(current_byte, 'error',
752                         'First byte of subrequest should be 0x06')
753             elif step == 1:
754                 raise No_more_data
755             elif step == 2:
756                 file_number = self.half_word(current_byte - 1)
757                 self.puti(current_byte, 'data',
758                           'Read File number {}'.format(file_number))
759             elif step == 3:
760                 raise No_more_data
761             elif step == 4:
762                 record_number = self.half_word(current_byte - 1)
763                 self.puti(current_byte, 'address',
764                     'Read from record number {}'.format(record_number))
765                 # TODO: Check if within range.
766             elif step == 5:
767                 raise No_more_data
768             elif step == 6:
769                 records_to_read = self.half_word(current_byte - 1)
770                 self.puti(current_byte, 'length',
771                     'Read {} records'.format(records_to_read))
772         self.check_crc(4 + bytecount)
773
774     def parse_read_write_registers(self):
775         '''Parse function 23: Read/Write multiple registers.'''
776         self.minimum_length = 13
777
778         self.puti(1, 'function', 'Function 23: Read/Write Multiple Registers')
779
780         starting_address = self.half_word(2)
781         # Some instruction manuals use a long form name for addresses, this is
782         # listed here for convienience.
783         # Example: holding register 60 becomes 30061.
784         address_name = 30001 + starting_address
785         self.puti(3, 'address',
786             'Read starting at address 0x{:X} / {:d}'.format(starting_address,
787                                                             address_name))
788
789         self.puti(5, 'length', 'Read {:d} units of data'.format(self.half_word(4)))
790
791         starting_address = self.half_word(6)
792         self.puti(7, 'address',
793             'Write starting at address 0x{:X} / {:d}'.format(starting_address,
794                                                              address_name))
795
796         quantity_of_outputs = self.half_word(8)
797         self.puti(9, 'length',
798                            'Write {} registers'.format(quantity_of_outputs))
799         proper_bytecount = quantity_of_outputs * 2
800
801         bytecount = self.data[10].data
802         if bytecount == proper_bytecount:
803             self.puti(10, 'length', 'Byte count: {}'.format(bytecount))
804         else:
805             self.puti(10, 'error',
806                 'Bad byte count, is {}, should be {}'.format(bytecount,
807                                                              proper_bytecount))
808         self.mimumum_length = bytecount + 13
809
810         self.putl('data', 'Data, value 0x{:02X}', 10 + bytecount)
811
812         self.check_crc(bytecount + 12)
813
814 class Decoder(srd.Decoder):
815     api_version = 3
816     id = 'modbus'
817     name = 'Modbus'
818     longname = 'Modbus RTU over RS232/RS485'
819     desc = 'Modbus RTU protocol for industrial applications.'
820     license = 'gplv3+'
821     inputs = ['uart']
822     outputs = ['modbus']
823     tags = ['Embedded/industrial']
824     annotations = (
825         ('sc-server-id', ''),
826         ('sc-function', ''),
827         ('sc-crc', ''),
828         ('sc-address', ''),
829         ('sc-data', ''),
830         ('sc-length', ''),
831         ('sc-error', ''),
832         ('cs-server-id', ''),
833         ('cs-function', ''),
834         ('cs-crc', ''),
835         ('cs-address', ''),
836         ('cs-data', ''),
837         ('cs-length', ''),
838         ('cs-error', ''),
839         ('error-indication', ''),
840     )
841     annotation_rows = (
842         ('sc', 'Server->client', (0, 1, 2, 3, 4, 5, 6)),
843         ('cs', 'Client->server', (7, 8, 9, 10, 11, 12, 13)),
844         ('error-indicator', 'Errors in frame', (14,)),
845     )
846     options = (
847         {'id': 'scchannel', 'desc': 'Server -> client channel',
848             'default': rxtx_channels[0], 'values': rxtx_channels},
849         {'id': 'cschannel', 'desc': 'Client -> server channel',
850             'default': rxtx_channels[1], 'values': rxtx_channels},
851         {'id': 'framegap', 'desc': 'Inter-frame bit gap', 'default': '28'},
852     )
853
854     def __init__(self):
855         self.reset()
856
857     def reset(self):
858         self.ADUSc = None # Start off with empty slave -> client ADU.
859         self.ADUCs = None # Start off with empty client -> slave ADU.
860
861         # The reason we have both (despite not supporting full duplex comms) is
862         # because we want to be able to decode the message as both client ->
863         # server and server -> client, and let the user see which of the two
864         # the ADU was.
865
866         self.bitlength = None # We will later test how long a bit is.
867
868     def start(self):
869         self.out_ann = self.register(srd.OUTPUT_ANN)
870
871     def puta(self, start, end, ann_str, message):
872         '''Put an annotation from start to end, with ann as a
873         string. This means you don't have to know the ann's
874         number to write annotations to it.'''
875         ann = [s[0] for s in self.annotations].index(ann_str)
876         self.put(start, end, self.out_ann, [ann, [message]])
877
878     def decode_adu(self, ss, es, data, direction):
879         '''Decode the next byte or bit (depending on type) in the ADU.
880         ss: Start time of the data
881         es: End time of the data
882         data: Data as passed from the UART decoder
883         direction: Is this data for the Cs (client -> server) or Sc (server ->
884                    client) being decoded right now?'''
885         ptype, rxtx, pdata = data
886
887         # We don't have a nice way to get the baud rate from UART, so we have
888         # to figure out how long a bit lasts. We do this by looking at the
889         # length of (probably) the startbit.
890         if self.bitlength is None:
891             if ptype == 'STARTBIT' or ptype == 'STOPBIT':
892                 self.bitlength = es - ss
893             else:
894                 # If we don't know the bitlength yet, we can't start decoding.
895                 return
896
897         # Select the ADU, create the ADU if needed.
898         # We set ADU.startNewFrame = True when we know the old one is over.
899         if direction == 'Sc':
900             if (self.ADUSc is None) or self.ADUSc.startNewFrame:
901                 self.ADUSc = Modbus_ADU_SC(self, ss, TX, 'sc-')
902             ADU = self.ADUSc
903         if direction == 'Cs':
904             if self.ADUCs is None or self.ADUCs.startNewFrame:
905                 self.ADUCs = Modbus_ADU_CS(self, ss, TX, 'cs-')
906             ADU = self.ADUCs
907
908         # We need to determine if the last ADU is over.
909         # According to the Modbus spec, there should be 3.5 characters worth of
910         # space between each message. But if within a message there is a length
911         # of more than 1.5 character, that's an error. For our purposes
912         # somewhere between seems fine.
913         # A character is 11 bits long, so (3.5 + 1.5)/2 * 11 ~= 28
914         # TODO: Display error for too short or too long.
915         if (ss - ADU.last_read) <= self.bitlength * int(self.options['framegap']):
916             ADU.add_data(ss, es, data)
917         else:
918             # It's been too long since the last part of the ADU!
919             # If there is any data in the ADU we need to show it to the user
920             if len(ADU.data) > 0:
921                 # Extend errors for 3 bits after last byte, we can guarantee
922                 # space.
923                 ADU.close(ADU.data[-1].end + self.bitlength * 3)
924
925             ADU.startNewFrame = True
926             # Restart this function, it will make a new ADU for us.
927             self.decode_adu(ss, es, data, direction)
928
929     def decode(self, ss, es, data):
930         ptype, rxtx, pdata = data
931
932         # Decide what ADU(s) we need this packet to go to.
933         # Note that it's possible to go to both ADUs.
934         if rxtx_channels[rxtx] == self.options['scchannel']:
935             self.decode_adu(ss, es, data, 'Sc')
936         if rxtx_channels[rxtx] == self.options['cschannel']:
937             self.decode_adu(ss, es, data, 'Cs')