modbus/modbus.py
changeset 1912 8391c11477f4
parent 1909 bb883e063175
child 1913 338e2f51b685
equal deleted inserted replaced
1908:d0b1ffcb9368 1912:8391c11477f4
       
     1 #!/usr/bin/env python
       
     2 # -*- coding: utf-8 -*-
       
     3 
       
     4 # This file is part of Beremiz, a Integrated Development Environment for
       
     5 # programming IEC 61131-3 automates supporting plcopen standard and CanFestival.
       
     6 #
       
     7 # Copyright (c) 2016 Mario de Sousa (msousa@fe.up.pt)
       
     8 #
       
     9 # This program is free software: you can redistribute it and/or modify
       
    10 # it under the terms of the GNU General Public License as published by
       
    11 # the Free Software Foundation, either version 3 of the License, or
       
    12 # (at your option) any later version.
       
    13 #
       
    14 # This program is distributed in the hope that it will be useful,
       
    15 # but WITHOUT ANY WARRANTY; without even the implied warranty of
       
    16 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
       
    17 # GNU General Public License for more details.
       
    18 #
       
    19 # You should have received a copy of the GNU General Public License
       
    20 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
       
    21 #    
       
    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.
       
    24 
       
    25 
       
    26 
       
    27 
       
    28 import os, sys
       
    29 base_folder = os.path.split(os.path.dirname(os.path.realpath(__file__)))[0]
       
    30 base_folder = os.path.join(base_folder, "..")
       
    31 ModbusPath  = os.path.join(base_folder, "Modbus")
       
    32 
       
    33 from mb_utils import *
       
    34 
       
    35 import wx
       
    36 from ConfigTreeNode import ConfigTreeNode
       
    37 from PLCControler import LOCATION_CONFNODE, LOCATION_MODULE, LOCATION_GROUP, LOCATION_VAR_INPUT, LOCATION_VAR_OUTPUT, LOCATION_VAR_MEMORY
       
    38 
       
    39 
       
    40 
       
    41 ###################################################
       
    42 ###################################################
       
    43 #                                                 #
       
    44 #         C L I E N T    R E Q U E S T            # 
       
    45 #                                                 #
       
    46 ###################################################
       
    47 ###################################################
       
    48 
       
    49 
       
    50 class _RequestPlug:
       
    51     XSD = """<?xml version="1.0" encoding="ISO-8859-1" ?>
       
    52     <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
       
    53       <xsd:element name="ModbusRequest">
       
    54         <xsd:complexType>
       
    55           <xsd:attribute name="Function" type="xsd:string" use="optional" default="01 - Read Coils"/>
       
    56           <xsd:attribute name="SlaveID" use="optional" default="1">
       
    57             <xsd:simpleType>
       
    58                 <xsd:restriction base="xsd:integer">
       
    59                     <xsd:minInclusive value="0"/>
       
    60                     <xsd:maxInclusive value="255"/>
       
    61                 </xsd:restriction>
       
    62             </xsd:simpleType>
       
    63           </xsd:attribute>
       
    64           <xsd:attribute name="Nr_of_Channels" use="optional" default="1">
       
    65             <xsd:simpleType>
       
    66                 <xsd:restriction base="xsd:integer">
       
    67                     <xsd:minInclusive value="1"/>
       
    68                     <xsd:maxInclusive value="2000"/>
       
    69                 </xsd:restriction>
       
    70             </xsd:simpleType>
       
    71           </xsd:attribute>
       
    72           <xsd:attribute name="Start_Address" use="optional" default="0">
       
    73             <xsd:simpleType>
       
    74                 <xsd:restriction base="xsd:integer">
       
    75                     <xsd:minInclusive value="0"/>
       
    76                     <xsd:maxInclusive value="65535"/>
       
    77                 </xsd:restriction>
       
    78             </xsd:simpleType>
       
    79           </xsd:attribute>
       
    80           <xsd:attribute name="Timeout_in_ms" use="optional" default="10">
       
    81             <xsd:simpleType>
       
    82                 <xsd:restriction base="xsd:integer">
       
    83                     <xsd:minInclusive value="1"/>
       
    84                     <xsd:maxInclusive value="100000"/>
       
    85                 </xsd:restriction>
       
    86             </xsd:simpleType>
       
    87           </xsd:attribute>
       
    88         </xsd:complexType>
       
    89       </xsd:element>
       
    90     </xsd:schema>
       
    91     """
       
    92 
       
    93     def GetParamsAttributes(self, path = None):
       
    94         infos = ConfigTreeNode.GetParamsAttributes(self, path = path)
       
    95         for element in infos:
       
    96             if element["name"] == "ModbusRequest":
       
    97                 for child in element["children"]:
       
    98                     if child["name"] == "Function":
       
    99                         list = modbus_function_dict.keys()
       
   100                         list.sort()
       
   101                         child["type"] = list
       
   102         return infos
       
   103       
       
   104     def GetVariableLocationTree(self):
       
   105         current_location = self.GetCurrentLocation()
       
   106         name = self.BaseParams.getName()
       
   107         address = self.GetParamsAttributes()[0]["children"][3]["value"]
       
   108         count   = self.GetParamsAttributes()[0]["children"][2]["value"]
       
   109         function= self.GetParamsAttributes()[0]["children"][0]["value"]
       
   110         # 'BOOL' or 'WORD'
       
   111         datatype= modbus_function_dict[function][3]
       
   112         # 1 or 16
       
   113         datasize= modbus_function_dict[function][4]
       
   114         # 'Q' for coils and holding registers, 'I' for input discretes and input registers
       
   115         datazone= modbus_function_dict[function][5] 
       
   116         # 'X' for bits, 'W' for words
       
   117         datatacc= modbus_function_dict[function][6] 
       
   118         # 'Coil', 'Holding Register', 'Input Discrete' or 'Input Register'
       
   119         dataname= modbus_function_dict[function][7] 
       
   120         entries = []
       
   121         for offset in range(address, address+count):
       
   122             entries.append({
       
   123                 "name": dataname + " " + str(offset),
       
   124                 "type": LOCATION_VAR_MEMORY,
       
   125                 "size": datasize,
       
   126                 "IEC_type": datatype,
       
   127                 "var_name": "var_name",
       
   128                 "location": datatacc + ".".join([str(i) for i in current_location]) + "." + str(offset),
       
   129                 "description": "description",
       
   130                 "children": []})
       
   131         return  {"name": name,
       
   132                  "type": LOCATION_CONFNODE,
       
   133                  "location": ".".join([str(i) for i in current_location]) + ".x",
       
   134                  "children": entries}
       
   135 
       
   136 
       
   137 
       
   138 
       
   139 
       
   140     def CTNGenerate_C(self, buildpath, locations):
       
   141         """
       
   142         Generate C code
       
   143         @param current_location: Tupple containing plugin IEC location : %I0.0.4.5 => (0,0,4,5)
       
   144         @param locations: List of complete variables locations \
       
   145             [{"IEC_TYPE" : the IEC type (i.e. "INT", "STRING", ...)
       
   146             "NAME" : name of the variable (generally "__IW0_1_2" style)
       
   147             "DIR" : direction "Q","I" or "M"
       
   148             "SIZE" : size "X", "B", "W", "D", "L"
       
   149             "LOC" : tuple of interger for IEC location (0,1,2,...)
       
   150             }, ...]
       
   151         @return: [(C_file_name, CFLAGS),...] , LDFLAGS_TO_APPEND
       
   152         """
       
   153         return [], "", False
       
   154         
       
   155         
       
   156 ###################################################
       
   157 ###################################################
       
   158 #                                                 #
       
   159 #     S E R V E R    M E M O R Y    A R E A       # 
       
   160 #                                                 #
       
   161 ###################################################
       
   162 ###################################################
       
   163 
       
   164 #dictionary implementing:
       
   165 #key - string with the description we want in the request plugin GUI
       
   166 #list - (modbus function number, request type, max count value)
       
   167 modbus_memtype_dict = {"01 - Coils" :            ( '1', 'rw_bits',  65536, "BOOL", 1  , "Q", "X", "Coil"),
       
   168                        "02 - Input Discretes" :  ( '2', 'ro_bits',  65536, "BOOL", 1  , "I", "X", "Input Discrete"),
       
   169                        "03 - Holding Registers" :( '3', 'rw_words', 65536, "WORD", 16 , "Q", "W", "Holding Register"),
       
   170                        "04 - Input Registers" :  ( '4', 'ro_words', 65536, "WORD", 16 , "I", "W", "Input Register"),
       
   171                        }
       
   172 
       
   173 class _MemoryAreaPlug:
       
   174     XSD = """<?xml version="1.0" encoding="ISO-8859-1" ?>
       
   175     <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
       
   176       <xsd:element name="MemoryArea">
       
   177         <xsd:complexType>
       
   178           <xsd:attribute name="MemoryAreaType" type="xsd:string" use="optional" default="01 - Coils"/>
       
   179           <xsd:attribute name="Nr_of_Channels" use="optional" default="1">
       
   180             <xsd:simpleType>
       
   181                 <xsd:restriction base="xsd:integer">
       
   182                     <xsd:minInclusive value="1"/>
       
   183                     <xsd:maxInclusive value="65536"/>
       
   184                 </xsd:restriction>
       
   185             </xsd:simpleType>
       
   186           </xsd:attribute>
       
   187           <xsd:attribute name="Start_Address" use="optional" default="0">
       
   188             <xsd:simpleType>
       
   189                 <xsd:restriction base="xsd:integer">
       
   190                     <xsd:minInclusive value="0"/>
       
   191                     <xsd:maxInclusive value="65535"/>
       
   192                 </xsd:restriction>
       
   193             </xsd:simpleType>
       
   194           </xsd:attribute>
       
   195         </xsd:complexType>
       
   196       </xsd:element>
       
   197     </xsd:schema>
       
   198     """
       
   199 
       
   200     def GetParamsAttributes(self, path = None):
       
   201         infos = ConfigTreeNode.GetParamsAttributes(self, path = path)
       
   202         for element in infos:
       
   203             if element["name"] == "MemoryArea":
       
   204                 for child in element["children"]:
       
   205                     if child["name"] == "MemoryAreaType":
       
   206                         list = modbus_memtype_dict.keys()
       
   207                         list.sort()
       
   208                         child["type"] = list
       
   209         return infos
       
   210 
       
   211     def GetVariableLocationTree(self):
       
   212         current_location = self.GetCurrentLocation()
       
   213         name = self.BaseParams.getName()
       
   214         address = self.GetParamsAttributes()[0]["children"][2]["value"]
       
   215         count   = self.GetParamsAttributes()[0]["children"][1]["value"]
       
   216         function= self.GetParamsAttributes()[0]["children"][0]["value"]
       
   217         # 'BOOL' or 'WORD'
       
   218         datatype= modbus_memtype_dict[function][3]
       
   219         # 1 or 16
       
   220         datasize= modbus_memtype_dict[function][4]
       
   221         # 'Q' for coils and holding registers, 'I' for input discretes and input registers
       
   222         datazone= modbus_memtype_dict[function][5] 
       
   223         # 'X' for bits, 'W' for words
       
   224         datatacc= modbus_memtype_dict[function][6] 
       
   225         # 'Coil', 'Holding Register', 'Input Discrete' or 'Input Register'
       
   226         dataname= modbus_memtype_dict[function][7] 
       
   227         entries = []
       
   228         for offset in range(address, address+count):
       
   229             entries.append({
       
   230                 "name": dataname + " " + str(offset),
       
   231                 "type": LOCATION_VAR_MEMORY,
       
   232                 "size": datasize,
       
   233                 "IEC_type": datatype,
       
   234                 "var_name": "var_name",
       
   235                 "location": datatacc + ".".join([str(i) for i in current_location]) + "." + str(offset),
       
   236                 "description": "description",
       
   237                 "children": []})
       
   238         return  {"name": name,
       
   239                  "type": LOCATION_CONFNODE,
       
   240                  "location": ".".join([str(i) for i in current_location]) + ".x",
       
   241                  "children": entries}
       
   242 
       
   243     def CTNGenerate_C(self, buildpath, locations):
       
   244         """
       
   245         Generate C code
       
   246         @param current_location: Tupple containing plugin IEC location : %I0.0.4.5 => (0,0,4,5)
       
   247         @param locations: List of complete variables locations \
       
   248             [{"IEC_TYPE" : the IEC type (i.e. "INT", "STRING", ...)
       
   249             "NAME" : name of the variable (generally "__IW0_1_2" style)
       
   250             "DIR" : direction "Q","I" or "M"
       
   251             "SIZE" : size "X", "B", "W", "D", "L"
       
   252             "LOC" : tuple of interger for IEC location (0,1,2,...)
       
   253             }, ...]
       
   254         @return: [(C_file_name, CFLAGS),...] , LDFLAGS_TO_APPEND
       
   255         """
       
   256         return [], "", False
       
   257 
       
   258         
       
   259 ###################################################
       
   260 ###################################################
       
   261 #                                                 #
       
   262 #            T C P    C L I E N T                 # 
       
   263 #                                                 #
       
   264 ###################################################
       
   265 ###################################################
       
   266 
       
   267 class _ModbusTCPclientPlug:
       
   268     XSD = """<?xml version="1.0" encoding="ISO-8859-1" ?>
       
   269     <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
       
   270       <xsd:element name="ModbusTCPclient">
       
   271         <xsd:complexType>
       
   272           <xsd:attribute name="Remote_IP_Address" type="xsd:string" use="optional" default="localhost"/>
       
   273           <xsd:attribute name="Remote_Port_Number" type="xsd:string" use="optional" default="502"/>
       
   274           <xsd:attribute name="Invocation_Rate_in_ms" use="optional" default="100">
       
   275             <xsd:simpleType>
       
   276                 <xsd:restriction base="xsd:unsignedLong">
       
   277                     <xsd:minInclusive value="1"/>
       
   278                     <xsd:maxInclusive value="2147483647"/>
       
   279                 </xsd:restriction>
       
   280             </xsd:simpleType>
       
   281           </xsd:attribute>
       
   282         </xsd:complexType>
       
   283       </xsd:element>
       
   284     </xsd:schema>
       
   285     """
       
   286     # NOTE: Max value of 2147483647 (i32_max) for Invocation_Rate_in_ms corresponds to aprox 25 days.
       
   287     CTNChildrenTypes = [("ModbusRequest",_RequestPlug, "Request")]
       
   288     # TODO: Replace with CTNType !!!
       
   289     PlugType = "ModbusTCPclient"
       
   290 
       
   291     # Return the number of (modbus library) nodes this specific TCP client will need
       
   292     #   return type: (tcp nodes, rtu nodes, ascii nodes)
       
   293     def GetNodeCount(self):
       
   294         return (1, 0, 0)
       
   295 
       
   296     def CTNGenerate_C(self, buildpath, locations):
       
   297         """
       
   298         Generate C code
       
   299         @param current_location: Tupple containing plugin IEC location : %I0.0.4.5 => (0,0,4,5)
       
   300         @param locations: List of complete variables locations \
       
   301             [{"IEC_TYPE" : the IEC type (i.e. "INT", "STRING", ...)
       
   302             "NAME" : name of the variable (generally "__IW0_1_2" style)
       
   303             "DIR" : direction "Q","I" or "M"
       
   304             "SIZE" : size "X", "B", "W", "D", "L"
       
   305             "LOC" : tuple of interger for IEC location (0,1,2,...)
       
   306             }, ...]
       
   307         @return: [(C_file_name, CFLAGS),...] , LDFLAGS_TO_APPEND
       
   308         """
       
   309         return [], "", False
       
   310 
       
   311 
       
   312 
       
   313         
       
   314 ###################################################
       
   315 ###################################################
       
   316 #                                                 #
       
   317 #            T C P    S E R V E R                 # 
       
   318 #                                                 #
       
   319 ###################################################
       
   320 ###################################################
       
   321 
       
   322 class _ModbusTCPserverPlug:
       
   323     # NOTE: the Port number is a 'string' and not an 'integer'!
       
   324     # This is because the underlying modbus library accepts strings 
       
   325     # (e.g.: well known port names!)
       
   326     XSD = """<?xml version="1.0" encoding="ISO-8859-1" ?>
       
   327     <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
       
   328       <xsd:element name="ModbusServerNode">
       
   329         <xsd:complexType>
       
   330           <xsd:attribute name="Local_IP_Address" type="xsd:string" use="optional"  default="#ANY#"/>
       
   331           <xsd:attribute name="Local_Port_Number" type="xsd:string" use="optional" default="502"/>
       
   332           <xsd:attribute name="SlaveID" use="optional" default="0">
       
   333             <xsd:simpleType>
       
   334                 <xsd:restriction base="xsd:integer">
       
   335                     <xsd:minInclusive value="0"/>
       
   336                     <xsd:maxInclusive value="255"/>
       
   337                 </xsd:restriction>
       
   338             </xsd:simpleType>
       
   339           </xsd:attribute>
       
   340         </xsd:complexType>
       
   341       </xsd:element>
       
   342     </xsd:schema>
       
   343     """
       
   344     CTNChildrenTypes = [("MemoryArea",_MemoryAreaPlug, "Memory Area")]
       
   345     # TODO: Replace with CTNType !!!
       
   346     PlugType = "ModbusTCPserver"
       
   347 
       
   348     # Return the number of (modbus library) nodes this specific TCP server will need
       
   349     #   return type: (tcp nodes, rtu nodes, ascii nodes)
       
   350     def GetNodeCount(self):
       
   351         return (1, 0, 0)
       
   352 
       
   353     # Return a list with a single tuple conatining the (location, port number)
       
   354     #     location: location of this node in the configuration tree
       
   355     #     port number: IP port used by this Modbus/IP server
       
   356     def GetIPServerPortNumbers(self):
       
   357         port = self.GetParamsAttributes()[0]["children"][1]["value"]
       
   358         return [(self.GetCurrentLocation() , port)]
       
   359 
       
   360     def CTNGenerate_C(self, buildpath, locations):
       
   361         """
       
   362         Generate C code
       
   363         @param current_location: Tupple containing plugin IEC location : %I0.0.4.5 => (0,0,4,5)
       
   364         @param locations: List of complete variables locations \
       
   365             [{"IEC_TYPE" : the IEC type (i.e. "INT", "STRING", ...)
       
   366             "NAME" : name of the variable (generally "__IW0_1_2" style)
       
   367             "DIR" : direction "Q","I" or "M"
       
   368             "SIZE" : size "X", "B", "W", "D", "L"
       
   369             "LOC" : tuple of interger for IEC location (0,1,2,...)
       
   370             }, ...]
       
   371         @return: [(C_file_name, CFLAGS),...] , LDFLAGS_TO_APPEND
       
   372         """
       
   373         return [], "", False
       
   374 
       
   375 
       
   376 
       
   377 
       
   378 ###################################################
       
   379 ###################################################
       
   380 #                                                 #
       
   381 #            R T U    C L I E N T                 # 
       
   382 #                                                 #
       
   383 ###################################################
       
   384 ###################################################
       
   385 
       
   386 class _ModbusRTUclientPlug:
       
   387     XSD = """<?xml version="1.0" encoding="ISO-8859-1" ?>
       
   388     <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
       
   389       <xsd:element name="ModbusRTUclient">
       
   390         <xsd:complexType>
       
   391           <xsd:attribute name="Serial_Port" type="xsd:string"  use="optional" default="/dev/ttyS0"/>
       
   392           <xsd:attribute name="Baud_Rate"   type="xsd:string"  use="optional" default="9600"/>
       
   393           <xsd:attribute name="Parity"      type="xsd:string"  use="optional" default="even"/>
       
   394           <xsd:attribute name="Stop_Bits"   type="xsd:string"  use="optional" default="1"/>
       
   395           <xsd:attribute name="Invocation_Rate_in_ms" use="optional" default="100">
       
   396             <xsd:simpleType>
       
   397                 <xsd:restriction base="xsd:integer">
       
   398                     <xsd:minInclusive value="1"/>
       
   399                     <xsd:maxInclusive value="2147483647"/>
       
   400                 </xsd:restriction>
       
   401             </xsd:simpleType>
       
   402           </xsd:attribute>
       
   403         </xsd:complexType>
       
   404       </xsd:element>
       
   405     </xsd:schema>
       
   406     """
       
   407     # NOTE: Max value of 2147483647 (i32_max) for Invocation_Rate_in_ms corresponds to aprox 25 days.
       
   408     CTNChildrenTypes = [("ModbusRequest",_RequestPlug, "Request")]
       
   409     # TODO: Replace with CTNType !!!
       
   410     PlugType = "ModbusRTUclient"
       
   411 
       
   412     def GetParamsAttributes(self, path = None):
       
   413         infos = ConfigTreeNode.GetParamsAttributes(self, path = path)
       
   414         for element in infos:
       
   415             if element["name"] == "ModbusRTUclient":
       
   416                 for child in element["children"]:
       
   417                     if child["name"] == "Baud_Rate":
       
   418                         child["type"] = modbus_serial_baudrate_list
       
   419                     if child["name"] == "Stop_Bits":
       
   420                         child["type"] = modbus_serial_stopbits_list
       
   421                     if child["name"] == "Parity":
       
   422                         child["type"] = modbus_serial_parity_dict.keys()
       
   423         return infos
       
   424       
       
   425     # Return the number of (modbus library) nodes this specific RTU client will need
       
   426     #   return type: (tcp nodes, rtu nodes, ascii nodes)
       
   427     def GetNodeCount(self):
       
   428         return (0, 1, 0)
       
   429 
       
   430     def CTNGenerate_C(self, buildpath, locations):
       
   431         """
       
   432         Generate C code
       
   433         @param current_location: Tupple containing plugin IEC location : %I0.0.4.5 => (0,0,4,5)
       
   434         @param locations: List of complete variables locations \
       
   435             [{"IEC_TYPE" : the IEC type (i.e. "INT", "STRING", ...)
       
   436             "NAME" : name of the variable (generally "__IW0_1_2" style)
       
   437             "DIR" : direction "Q","I" or "M"
       
   438             "SIZE" : size "X", "B", "W", "D", "L"
       
   439             "LOC" : tuple of interger for IEC location (0,1,2,...)
       
   440             }, ...]
       
   441         @return: [(C_file_name, CFLAGS),...] , LDFLAGS_TO_APPEND
       
   442         """
       
   443         return [], "", False
       
   444 
       
   445 
       
   446 
       
   447 ###################################################
       
   448 ###################################################
       
   449 #                                                 #
       
   450 #            R T U    S L A V E                   # 
       
   451 #                                                 #
       
   452 ###################################################
       
   453 ###################################################
       
   454 
       
   455 
       
   456 class _ModbusRTUslavePlug:
       
   457     XSD = """<?xml version="1.0" encoding="ISO-8859-1" ?>
       
   458     <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
       
   459       <xsd:element name="ModbusRTUslave">
       
   460         <xsd:complexType>
       
   461           <xsd:attribute name="Serial_Port" type="xsd:string"  use="optional" default="/dev/ttyS0"/>
       
   462           <xsd:attribute name="Baud_Rate"   type="xsd:string"  use="optional" default="9600"/>
       
   463           <xsd:attribute name="Parity"      type="xsd:string"  use="optional" default="even"/>
       
   464           <xsd:attribute name="Stop_Bits"   type="xsd:string"  use="optional" default="1"/>
       
   465           <xsd:attribute name="SlaveID" use="optional" default="1">
       
   466             <xsd:simpleType>
       
   467                 <xsd:restriction base="xsd:integer">
       
   468                     <xsd:minInclusive value="1"/>
       
   469                     <xsd:maxInclusive value="255"/>
       
   470                 </xsd:restriction>
       
   471             </xsd:simpleType>
       
   472           </xsd:attribute>
       
   473         </xsd:complexType>
       
   474       </xsd:element>
       
   475     </xsd:schema>
       
   476     """
       
   477     CTNChildrenTypes = [("MemoryArea",_MemoryAreaPlug, "Memory Area")]
       
   478     # TODO: Replace with CTNType !!!
       
   479     PlugType = "ModbusRTUslave"
       
   480 
       
   481     def GetParamsAttributes(self, path = None):
       
   482         infos = ConfigTreeNode.GetParamsAttributes(self, path = path)
       
   483         for element in infos:
       
   484             if element["name"] == "ModbusRTUslave":
       
   485                 for child in element["children"]:
       
   486                     if child["name"] == "Baud_Rate":
       
   487                         child["type"] = modbus_serial_baudrate_list
       
   488                     if child["name"] == "Stop_Bits":
       
   489                         child["type"] = modbus_serial_stopbits_list
       
   490                     if child["name"] == "Parity":
       
   491                         child["type"] = modbus_serial_parity_dict.keys()
       
   492         return infos
       
   493       
       
   494     # Return the number of (modbus library) nodes this specific RTU slave will need
       
   495     #   return type: (tcp nodes, rtu nodes, ascii nodes)
       
   496     def GetNodeCount(self):
       
   497         return (0, 1, 0)
       
   498 
       
   499     def CTNGenerate_C(self, buildpath, locations):
       
   500         """
       
   501         Generate C code
       
   502         @param current_location: Tupple containing plugin IEC location : %I0.0.4.5 => (0,0,4,5)
       
   503         @param locations: List of complete variables locations \
       
   504             [{"IEC_TYPE" : the IEC type (i.e. "INT", "STRING", ...)
       
   505             "NAME" : name of the variable (generally "__IW0_1_2" style)
       
   506             "DIR" : direction "Q","I" or "M"
       
   507             "SIZE" : size "X", "B", "W", "D", "L"
       
   508             "LOC" : tuple of interger for IEC location (0,1,2,...)
       
   509             }, ...]
       
   510         @return: [(C_file_name, CFLAGS),...] , LDFLAGS_TO_APPEND
       
   511         """
       
   512         return [], "", False
       
   513 
       
   514     
       
   515 
       
   516 ###################################################
       
   517 ###################################################
       
   518 #                                                 #
       
   519 #             R O O T    C L A S S                # 
       
   520 #                                                 #
       
   521 ###################################################
       
   522 ###################################################
       
   523 class RootClass:
       
   524     XSD = """<?xml version="1.0" encoding="ISO-8859-1" ?>
       
   525     <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
       
   526       <xsd:element name="ModbusRoot">
       
   527         <xsd:complexType>
       
   528           <xsd:attribute name="MaxRemoteTCPclients" use="optional" default="10">
       
   529             <xsd:simpleType>
       
   530                 <xsd:restriction base="xsd:integer">
       
   531                     <xsd:minInclusive value="0"/>
       
   532                     <xsd:maxInclusive value="65535"/>
       
   533                 </xsd:restriction>
       
   534             </xsd:simpleType>
       
   535           </xsd:attribute>
       
   536         </xsd:complexType>
       
   537       </xsd:element>
       
   538     </xsd:schema>
       
   539     """
       
   540     CTNChildrenTypes = [("ModbusTCPclient",_ModbusTCPclientPlug, "Modbus TCP Client")
       
   541                        ,("ModbusTCPserver",_ModbusTCPserverPlug, "Modbus TCP Server")
       
   542                        ,("ModbusRTUclient",_ModbusRTUclientPlug, "Modbus RTU Client")
       
   543                        ,("ModbusRTUslave", _ModbusRTUslavePlug,  "Modbus RTU Slave")
       
   544                        ]
       
   545     
       
   546     # Return the number of (modbus library) nodes this specific instance of the modbus plugin will need
       
   547     #   return type: (tcp nodes, rtu nodes, ascii nodes)
       
   548     def GetNodeCount(self):
       
   549         max_remote_tcpclient = self.GetParamsAttributes()[0]["children"][0]["value"]
       
   550         total_node_count = (max_remote_tcpclient, 0, 0)
       
   551         for child in self.IECSortedChildren():
       
   552             # ask each child how many nodes it needs, and add them all up.
       
   553             total_node_count = tuple(x1 + x2 for x1, x2 in zip(total_node_count, child.GetNodeCount())) 
       
   554         return total_node_count
       
   555 
       
   556     # Return a list with tuples of the (location, port numbers) used by all the Modbus/IP servers
       
   557     def GetIPServerPortNumbers(self):
       
   558         IPServer_port_numbers = []
       
   559         for child in self.IECSortedChildren():
       
   560             if child.CTNType == "ModbusTCPserver":
       
   561                 IPServer_port_numbers.extend(child.GetIPServerPortNumbers())
       
   562         return IPServer_port_numbers
       
   563 
       
   564     def CTNGenerate_C(self, buildpath, locations):
       
   565         #print "#############"
       
   566         #print self.__class__
       
   567         #print type(self)
       
   568         #print "self.CTNType >>>"
       
   569         #print self.CTNType
       
   570         #print "type(self.CTNType) >>>"
       
   571         #print type(self.CTNType)
       
   572         #print "#############"
       
   573         
       
   574         loc_dict = {"locstr" : "_".join(map(str,self.GetCurrentLocation())),
       
   575                    }
       
   576         
       
   577         # Determine the number of (modbus library) nodes ALL instances of the modbus plugin will need
       
   578         #   total_node_count: (tcp nodes, rtu nodes, ascii nodes)
       
   579         # Also get a list with tuples of (location, IP port numbers) used by all the Modbus/IP server nodes
       
   580         #   This list is later used to search for duplicates in port numbers!
       
   581         #   IPServer_port_numbers = [(location ,IPserver_port_number), ...]
       
   582         #       location: tuple similar to (0, 3, 1) representing the location in the configuration tree "0.3.1.x"
       
   583         #       IPserver_port_number: a number (i.e. port number used by the Modbus/IP server)
       
   584         total_node_count = (0, 0, 0)
       
   585         IPServer_port_numbers = []
       
   586         for CTNInstance in self.GetCTRoot().IterChildren():
       
   587             if CTNInstance.CTNType == "modbus":
       
   588                 # ask each modbus plugin instance how many nodes it needs, and add them all up.
       
   589                 total_node_count = tuple(x1 + x2 for x1, x2 in zip(total_node_count, CTNInstance.GetNodeCount())) 
       
   590                 IPServer_port_numbers.extend(CTNInstance.GetIPServerPortNumbers())
       
   591 
       
   592         # Search for use of duplicate port numbers by Modbus/IP servers
       
   593         #print IPServer_port_numbers 
       
   594         # ..but first define a lambda function to convert a tuple with the config tree location to a nice looking string
       
   595         #   for e.g., convert the tuple (0, 3, 4) to "0.3.4" 
       
   596         lt_to_str = lambda loctuple: '.'.join(map(str, loctuple))
       
   597         for i in range(0, len(IPServer_port_numbers)-1):
       
   598             for j in range (i+1, len(IPServer_port_numbers)):
       
   599                 if IPServer_port_numbers[i][1] == IPServer_port_numbers[j][1]:
       
   600                     self.GetCTRoot().logger.write_warning(_("Error: Modbus/IP Servers %s.x and %s.x use the same port number %s.\n")%(lt_to_str(IPServer_port_numbers[i][0]), lt_to_str(IPServer_port_numbers[j][0]), IPServer_port_numbers[j][1]))
       
   601                     raise Exception, False
       
   602                     # TODO: return an error code instead of raising an exception
       
   603         
       
   604         # Determine the current location in Beremiz's project configuration tree 
       
   605         current_location = self.GetCurrentLocation()
       
   606         
       
   607         # define a unique name for the generated C and h files
       
   608         prefix = "_".join(map(str, current_location))
       
   609         Gen_MB_c_path = os.path.join(buildpath, "MB_%s.c"%prefix)
       
   610         Gen_MB_h_path = os.path.join(buildpath, "MB_%s.h"%prefix) 
       
   611         c_filename = os.path.join(os.path.split(__file__)[0],"mb_runtime.c")
       
   612         h_filename = os.path.join(os.path.split(__file__)[0],"mb_runtime.h")
       
   613 
       
   614         tcpclient_reqs_count = 0
       
   615         rtuclient_reqs_count = 0
       
   616         ascclient_reqs_count = 0
       
   617         tcpclient_node_count = 0
       
   618         rtuclient_node_count = 0
       
   619         ascclient_node_count = 0
       
   620         tcpserver_node_count = 0
       
   621         rtuserver_node_count = 0
       
   622         ascserver_node_count = 0
       
   623         nodeid = 0
       
   624         client_nodeid = 0
       
   625         client_requestid = 0
       
   626         server_id = 0
       
   627         
       
   628         server_node_list = []
       
   629         client_node_list = []
       
   630         client_request_list = []
       
   631         server_memarea_list = []
       
   632         loc_vars = []
       
   633         loc_vars_list = [] # list of variables already declared in C code!
       
   634         for child in self.IECSortedChildren():
       
   635             #print "<<<<<<<<<<<<<"
       
   636             #print "child (self.IECSortedChildren())----->"
       
   637             #print child.__class__
       
   638             #print ">>>>>>>>>>>>>"
       
   639             ######################################
       
   640             if child.PlugType == "ModbusTCPserver":
       
   641                 tcpserver_node_count += 1
       
   642                 new_node = GetTCPServerNodePrinted(self, child)
       
   643                 if new_node is None:
       
   644                     return [],"",False
       
   645                 server_node_list.append(new_node)
       
   646                 ##############
       
   647                 for subchild in child.IECSortedChildren():
       
   648                     new_memarea = GetTCPServerMemAreaPrinted(self, subchild, nodeid)
       
   649                     if new_memarea is None:
       
   650                         return [],"",False
       
   651                     server_memarea_list.append(new_memarea)
       
   652                     function= subchild.GetParamsAttributes()[0]["children"][0]["value"]
       
   653                     # 'ro_bits', 'rw_bits', 'ro_words' or 'rw_words'
       
   654                     memarea= modbus_memtype_dict[function][1]
       
   655                     for iecvar in subchild.GetLocations():
       
   656                         #print repr(iecvar)
       
   657                         absloute_address = iecvar["LOC"][3]
       
   658                         start_address = int(subchild.GetParamsAttributes()[0]["children"][2]["value"])
       
   659                         relative_addr = absloute_address - start_address
       
   660                         #test if relative address in request specified range
       
   661                         if relative_addr in xrange(int(subchild.GetParamsAttributes()[0]["children"][1]["value"])):
       
   662                             if str(iecvar["NAME"]) not in loc_vars_list:
       
   663                                 loc_vars.append("u16 *" + str(iecvar["NAME"]) + " = &server_nodes[%d].mem_area.%s[%d];" % (server_id, memarea, absloute_address))
       
   664                                 loc_vars_list.append(str(iecvar["NAME"]))
       
   665                 server_id += 1
       
   666             ######################################
       
   667             if child.PlugType == "ModbusRTUslave":
       
   668                 rtuserver_node_count += 1
       
   669                 new_node = GetRTUSlaveNodePrinted(self, child)
       
   670                 if new_node is None:
       
   671                     return [],"",False
       
   672                 server_node_list.append(new_node)
       
   673                 ##############
       
   674                 for subchild in child.IECSortedChildren():
       
   675                     new_memarea = GetTCPServerMemAreaPrinted(self, subchild, nodeid)
       
   676                     if new_memarea is None:
       
   677                         return [],"",False
       
   678                     server_memarea_list.append(new_memarea)
       
   679                     function= subchild.GetParamsAttributes()[0]["children"][0]["value"]
       
   680                     # 'ro_bits', 'rw_bits', 'ro_words' or 'rw_words'
       
   681                     memarea= modbus_memtype_dict[function][1]
       
   682                     for iecvar in subchild.GetLocations():
       
   683                         #print repr(iecvar)
       
   684                         absloute_address = iecvar["LOC"][3]
       
   685                         start_address = int(subchild.GetParamsAttributes()[0]["children"][2]["value"])
       
   686                         relative_addr = absloute_address - start_address
       
   687                         #test if relative address in request specified range
       
   688                         if relative_addr in xrange(int(subchild.GetParamsAttributes()[0]["children"][1]["value"])):
       
   689                             if str(iecvar["NAME"]) not in loc_vars_list:
       
   690                                 loc_vars.append("u16 *" + str(iecvar["NAME"]) + " = &server_nodes[%d].mem_area.%s[%d];" % (server_id, memarea, absloute_address))
       
   691                                 loc_vars_list.append(str(iecvar["NAME"]))
       
   692                 server_id += 1
       
   693             ######################################
       
   694             if child.PlugType == "ModbusTCPclient":
       
   695                 tcpclient_reqs_count += len(child.IECSortedChildren())
       
   696                 new_node = GetTCPClientNodePrinted(self, child)
       
   697                 if new_node is None:
       
   698                     return [],"",False
       
   699                 client_node_list.append(new_node)
       
   700                 for subchild in child.IECSortedChildren():
       
   701                     new_req = GetClientRequestPrinted(self, subchild, client_nodeid)
       
   702                     if new_req is None:
       
   703                         return [],"",False
       
   704                     client_request_list.append(new_req)
       
   705                     for iecvar in subchild.GetLocations():
       
   706                         #absloute address - start address
       
   707                         relative_addr = iecvar["LOC"][3] - int(subchild.GetParamsAttributes()[0]["children"][3]["value"])
       
   708                         #test if relative address in request specified range
       
   709                         if relative_addr in xrange(int(subchild.GetParamsAttributes()[0]["children"][2]["value"])):
       
   710                             if str(iecvar["NAME"]) not in loc_vars_list:
       
   711                                 loc_vars.append("u16 *" + str(iecvar["NAME"]) + " = &client_requests[%d].plcv_buffer[%d];" % (client_requestid, relative_addr))
       
   712                                 loc_vars_list.append(str(iecvar["NAME"]))
       
   713                     client_requestid += 1
       
   714                 tcpclient_node_count += 1
       
   715                 client_nodeid += 1
       
   716             ######################################
       
   717             if child.PlugType == "ModbusRTUclient":
       
   718                 rtuclient_reqs_count += len(child.IECSortedChildren())
       
   719                 new_node = GetRTUClientNodePrinted(self, child)
       
   720                 if new_node is None:
       
   721                     return [],"",False
       
   722                 client_node_list.append(new_node)
       
   723                 for subchild in child.IECSortedChildren():
       
   724                     new_req = GetClientRequestPrinted(self, subchild, client_nodeid)
       
   725                     if new_req is None:
       
   726                         return [],"",False
       
   727                     client_request_list.append(new_req)
       
   728                     for iecvar in subchild.GetLocations():
       
   729                         #absloute address - start address
       
   730                         relative_addr = iecvar["LOC"][3] - int(subchild.GetParamsAttributes()[0]["children"][3]["value"])
       
   731                         #test if relative address in request specified range
       
   732                         if relative_addr in xrange(int(subchild.GetParamsAttributes()[0]["children"][2]["value"])):
       
   733                             if str(iecvar["NAME"]) not in loc_vars_list:
       
   734                                 loc_vars.append("u16 *" + str(iecvar["NAME"]) + " = &client_requests[%d].plcv_buffer[%d];" % (client_requestid, relative_addr))
       
   735                                 loc_vars_list.append(str(iecvar["NAME"]))
       
   736                     client_requestid += 1
       
   737                 rtuclient_node_count += 1
       
   738                 client_nodeid += 1
       
   739             nodeid += 1
       
   740         
       
   741         loc_dict["loc_vars"]             = "\n".join(loc_vars)
       
   742         loc_dict["server_nodes_params"]  = ",\n\n".join(server_node_list)
       
   743         loc_dict["client_nodes_params"]  = ",\n\n".join(client_node_list)
       
   744         loc_dict["client_req_params"]    = ",\n\n".join(client_request_list)
       
   745         loc_dict["tcpclient_reqs_count"] = str(tcpclient_reqs_count)
       
   746         loc_dict["tcpclient_node_count"] = str(tcpclient_node_count)
       
   747         loc_dict["tcpserver_node_count"] = str(tcpserver_node_count)
       
   748         loc_dict["rtuclient_reqs_count"] = str(rtuclient_reqs_count)
       
   749         loc_dict["rtuclient_node_count"] = str(rtuclient_node_count)
       
   750         loc_dict["rtuserver_node_count"] = str(rtuserver_node_count)
       
   751         loc_dict["ascclient_reqs_count"] = str(ascclient_reqs_count)
       
   752         loc_dict["ascclient_node_count"] = str(ascclient_node_count)
       
   753         loc_dict["ascserver_node_count"] = str(ascserver_node_count)
       
   754         loc_dict["total_tcpnode_count"]  = str(total_node_count[0])
       
   755         loc_dict["total_rtunode_count"]  = str(total_node_count[1])
       
   756         loc_dict["total_ascnode_count"]  = str(total_node_count[2])
       
   757         loc_dict["max_remote_tcpclient"] = int(self.GetParamsAttributes()[0]["children"][0]["value"])
       
   758 
       
   759         #get template file content into a string, format it with dict
       
   760         #and write it to proper .h file
       
   761         mb_main = open(h_filename).read() % loc_dict
       
   762         f = open(Gen_MB_h_path,'w')
       
   763         f.write(mb_main)
       
   764         f.close()
       
   765         #same thing as above, but now to .c file
       
   766         mb_main = open(c_filename).read() % loc_dict
       
   767         f = open(Gen_MB_c_path,'w')
       
   768         f.write(mb_main)
       
   769         f.close()
       
   770 
       
   771         LDFLAGS = []
       
   772         LDFLAGS.append(" \"-L" + ModbusPath + "\"")
       
   773         LDFLAGS.append(" -lmb ")
       
   774         LDFLAGS.append(" \"-Wl,-rpath," + ModbusPath + "\"")
       
   775         #LDFLAGS.append("\"" + os.path.join(ModbusPath, "mb_slave_and_master.o") + "\"")
       
   776         #LDFLAGS.append("\"" + os.path.join(ModbusPath, "mb_slave.o") + "\"")
       
   777         #LDFLAGS.append("\"" + os.path.join(ModbusPath, "mb_master.o") + "\"")
       
   778         #LDFLAGS.append("\"" + os.path.join(ModbusPath, "mb_tcp.o")    + "\"")
       
   779         #LDFLAGS.append("\"" + os.path.join(ModbusPath, "mb_rtu.o")    + "\"")
       
   780         #LDFLAGS.append("\"" + os.path.join(ModbusPath, "mb_ascii.o")    + "\"")
       
   781         #LDFLAGS.append("\"" + os.path.join(ModbusPath, "sin_util.o")  + "\"")
       
   782         if os.name == 'nt':   # other possible values: 'posix' 'os2' 'ce' 'java' 'riscos'
       
   783             LDFLAGS.append(" -lws2_32 ")  # on windows we need to load winsock library!
       
   784 
       
   785         return [(Gen_MB_c_path, ' -I"'+ModbusPath+'"')], LDFLAGS, True