2 ## This file is part of the libsigrokdecode project.
4 ## Copyright (C) 2015 Bart de Waal <bart@waalamo.com>
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.
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.
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/>.
20 import sigrokdecode as srd
26 class No_more_data(Exception):
27 '''This exception is a signal that we should stop parsing an ADU as there
28 is no more data to parse.'''
32 '''The Data class is used to hold the bytes from the serial decode.'''
33 def __init__(self, start, end, data):
39 '''An Application Data Unit is what Modbus calls one message.
40 Protocol decoders are supposed to keep track of state and then provide
41 decoded data to the backend as it reads it. In Modbus' case, the state is
42 the ADU up to that point. This class represents the state and writes the
43 messages to the backend.
44 This class is for the common infrastructure between CS and SC. It should
45 not be used directly, only inhereted from.'''
47 def __init__(self, parent, start, write_channel, annotation_prefix):
48 self.data = [] # List of all the data received up to now
49 self.parent = parent # Reference to the decoder object
51 self.last_read = start # The last moment parsed by this ADU object
52 self.write_channel = write_channel
53 self.last_byte_put = -1
54 self.annotation_prefix = annotation_prefix
55 # Any Modbus message needs to be at least 4 bytes long. The Modbus
56 # function may make this longer.
57 self.minimum_length = 4
59 # This variable is used by an external function to determine when the
60 # next frame should be started.
61 self.startNewFrame = False
63 # If there is an error in a frame, we'd like to highlight it. Keep
67 def add_data(self, start, end, data):
68 '''Let the frame handle another piece of data.
69 start: start of this data
71 data: data as received from the UART decoder'''
72 ptype, rxtx, pdata = data
75 self.data.append(Data(start, end, pdata[0]))
76 self.parse() # parse() is defined in the specific type of ADU.
78 def puti(self, byte_to_put, annotation, message):
79 '''This class keeps track of how much of the data has already been
80 annotated. This function tells the parent class to write message, but
81 only if it hasn't written about this bit before.
82 byte_to_put: Only write if it hasn't yet written byte_to_put. It will
83 write from the start of self.last_byte_put+1 to the end
85 annotation: Annotation to write to, without annotation_prefix.
86 message: Message to write.'''
87 if byte_to_put > len(self.data) - 1:
88 # If the byte_to_put hasn't been read yet.
91 if annotation == 'error':
94 if byte_to_put > self.last_byte_put:
96 self.data[self.last_byte_put + 1].start,
97 self.data[byte_to_put].end,
98 self.annotation_prefix + annotation,
100 self.last_byte_put = byte_to_put
103 def putl(self, annotation, message, maximum=None):
104 '''Puts the last byte on the stack with message. The contents of the
105 last byte will be applied to message using format.'''
106 last_byte_address = len(self.data) - 1
107 if maximum is not None and last_byte_address > maximum:
109 self.puti(last_byte_address, annotation,
110 message.format(self.data[-1].data))
112 def close(self, message_overflow):
113 '''Function to be called when next message is started. As there is
114 always space between one message and the next, we can use that space
115 for errors at the end.'''
116 # TODO: Figure out how to make this happen for last message.
118 if len(data) < self.minimum_length:
120 # Sometimes happens with noise, safe to ignore.
123 data[self.last_byte_put].end, message_overflow,
124 self.annotation_prefix + 'error',
125 'Message too short or not finished')
127 if self.hasError and self.parent.options['channel'] == 'RX':
128 # If we are on RX mode (so client->server and server->client
129 # messages can be seperated) we like to mark blocks containing
130 # errors. We don't do this in TX mode, because then we interpret
131 # each frame as both a client->server and server->client frame, and
132 # one of those is bound to contain an error, making highlighting
134 self.parent.puta(data[0].start, data[-1].end,
135 'error-indication', 'Frame contains error')
138 self.puti(len(data) - 1, self.annotation_prefix + 'error',
139 'Modbus data frames are limited to 256 bytes')
143 def check_crc(self, byte_to_put):
144 '''Check the CRC code, data[byte_to_put] is the 2nd byte of the CRC.'''
145 crc_byte1, crc_byte2 = self.calc_crc(byte_to_put)
147 if data[-2].data == crc_byte1 and data[-1].data == crc_byte2:
148 self.puti(byte_to_put, 'crc', 'CRC correct')
150 self.puti(byte_to_put, 'error',
151 'CRC should be {} {}'.format(crc_byte1, crc_byte2))
153 def half_word(self, start):
154 '''Return the half word (16 bit) value starting at start bytes in. If
155 it goes out of range it raises the usual errors.'''
156 if (start + 1) > (len(self.data) - 1):
157 # If there isn't enough length to access data[start + 1].
159 return self.data[start].data * 0x100 + self.data[start + 1].data
161 def calc_crc(self, last_byte):
162 '''Calculate the CRC, as described in the spec.
163 The last byte of the CRC should be data[last_byte].'''
165 # Every Modbus ADU should be as least 4 long, so we should never
166 # have to calculate a CRC on something shorter.
167 raise Exception('Could not calculate CRC: message too short')
170 magic_number = 0xA001 # As defined in the modbus specification.
171 for byte in self.data[:last_byte - 1]:
172 result = result ^ byte.data
176 if (LSB): # If the LSB is true.
177 result = result ^ magic_number
178 byte1 = result & 0xFF
179 byte2 = (result & 0xFF00) >> 8
180 return (byte1, byte2)
182 def parse_write_single_coil(self):
183 '''Parse function 5, write single coil.'''
184 self.minimum_length = 8
186 self.puti(1, 'function', 'Function 5: Write Single Coil')
188 address = self.half_word(2)
189 self.puti(3, 'address',
190 'Address 0x{:X} / {:d}'.format(address, address + 10000))
192 raw_value = self.half_word(4)
193 value = 'Invalid Coil Value'
194 if raw_value == 0x0000:
195 value = 'Coil Value OFF'
196 elif raw_value == 0xFF00:
197 value = 'Coil Value ON'
198 self.puti(5, 'data', value)
202 def parse_write_single_register(self):
203 '''Parse function 6, write single register.'''
204 self.minimum_length = 8
206 self.puti(1, 'function', 'Function 6: Write Single Register')
208 address = self.half_word(2)
209 self.puti(3, 'address',
210 'Address 0x{:X} / {:d}'.format(address, address + 30000))
212 value = self.half_word(4)
213 value_formatted = 'Register Value 0x{0:X} / {0:d}'.format(value)
214 self.puti(5, 'data', value_formatted)
218 def parse_diagnostics(self):
219 '''Parse function 8, diagnostics. This function has many subfunctions,
220 but they are all more or less the same.'''
221 self.minimum_length = 8
223 self.puti(1, 'function', 'Function 8: Diagnostics')
226 0: 'Return Query data',
227 1: 'Restart Communications Option',
228 2: 'Return Diagnostics Register',
229 3: 'Change ASCII Input Delimiter',
230 4: 'Force Listen Only Mode',
231 10: 'Clear Counters and Diagnostic Register',
232 11: 'Return Bus Message Count',
233 12: 'Return Bus Communication Error Count',
234 13: 'Return Bus Exception Error Count',
235 14: 'Return Slave Message Count',
236 15: 'Return Slave No Response Count',
237 16: 'Return Slave NAK Count',
238 17: 'Return Slave Busy Count',
239 18: 'Return Bus Character Overrun Count',
240 20: 'Return Overrun Counter and Flag',
242 subfunction = self.half_word(2)
243 subfunction_name = diag_subfunction.get(subfunction,
244 'Reserved subfunction')
246 'Subfunction {}: {}'.format(subfunction, subfunction_name))
248 diagnostic_data = self.half_word(4)
250 'Data Field: {0} / 0x{0:04X}'.format(diagnostic_data))
254 def parse_mask_write_register(self):
255 '''Parse function 22, Mask Write Register.'''
256 self.minimum_length = 10
259 self.puti(1, 'function', 'Function 22: Mask Write Register')
261 address = self.half_word(2)
262 self.puti(3, 'address',
263 'Address 0x{:X} / {:d}'.format(address, address + 30001))
265 self.half_word(4) # To make sure we don't oveflow data.
266 and_mask_1 = data[4].data
267 and_mask_2 = data[5].data
269 'AND mask: {:08b} {:08b}'.format(and_mask_1, and_mask_2))
271 self.half_word(6) # To make sure we don't oveflow data.
272 or_mask_1 = data[6].data
273 or_mask_2 = data[7].data
275 'OR mask: {:08b} {:08b}'.format(or_mask_1, or_mask_2))
279 def parse_not_implemented(self):
280 '''Explicitly mark certain functions as legal functions, but not
281 implemented in this parser. This is due to the author not being able to
282 find anything (hardware or software) that supports these functions.'''
283 # TODO: Implement these functions.
285 # Mentioning what function it is is no problem.
286 function = self.data[1].data
288 20: 'Read File Record',
289 21: 'Write File Record',
290 24: 'Read FIFO Queue',
291 43: 'Read Device Identification/Encapsulated Interface Transport',
293 self.puti(1, 'function',
294 'Function {}: {} (not supported)'.format(function, functionname))
296 # From there on out we can keep marking it unsupported.
297 self.putl('data', 'This function is not currently supported')
299 class Modbus_ADU_SC(Modbus_ADU):
300 '''SC stands for Server -> Client.'''
302 '''Select which specific Modbus function we should parse.'''
305 # This try-catch is being used as flow control.
307 server_id = data[0].data
308 if 1 <= server_id <= 247:
309 message = 'Slave ID: {}'.format(server_id)
311 message = 'Slave ID {} is invalid'
312 self.puti(0, 'server-id', message)
314 function = data[1].data
315 if function == 1 or function == 2:
316 self.parse_read_bits()
317 elif function == 3 or function == 4 or function == 23:
318 self.parse_read_registers()
320 self.parse_write_single_coil()
322 self.parse_write_single_register()
324 self.parse_read_exception_status()
326 self.parse_diagnostics()
328 self.parse_get_comm_event_counter()
330 self.parse_get_comm_event_log()
331 elif function == 15 or function == 16:
332 self.parse_write_multiple()
334 self.parse_report_server_id()
336 self.parse_mask_write_register()
337 elif function in {21, 21, 24, 43}:
338 self.parse_not_implemented()
339 elif function > 0x80:
342 self.puti(1, 'error',
343 'Unknown function: {}'.format(data[1].data))
344 self.putl('error', 'Unknown function')
346 # If the message gets here without raising an exception, the
347 # message goes on longer than it should.
348 self.putl('error', 'Message too long')
351 # Just a message saying we don't need to parse anymore this round.
354 def parse_read_bits(self):
355 self.mimumum_length = 5
358 function = data[1].data
361 self.puti(1, 'function', 'Function 1: Read Coils')
363 self.puti(1, 'function', 'Function 2: Read Discrete Inputs')
365 bytecount = self.data[2].data
366 self.minimum_length = 5 + bytecount # 3 before data, 2 CRC.
367 self.puti(2, 'length', 'Byte count: {}'.format(bytecount))
369 # From here on out, we expect registers on 3 and 4, 5 and 6 etc.
370 # So registers never start when the length is even.
371 self.putl('data', '{:08b}', bytecount + 2)
372 self.check_crc(bytecount + 4)
374 def parse_read_registers(self):
375 self.mimumum_length = 5
379 function = data[1].data
381 self.puti(1, 'function', 'Function 3: Read Holding Registers')
383 self.puti(1, 'function', 'Function 4: Read Input Registers')
385 self.puti(1, 'function', 'Function 23: Read/Write Multiple Registers')
387 bytecount = self.data[2].data
388 self.minimum_length = 5 + bytecount # 3 before data, 2 CRC.
389 if bytecount % 2 == 0:
390 self.puti(2, 'length', 'Byte count: {}'.format(bytecount))
392 self.puti(2, 'error',
393 'Error: Odd byte count ({})'.format(bytecount))
395 # From here on out, we expect registers on 3 and 4, 5 and 6 etc.
396 # So registers never start when the length is even.
397 if len(data) % 2 == 1:
398 register_value = self.half_word(-2)
399 self.putl('data', '0x{0:04X} / {0}'.format(register_value),
404 self.check_crc(bytecount + 4)
406 def parse_read_exception_status(self):
407 self.mimumum_length = 5
409 self.puti(1, 'function', 'Function 7: Read Exception Status')
410 exception_status = self.data[2].data
412 'Exception status: {:08b}'.format(exception_status))
415 def parse_get_comm_event_counter(self):
416 self.mimumum_length = 8
418 self.puti(1, 'function', 'Function 11: Get Comm Event Counter')
420 status = self.half_word(2)
422 self.puti(3, 'data', 'Status: not busy')
423 elif status == 0xFFFF:
424 self.puti(3, 'data', 'Status: busy')
426 self.puti(3, 'error', 'Bad status: 0x{:04X}'.format(status))
428 count = self.half_word(4)
429 self.puti(5, 'data', 'Event Count: {}'.format(count))
432 def parse_get_comm_event_log(self):
433 self.mimumum_length = 11
434 self.puti(1, 'function', 'Function 12: Get Comm Event Log')
438 bytecount = data[2].data
439 self.puti(2, 'length', 'Bytecount: {}'.format(bytecount))
440 # The bytecount is the length of everything except the slaveID,
441 # function code, bytecount and CRC.
442 self.mimumum_length = 5 + bytecount
444 status = self.half_word(3)
446 self.puti(4, 'data', 'Status: not busy')
447 elif status == 0xFFFF:
448 self.puti(4, 'data', 'Status: busy')
450 self.puti(4, 'error', 'Bad status: 0x{:04X}'.format(status))
452 event_count = self.half_word(5)
453 self.puti(6, 'data', 'Event Count: {}'.format(event_count))
455 message_count = self.half_word(7)
456 self.puti(8, 'data', 'Message Count: {}'.format(message_count))
458 self.putl('data', 'Event: 0x{:02X}'.format(data[-1].data),
461 self.check_crc(bytecount + 4)
463 def parse_write_multiple(self):
464 '''Function 15 and 16 are almost the same, so we can parse them both
465 using one function.'''
466 self.mimumum_length = 8
468 function = self.data[1].data
472 long_address_offset = 10001
474 data_unit = 'Registers'
476 long_address_offset = 30001
478 self.puti(1, 'function',
479 'Function {}: Write Multiple {}'.format(function, data_unit))
481 starting_address = self.half_word(2)
482 # Some instruction manuals use a long form name for addresses, this is
483 # listed here for convienience.
484 address_name = long_address_offset + starting_address
485 self.puti(3, 'address',
486 'Start at address 0x{:X} / {:d}'.format(starting_address,
489 quantity_of_outputs = self.half_word(4)
490 if quantity_of_outputs <= max_outputs:
492 'Write {} {}'.format(quantity_of_outputs, data_unit))
494 self.puti(5, 'error',
495 'Bad value: {} {}. Max is {}'.format(quantity_of_outputs,
496 data_unit, max_outputs))
500 def parse_report_server_id(self):
501 # Buildup of this function:
503 # 1 byte function (17)
505 # 1 byte serverID (counts for bytecount)
506 # 1 byte Run Indicator Status (counts for bytecount)
507 # bytecount - 2 bytes of device specific data (counts for bytecount)
509 self.mimumum_length = 7
511 self.puti(1, 'function', 'Function 17: Report Server ID')
513 bytecount = data[2].data
514 self.puti(2, 'length', 'Data is {} bytes long'.format(bytecount))
516 self.puti(3, 'data', 'serverID: {}'.format(data[3].data))
518 run_indicator_status = data[4].data
519 if run_indicator_status == 0x00:
520 self.puti(4, 'data', 'Run Indicator status: Off')
521 elif run_indicator_status == 0xFF:
522 self.puti(4, 'data', 'Run Indicator status: On')
524 self.puti(4, 'error',
525 'Bad Run Indicator status: 0x{:X}'.format(run_indicator_status))
527 self.putl('data', 'Device specific data: {}, "{}"'.format(data[-1].data,
528 chr(data[-1].data)), 2 + bytecount)
530 self.check_crc(4 + bytecount)
532 def parse_error(self):
533 '''Parse a Modbus error message.'''
534 self.mimumum_length = 5
535 # The function code of an error is always 0x80 above the function call
537 functioncode = self.data[1].data - 0x80
541 2: 'Read Discrete Inputs',
542 3: 'Read Holding Registers',
543 4: 'Read Input Registers',
544 5: 'Write Single Coil',
545 6: 'Write Single Register',
546 7: 'Read Exception Status',
548 11: 'Get Com Event Counter',
549 12: 'Get Com Event Log',
550 15: 'Write Multiple Coils',
551 16: 'Write Multiple Registers',
552 17: 'Report Slave ID',
553 20: 'Read File Record',
554 21: 'Write File Record',
555 22: 'Mask Write Register',
556 23: 'Read/Write Multiple Registers',
557 24: 'Read FIFO Queue',
558 43: 'Read Device Identification/Encapsulated Interface Transport',
560 functionname = '{}: {}'.format(functioncode,
561 functions.get(functioncode, 'Unknown function'))
562 self.puti(1, 'function',
563 'Error for function {}'.format(functionname))
565 error = self.data[2].data
567 1: 'Illegal Function',
568 2: 'Illegal Data Address',
569 3: 'Illegal Data Value',
570 4: 'Slave Device Failure',
572 6: 'Slave Device Busy',
573 8: 'Memory Parity Error',
574 10: 'Gateway Path Unavailable',
575 11: 'Gateway Target Device failed to respond',
577 errorname = '{}: {}'.format(error, errorcodes.get(error, 'Unknown'))
578 self.puti(2, 'data', 'Error {}'.format(errorname))
581 class Modbus_ADU_CS(Modbus_ADU):
582 '''CS stands for Client -> Server.'''
584 '''Select which specific Modbus function we should parse.'''
587 # This try-catch is being used as flow control.
589 server_id = data[0].data
592 message = 'Broadcast message'
593 elif 1 <= server_id <= 247:
594 message = 'Slave ID: {}'.format(server_id)
595 elif 248 <= server_id <= 255:
596 message = 'Slave ID: {} (reserved address)'.format(server_id)
597 self.puti(0, 'server-id', message)
599 function = data[1].data
600 if function >= 1 and function <= 4:
601 self.parse_read_data_command()
603 self.parse_write_single_coil()
605 self.parse_write_single_register()
606 if function in {7, 11, 12, 17}:
607 self.parse_single_byte_request()
609 self.parse_diagnostics()
610 if function in {15, 16}:
611 self.parse_write_multiple()
613 self.parse_mask_write_register()
615 self.parse_read_write_registers()
616 elif function in {21, 21, 24, 43}:
617 self.parse_not_implemented()
619 self.puti(1, 'error',
620 'Unknown function: {}'.format(data[1].data))
621 self.putl('error', 'Unknown function')
623 # If the message gets here without raising an exception, the
624 # message goes on longer than it should.
625 self.putl('error', 'Message too long')
628 # Just a message saying we don't need to parse anymore this round.
631 def parse_read_data_command(self):
632 '''Interpret a command to read x units of data starting at address, ie
633 functions 1, 2, 3 and 4, and write the result to the annotations.'''
635 self.minimum_length = 8
637 function = data[1].data
638 functionname = {1: 'Read Coils',
639 2: 'Read Discrete Inputs',
640 3: 'Read Holding Registers',
641 4: 'Read Input Registers',
644 self.puti(1, 'function',
645 'Function {}: {}'.format(function, functionname))
647 starting_address = self.half_word(2)
648 # Some instruction manuals use a long form name for addresses, this is
649 # listed here for convienience.
650 # Example: holding register 60 becomes 30061.
651 address_name = 10000 * function + 1 + starting_address
652 self.puti(3, 'address',
653 'Start at address 0x{:X} / {:d}'.format(starting_address,
656 self.puti(5, 'length',
657 'Read {:d} units of data'.format(self.half_word(4)))
660 def parse_single_byte_request(self):
661 '''Some Modbus functions have no arguments, this parses those.'''
662 function = self.data[1].data
663 function_name = {7: 'Read Exception Status',
664 11: 'Get Comm Event Counter',
665 12: 'Get Comm Event Log',
666 17: 'Report Slave ID',
668 self.puti(1, 'function',
669 'Function {}: {}'.format(function, function_name))
673 def parse_write_multiple(self):
674 '''Function 15 and 16 are almost the same, so we can parse them both
675 using one function.'''
676 self.mimumum_length = 9
678 function = self.data[1].data
682 ratio_bytes_data = 1/8
683 long_address_offset = 10001
685 data_unit = 'Registers'
688 long_address_offset = 30001
690 self.puti(1, 'function',
691 'Function {}: Write Multiple {}'.format(function, data_unit))
693 starting_address = self.half_word(2)
694 # Some instruction manuals use a long form name for addresses, this is
695 # listed here for convienience.
696 address_name = long_address_offset + starting_address
697 self.puti(3, 'address',
698 'Start at address 0x{:X} / {:d}'.format(starting_address,
701 quantity_of_outputs = self.half_word(4)
702 if quantity_of_outputs <= max_outputs:
703 self.puti(5, 'length',
704 'Write {} {}'.format(quantity_of_outputs, data_unit))
706 self.puti(5, 'error',
707 'Bad value: {} {}. Max is {}'.format(quantity_of_outputs,
708 data_unit, max_outputs))
709 proper_bytecount = ceil(quantity_of_outputs * ratio_bytes_data)
711 bytecount = self.data[6].data
712 if bytecount == proper_bytecount:
713 self.puti(6, 'length', 'Byte count: {}'.format(bytecount))
715 self.puti(6, 'error',
716 'Bad byte count, is {}, should be {}'.format(bytecount,
718 self.mimumum_length = bytecount + 9
720 self.putl('data', 'Value 0x{:X}', 6 + bytecount)
722 self.check_crc(bytecount + 8)
724 def parse_read_file_record(self):
725 self.puti(1, 'function', 'Function 20: Read file records')
729 bytecount = data[2].data
731 self.minimum_length = 5 + bytecount
732 # 1 for serverID, 1 for function, 1 for bytecount, 2 for CRC.
734 if 0x07 <= bytecount <= 0xF5:
735 self.puti(2, 'length', 'Request is {} bytes long'.format(bytecount))
737 self.puti(2, 'error',
738 'Request claims to be {} bytes long, legal values are between'
739 ' 7 and 247'.format(bytecount))
741 current_byte = len(data) - 1
742 # Function 20 is a number of sub-requests, the first starting at 3,
743 # the total length of the sub-requests is bytecount.
744 if current_byte <= bytecount + 2:
745 step = (current_byte - 3) % 7
747 if data[current_byte].data == 6:
748 self.puti(current_byte, 'data', 'Start sub-request')
750 self.puti(current_byte, 'error',
751 'First byte of subrequest should be 0x06')
755 file_number = self.half_word(current_byte - 1)
756 self.puti(current_byte, 'data',
757 'Read File number {}'.format(file_number))
761 record_number = self.half_word(current_byte - 1)
762 self.puti(current_byte, 'address',
763 'Read from record number {}'.format(record_number))
764 # TODO: Check if within range.
768 records_to_read = self.half_word(current_byte - 1)
769 self.puti(current_byte, 'length',
770 'Read {} records'.format(records_to_read))
771 self.check_crc(4 + bytecount)
773 def parse_read_write_registers(self):
774 '''Parse function 23: Read/Write multiple registers.'''
775 self.minimum_length = 13
777 self.puti(1, 'function', 'Function 23: Read/Write Multiple Registers')
779 starting_address = self.half_word(2)
780 # Some instruction manuals use a long form name for addresses, this is
781 # listed here for convienience.
782 # Example: holding register 60 becomes 30061.
783 address_name = 30001 + starting_address
784 self.puti(3, 'address',
785 'Read starting at address 0x{:X} / {:d}'.format(starting_address,
788 self.puti(5, 'length', 'Read {:d} units of data'.format(self.half_word(4)))
790 starting_address = self.half_word(6)
791 self.puti(7, 'address',
792 'Write starting at address 0x{:X} / {:d}'.format(starting_address,
795 quantity_of_outputs = self.half_word(8)
796 self.puti(9, 'length',
797 'Write {} registers'.format(quantity_of_outputs))
798 proper_bytecount = quantity_of_outputs * 2
800 bytecount = self.data[10].data
801 if bytecount == proper_bytecount:
802 self.puti(10, 'length', 'Byte count: {}'.format(bytecount))
804 self.puti(10, 'error',
805 'Bad byte count, is {}, should be {}'.format(bytecount,
807 self.mimumum_length = bytecount + 13
809 self.putl('data', 'Data, value 0x{:02X}', 10 + bytecount)
811 self.check_crc(bytecount + 12)
813 class Decoder(srd.Decoder):
817 longname = 'Modbus RTU over RS232/RS485'
818 desc = 'Modbus RTU protocol for industrial applications.'
823 ('sc-server-id', ''),
830 ('cs-server-id', ''),
837 ('error-indication', ''),
840 ('sc', 'Server->client', (0, 1, 2, 3, 4, 5, 6)),
841 ('cs', 'Client->server', (7, 8, 9, 10, 11, 12, 13)),
842 ('error-indicator', 'Errors in frame', (14,)),
845 {'id': 'channel', 'desc': 'Server -> client channel', 'default': 'RX',
846 'values': ('RX', 'TX')},
850 self.ADUSc = None # Start off with empty slave -> client ADU.
851 self.ADUCs = None # Start off with empty client -> slave ADU.
853 # The reason we have both (despite not supporting full duplex comms) is
854 # because we want to be able to decode the message as both client ->
855 # server and server -> client, and let the user see which of the two
858 self.bitlength = None # We will later test how long a bit is.
861 self.out_ann = self.register(srd.OUTPUT_ANN)
863 def puta(self, start, end, ann_str, message):
864 '''Put an annotation from start to end, with ann as a
865 string. This means you don't have to know the ann's
866 number to write annotations to it.'''
867 ann = [s[0] for s in self.annotations].index(ann_str)
868 self.put(start, end, self.out_ann, [ann, [message]])
870 def decode_adu(self, ss, es, data, direction):
871 '''Decode the next byte or bit (depending on type) in the ADU.
872 ss: Start time of the data
873 es: End time of the data
874 data: Data as passed from the UART decoder
875 direction: Is this data for the Cs (client -> server) or Sc (server ->
876 client) being decoded right now?'''
877 ptype, rxtx, pdata = data
879 # We don't have a nice way to get the baud rate from UART, so we have
880 # to figure out how long a bit lasts. We do this by looking at the
881 # length of (probably) the startbit.
882 if self.bitlength is None:
883 if ptype == 'STARTBIT' or ptype == 'STOPBIT':
884 self.bitlength = es - ss
886 # If we don't know the bitlength yet, we can't start decoding.
889 # Select the ADU, create the ADU if needed.
890 # We set ADU.startNewFrame = True when we know the old one is over.
891 if direction == 'Sc':
892 if (self.ADUSc is None) or self.ADUSc.startNewFrame:
893 self.ADUSc = Modbus_ADU_SC(self, ss, TX, 'sc-')
895 if direction == 'Cs':
896 if self.ADUCs is None or self.ADUCs.startNewFrame:
897 self.ADUCs = Modbus_ADU_CS(self, ss, TX, 'cs-')
900 # We need to determine if the last ADU is over.
901 # According to the Modbus spec, there should be 3.5 characters worth of
902 # space between each message. But if within a message there is a length
903 # of more than 1.5 character, that's an error. For our purposes
904 # somewhere between seems fine.
905 # A character is 11 bits long, so (3.5 + 1.5)/2 * 11 ~= 28
906 # TODO: Display error for too short or too long.
907 if (ss - ADU.last_read) <= self.bitlength * 28:
908 ADU.add_data(ss, es, data)
910 # It's been too long since the last part of the ADU!
911 # If there is any data in the ADU we need to show it to the user
912 if len(ADU.data) > 0:
913 # Extend errors for 3 bits after last byte, we can guarantee
915 ADU.close(ADU.data[-1].end + self.bitlength * 3)
917 ADU.startNewFrame = True
918 # Restart this function, it will make a new ADU for us.
919 self.decode_adu(ss, es, data, direction)
921 def decode(self, ss, es, data):
922 ptype, rxtx, pdata = data
924 # Decide what ADU(s) we need this packet to go to.
925 # Note that it's possible to go to both ADUs.
927 self.decode_adu(ss, es, data, 'Cs')
928 if rxtx == TX and self.options['channel'] == 'TX':
929 self.decode_adu(ss, es, data, 'Sc')
930 if rxtx == RX and self.options['channel'] == 'RX':
931 self.decode_adu(ss, es, data, 'Sc')