21 # |
21 # |
22 # This code is made available on the understanding that it will not be |
22 # This code is made available on the understanding that it will not be |
23 # used in safety-critical situations without a full and competent review. |
23 # used in safety-critical situations without a full and competent review. |
24 |
24 |
25 |
25 |
|
26 from __future__ import absolute_import |
26 import os |
27 import os |
27 import sys |
28 from modbus.mb_utils import * |
28 from mb_utils import * |
29 |
29 |
|
30 import wx |
|
31 from ConfigTreeNode import ConfigTreeNode |
30 from ConfigTreeNode import ConfigTreeNode |
32 from PLCControler import LOCATION_CONFNODE, LOCATION_MODULE, LOCATION_GROUP, LOCATION_VAR_INPUT, LOCATION_VAR_OUTPUT, LOCATION_VAR_MEMORY |
31 from PLCControler import LOCATION_CONFNODE, LOCATION_VAR_MEMORY |
33 |
32 |
34 base_folder = os.path.split(os.path.dirname(os.path.realpath(__file__)))[0] |
33 base_folder = os.path.split(os.path.dirname(os.path.realpath(__file__)))[0] |
35 base_folder = os.path.join(base_folder, "..") |
34 base_folder = os.path.join(base_folder, "..") |
36 ModbusPath = os.path.join(base_folder, "Modbus") |
35 ModbusPath = os.path.join(base_folder, "Modbus") |
37 |
36 |
43 # |
42 # |
44 # |
43 # |
45 # |
44 # |
46 |
45 |
47 |
46 |
48 class _RequestPlug: |
47 class _RequestPlug(object): |
49 XSD = """<?xml version="1.0" encoding="ISO-8859-1" ?> |
48 XSD = """<?xml version="1.0" encoding="ISO-8859-1" ?> |
50 <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"> |
49 <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"> |
51 <xsd:element name="ModbusRequest"> |
50 <xsd:element name="ModbusRequest"> |
52 <xsd:complexType> |
51 <xsd:complexType> |
53 <xsd:attribute name="Function" type="xsd:string" use="optional" default="01 - Read Coils"/> |
52 <xsd:attribute name="Function" type="xsd:string" use="optional" default="01 - Read Coils"/> |
108 # 'BOOL' or 'WORD' |
107 # 'BOOL' or 'WORD' |
109 datatype = modbus_function_dict[function][3] |
108 datatype = modbus_function_dict[function][3] |
110 # 1 or 16 |
109 # 1 or 16 |
111 datasize = modbus_function_dict[function][4] |
110 datasize = modbus_function_dict[function][4] |
112 # 'Q' for coils and holding registers, 'I' for input discretes and input registers |
111 # 'Q' for coils and holding registers, 'I' for input discretes and input registers |
113 datazone = modbus_function_dict[function][5] |
112 # datazone = modbus_function_dict[function][5] |
114 # 'X' for bits, 'W' for words |
113 # 'X' for bits, 'W' for words |
115 datatacc = modbus_function_dict[function][6] |
114 datatacc = modbus_function_dict[function][6] |
116 # 'Coil', 'Holding Register', 'Input Discrete' or 'Input Register' |
115 # 'Coil', 'Holding Register', 'Input Discrete' or 'Input Register' |
117 dataname = modbus_function_dict[function][7] |
116 dataname = modbus_function_dict[function][7] |
118 entries = [] |
117 entries = [] |
164 "03 - Holding Registers": ('3', 'rw_words', 65536, "WORD", 16, "Q", "W", "Holding Register"), |
163 "03 - Holding Registers": ('3', 'rw_words', 65536, "WORD", 16, "Q", "W", "Holding Register"), |
165 "04 - Input Registers": ('4', 'ro_words', 65536, "WORD", 16, "I", "W", "Input Register"), |
164 "04 - Input Registers": ('4', 'ro_words', 65536, "WORD", 16, "I", "W", "Input Register"), |
166 } |
165 } |
167 |
166 |
168 |
167 |
169 class _MemoryAreaPlug: |
168 class _MemoryAreaPlug(object): |
170 XSD = """<?xml version="1.0" encoding="ISO-8859-1" ?> |
169 XSD = """<?xml version="1.0" encoding="ISO-8859-1" ?> |
171 <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"> |
170 <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"> |
172 <xsd:element name="MemoryArea"> |
171 <xsd:element name="MemoryArea"> |
173 <xsd:complexType> |
172 <xsd:complexType> |
174 <xsd:attribute name="MemoryAreaType" type="xsd:string" use="optional" default="01 - Coils"/> |
173 <xsd:attribute name="MemoryAreaType" type="xsd:string" use="optional" default="01 - Coils"/> |
213 # 'BOOL' or 'WORD' |
212 # 'BOOL' or 'WORD' |
214 datatype = modbus_memtype_dict[function][3] |
213 datatype = modbus_memtype_dict[function][3] |
215 # 1 or 16 |
214 # 1 or 16 |
216 datasize = modbus_memtype_dict[function][4] |
215 datasize = modbus_memtype_dict[function][4] |
217 # 'Q' for coils and holding registers, 'I' for input discretes and input registers |
216 # 'Q' for coils and holding registers, 'I' for input discretes and input registers |
218 datazone = modbus_memtype_dict[function][5] |
217 # datazone = modbus_memtype_dict[function][5] |
219 # 'X' for bits, 'W' for words |
218 # 'X' for bits, 'W' for words |
220 datatacc = modbus_memtype_dict[function][6] |
219 datatacc = modbus_memtype_dict[function][6] |
221 # 'Coil', 'Holding Register', 'Input Discrete' or 'Input Register' |
220 # 'Coil', 'Holding Register', 'Input Discrete' or 'Input Register' |
222 dataname = modbus_memtype_dict[function][7] |
221 dataname = modbus_memtype_dict[function][7] |
223 entries = [] |
222 entries = [] |
258 # T C P C L I E N T # |
257 # T C P C L I E N T # |
259 # |
258 # |
260 # |
259 # |
261 # |
260 # |
262 |
261 |
263 class _ModbusTCPclientPlug: |
262 class _ModbusTCPclientPlug(object): |
264 XSD = """<?xml version="1.0" encoding="ISO-8859-1" ?> |
263 XSD = """<?xml version="1.0" encoding="ISO-8859-1" ?> |
265 <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"> |
264 <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"> |
266 <xsd:element name="ModbusTCPclient"> |
265 <xsd:element name="ModbusTCPclient"> |
267 <xsd:complexType> |
266 <xsd:complexType> |
268 <xsd:attribute name="Remote_IP_Address" type="xsd:string" use="optional" default="localhost"/> |
267 <xsd:attribute name="Remote_IP_Address" type="xsd:string" use="optional" default="localhost"/> |
312 # T C P S E R V E R # |
311 # T C P S E R V E R # |
313 # |
312 # |
314 # |
313 # |
315 # |
314 # |
316 |
315 |
317 class _ModbusTCPserverPlug: |
316 class _ModbusTCPserverPlug(object): |
318 # NOTE: the Port number is a 'string' and not an 'integer'! |
317 # NOTE: the Port number is a 'string' and not an 'integer'! |
319 # This is because the underlying modbus library accepts strings |
318 # This is because the underlying modbus library accepts strings |
320 # (e.g.: well known port names!) |
319 # (e.g.: well known port names!) |
321 XSD = """<?xml version="1.0" encoding="ISO-8859-1" ?> |
320 XSD = """<?xml version="1.0" encoding="ISO-8859-1" ?> |
322 <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"> |
321 <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"> |
374 # R T U C L I E N T # |
373 # R T U C L I E N T # |
375 # |
374 # |
376 # |
375 # |
377 # |
376 # |
378 |
377 |
379 class _ModbusRTUclientPlug: |
378 class _ModbusRTUclientPlug(object): |
380 XSD = """<?xml version="1.0" encoding="ISO-8859-1" ?> |
379 XSD = """<?xml version="1.0" encoding="ISO-8859-1" ?> |
381 <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"> |
380 <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"> |
382 <xsd:element name="ModbusRTUclient"> |
381 <xsd:element name="ModbusRTUclient"> |
383 <xsd:complexType> |
382 <xsd:complexType> |
384 <xsd:attribute name="Serial_Port" type="xsd:string" use="optional" default="/dev/ttyS0"/> |
383 <xsd:attribute name="Serial_Port" type="xsd:string" use="optional" default="/dev/ttyS0"/> |
444 # |
443 # |
445 # |
444 # |
446 # |
445 # |
447 |
446 |
448 |
447 |
449 class _ModbusRTUslavePlug: |
448 class _ModbusRTUslavePlug(object): |
450 XSD = """<?xml version="1.0" encoding="ISO-8859-1" ?> |
449 XSD = """<?xml version="1.0" encoding="ISO-8859-1" ?> |
451 <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"> |
450 <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"> |
452 <xsd:element name="ModbusRTUslave"> |
451 <xsd:element name="ModbusRTUslave"> |
453 <xsd:complexType> |
452 <xsd:complexType> |
454 <xsd:attribute name="Serial_Port" type="xsd:string" use="optional" default="/dev/ttyS0"/> |
453 <xsd:attribute name="Serial_Port" type="xsd:string" use="optional" default="/dev/ttyS0"/> |
514 # |
513 # |
515 # R O O T C L A S S # |
514 # R O O T C L A S S # |
516 # |
515 # |
517 # |
516 # |
518 # |
517 # |
519 class RootClass: |
518 class RootClass(object): |
520 XSD = """<?xml version="1.0" encoding="ISO-8859-1" ?> |
519 XSD = """<?xml version="1.0" encoding="ISO-8859-1" ?> |
521 <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"> |
520 <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"> |
522 <xsd:element name="ModbusRoot"> |
521 <xsd:element name="ModbusRoot"> |
523 <xsd:complexType> |
522 <xsd:complexType> |
524 <xsd:attribute name="MaxRemoteTCPclients" use="optional" default="10"> |
523 <xsd:attribute name="MaxRemoteTCPclients" use="optional" default="10"> |
531 </xsd:attribute> |
530 </xsd:attribute> |
532 </xsd:complexType> |
531 </xsd:complexType> |
533 </xsd:element> |
532 </xsd:element> |
534 </xsd:schema> |
533 </xsd:schema> |
535 """ |
534 """ |
536 CTNChildrenTypes = [("ModbusTCPclient", _ModbusTCPclientPlug, "Modbus TCP Client"), ("ModbusTCPserver", _ModbusTCPserverPlug, "Modbus TCP Server"), ("ModbusRTUclient", _ModbusRTUclientPlug, "Modbus RTU Client"), ("ModbusRTUslave", _ModbusRTUslavePlug, "Modbus RTU Slave") |
535 CTNChildrenTypes = [("ModbusTCPclient", _ModbusTCPclientPlug, "Modbus TCP Client"), |
537 ] |
536 ("ModbusTCPserver", _ModbusTCPserverPlug, "Modbus TCP Server"), |
|
537 ("ModbusRTUclient", _ModbusRTUclientPlug, "Modbus RTU Client"), |
|
538 ("ModbusRTUslave", _ModbusRTUslavePlug, "Modbus RTU Slave")] |
538 |
539 |
539 # Return the number of (modbus library) nodes this specific instance of the modbus plugin will need |
540 # Return the number of (modbus library) nodes this specific instance of the modbus plugin will need |
540 # return type: (tcp nodes, rtu nodes, ascii nodes) |
541 # return type: (tcp nodes, rtu nodes, ascii nodes) |
541 def GetNodeCount(self): |
542 def GetNodeCount(self): |
542 max_remote_tcpclient = self.GetParamsAttributes()[ |
543 max_remote_tcpclient = self.GetParamsAttributes()[ |
565 # print self.CTNType |
566 # print self.CTNType |
566 # print "type(self.CTNType) >>>" |
567 # print "type(self.CTNType) >>>" |
567 # print type(self.CTNType) |
568 # print type(self.CTNType) |
568 # print "#############" |
569 # print "#############" |
569 |
570 |
570 loc_dict = {"locstr": "_".join(map(str, self.GetCurrentLocation())), |
571 loc_dict = {"locstr": "_".join(map(str, self.GetCurrentLocation()))} |
571 } |
|
572 |
572 |
573 # Determine the number of (modbus library) nodes ALL instances of the modbus plugin will need |
573 # Determine the number of (modbus library) nodes ALL instances of the modbus plugin will need |
574 # total_node_count: (tcp nodes, rtu nodes, ascii nodes) |
574 # total_node_count: (tcp nodes, rtu nodes, ascii nodes) |
575 # Also get a list with tuples of (location, IP port numbers) used by all the Modbus/IP server nodes |
575 # Also get a list with tuples of (location, IP port numbers) used by all the Modbus/IP server nodes |
576 # This list is later used to search for duplicates in port numbers! |
576 # This list is later used to search for duplicates in port numbers! |
661 # 'ro_bits', 'rw_bits', 'ro_words' or 'rw_words' |
661 # 'ro_bits', 'rw_bits', 'ro_words' or 'rw_words' |
662 memarea = modbus_memtype_dict[function][1] |
662 memarea = modbus_memtype_dict[function][1] |
663 for iecvar in subchild.GetLocations(): |
663 for iecvar in subchild.GetLocations(): |
664 # print repr(iecvar) |
664 # print repr(iecvar) |
665 absloute_address = iecvar["LOC"][3] |
665 absloute_address = iecvar["LOC"][3] |
666 start_address = int(GetCTVal(child, 2)) |
666 start_address = int(GetCTVal(subchild, 2)) |
667 relative_addr = absloute_address - start_address |
667 relative_addr = absloute_address - start_address |
668 # test if relative address in request specified range |
668 # test if relative address in request specified range |
669 if relative_addr in xrange(int(GetCTVal(child, 1))): |
669 if relative_addr in xrange(int(GetCTVal(subchild, 1))): |
670 if str(iecvar["NAME"]) not in loc_vars_list: |
670 if str(iecvar["NAME"]) not in loc_vars_list: |
671 loc_vars.append("u16 *" + str(iecvar["NAME"]) + " = &server_nodes[%d].mem_area.%s[%d];" % ( |
671 loc_vars.append("u16 *" + str(iecvar["NAME"]) + " = &server_nodes[%d].mem_area.%s[%d];" % ( |
672 server_id, memarea, absloute_address)) |
672 server_id, memarea, absloute_address)) |
673 loc_vars_list.append(str(iecvar["NAME"])) |
673 loc_vars_list.append(str(iecvar["NAME"])) |
674 server_id += 1 |
674 server_id += 1 |
691 # 'ro_bits', 'rw_bits', 'ro_words' or 'rw_words' |
691 # 'ro_bits', 'rw_bits', 'ro_words' or 'rw_words' |
692 memarea = modbus_memtype_dict[function][1] |
692 memarea = modbus_memtype_dict[function][1] |
693 for iecvar in subchild.GetLocations(): |
693 for iecvar in subchild.GetLocations(): |
694 # print repr(iecvar) |
694 # print repr(iecvar) |
695 absloute_address = iecvar["LOC"][3] |
695 absloute_address = iecvar["LOC"][3] |
696 start_address = int(GetCTVal(child, 2)) |
696 start_address = int(GetCTVal(subchild, 2)) |
697 relative_addr = absloute_address - start_address |
697 relative_addr = absloute_address - start_address |
698 # test if relative address in request specified range |
698 # test if relative address in request specified range |
699 if relative_addr in xrange(int(GetCTVal(child, 1))): |
699 if relative_addr in xrange(int(GetCTVal(subchild, 1))): |
700 if str(iecvar["NAME"]) not in loc_vars_list: |
700 if str(iecvar["NAME"]) not in loc_vars_list: |
701 loc_vars.append("u16 *" + str(iecvar["NAME"]) + " = &server_nodes[%d].mem_area.%s[%d];" % ( |
701 loc_vars.append("u16 *" + str(iecvar["NAME"]) + " = &server_nodes[%d].mem_area.%s[%d];" % ( |
702 server_id, memarea, absloute_address)) |
702 server_id, memarea, absloute_address)) |
703 loc_vars_list.append(str(iecvar["NAME"])) |
703 loc_vars_list.append(str(iecvar["NAME"])) |
704 server_id += 1 |
704 server_id += 1 |
715 if new_req is None: |
715 if new_req is None: |
716 return [], "", False |
716 return [], "", False |
717 client_request_list.append(new_req) |
717 client_request_list.append(new_req) |
718 for iecvar in subchild.GetLocations(): |
718 for iecvar in subchild.GetLocations(): |
719 # absloute address - start address |
719 # absloute address - start address |
720 relative_addr = iecvar["LOC"][3] - int(GetCTVal(child, 3)) |
720 relative_addr = iecvar["LOC"][3] - int(GetCTVal(subchild, 3)) |
721 # test if relative address in request specified range |
721 # test if relative address in request specified range |
722 if relative_addr in xrange(int(GetCTVal(child, 2))): |
722 if relative_addr in xrange(int(GetCTVal(subchild, 2))): |
723 if str(iecvar["NAME"]) not in loc_vars_list: |
723 if str(iecvar["NAME"]) not in loc_vars_list: |
724 loc_vars.append( |
724 loc_vars.append( |
725 "u16 *" + str(iecvar["NAME"]) + " = &client_requests[%d].plcv_buffer[%d];" % (client_requestid, relative_addr)) |
725 "u16 *" + str(iecvar["NAME"]) + " = &client_requests[%d].plcv_buffer[%d];" % (client_requestid, relative_addr)) |
726 loc_vars_list.append(str(iecvar["NAME"])) |
726 loc_vars_list.append(str(iecvar["NAME"])) |
727 client_requestid += 1 |
727 client_requestid += 1 |
740 if new_req is None: |
740 if new_req is None: |
741 return [], "", False |
741 return [], "", False |
742 client_request_list.append(new_req) |
742 client_request_list.append(new_req) |
743 for iecvar in subchild.GetLocations(): |
743 for iecvar in subchild.GetLocations(): |
744 # absloute address - start address |
744 # absloute address - start address |
745 relative_addr = iecvar["LOC"][3] - int(GetCTVal(child, 3)) |
745 relative_addr = iecvar["LOC"][3] - int(GetCTVal(subchild, 3)) |
746 # test if relative address in request specified range |
746 # test if relative address in request specified range |
747 if relative_addr in xrange(int(GetCTVal(child, 2))): |
747 if relative_addr in xrange(int(GetCTVal(subchild, 2))): |
748 if str(iecvar["NAME"]) not in loc_vars_list: |
748 if str(iecvar["NAME"]) not in loc_vars_list: |
749 loc_vars.append( |
749 loc_vars.append( |
750 "u16 *" + str(iecvar["NAME"]) + " = &client_requests[%d].plcv_buffer[%d];" % (client_requestid, relative_addr)) |
750 "u16 *" + str(iecvar["NAME"]) + " = &client_requests[%d].plcv_buffer[%d];" % (client_requestid, relative_addr)) |
751 loc_vars_list.append(str(iecvar["NAME"])) |
751 loc_vars_list.append(str(iecvar["NAME"])) |
752 client_requestid += 1 |
752 client_requestid += 1 |