modbus/mb_utils.py
changeset 1909 bb883e063175
child 1918 e7b6478b4ebc
equal deleted inserted replaced
1475:de4ee16f7c6c 1909:bb883e063175
       
     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 #dictionary implementing:
       
    29 #key   - string with the description we want in the request plugin GUI
       
    30 #tuple - (modbus function number, request type, max count value, data_type, bit_size)
       
    31 modbus_function_dict = {"01 - Read Coils" :               ( '1', 'req_input', 2000, "BOOL", 1 , "Q", "X", "Coil"),
       
    32                         "02 - Read Input Discretes" :     ( '2', 'req_input', 2000, "BOOL", 1 , "I", "X", "Input Discrete"),
       
    33                         "03 - Read Holding Registers" :   ( '3', 'req_input',  125, "WORD", 16, "Q", "W", "Holding Register"),
       
    34                         "04 - Read Input Registers" :     ( '4', 'req_input',  125, "WORD", 16, "I", "W", "Input Register"),
       
    35                         "05 - Write Single coil" :        ( '5','req_output',    1, "BOOL", 1 , "Q", "X", "Coil"),
       
    36                         "06 - Write Single Register" :    ( '6','req_output',    1, "WORD", 16, "Q", "W", "Holding Register"),
       
    37                         "15 - Write Multiple Coils" :     ('15','req_output', 1968, "BOOL", 1 , "Q", "X", "Coil"),
       
    38                         "16 - Write Multiple Registers" : ('16','req_output',  123, "WORD", 16, "Q", "W", "Holding Register"),
       
    39                        }
       
    40 
       
    41 
       
    42 
       
    43 def GetTCPServerNodePrinted(self, child):
       
    44     """
       
    45     Outputs a string to be used on C files
       
    46     params: child - the correspondent subplugin in Beremiz
       
    47     """
       
    48     node_init_template = '''/*node %(locnodestr)s*/
       
    49 {"%(locnodestr)s", %(slaveid)s, {naf_tcp, {.tcp = {%(host)s, "%(port)s", DEF_CLOSE_ON_SILENCE}}}, -1 /* mb_nd */, 0 /* init_state */}'''
       
    50 
       
    51     location = ".".join(map(str, child.GetCurrentLocation()))
       
    52     host     = child.GetParamsAttributes()[0]["children"][0]["value"]
       
    53     port     = child.GetParamsAttributes()[0]["children"][1]["value"]
       
    54     slaveid  = child.GetParamsAttributes()[0]["children"][2]["value"]
       
    55     if host=="#ANY#":
       
    56       host='INADDR_ANY'
       
    57     else:
       
    58       host='"'+host+'"'
       
    59     #slaveid = child.GetParamsAttributes()[0]["children"][2]["value"]
       
    60     #if int(slaveid) not in xrange(256):
       
    61         #self.GetCTRoot().logger.write_error("Error: Wrong slave ID in %s server node\nModbus Plugin C code returns empty\n"%location)
       
    62         #return None
       
    63 
       
    64     node_dict = {"locnodestr" : location,
       
    65                  "host"       : host,
       
    66                  "port"       : port,
       
    67                  "slaveid"    : slaveid,
       
    68                 }
       
    69     return node_init_template % node_dict
       
    70 
       
    71 
       
    72 
       
    73 
       
    74 def GetTCPServerMemAreaPrinted(self, child, nodeid):
       
    75     """
       
    76     Outputs a string to be used on C files
       
    77     params: child - the correspondent subplugin in Beremiz
       
    78             nodeid - on C code, each request has it's own parent node (sequential, 0..NUMBER_OF_NODES)
       
    79                      It's this parameter.
       
    80     return: None - if any definition error found
       
    81             The string that should be added on C code - if everything goes allright
       
    82     """
       
    83     request_dict = {}
       
    84 
       
    85     request_dict["locreqstr"] = "_".join(map(str, child.GetCurrentLocation()))
       
    86     request_dict["nodeid"] = str(nodeid)
       
    87     request_dict["address"] = child.GetParamsAttributes()[0]["children"][2]["value"]
       
    88     if int(request_dict["address"]) not in xrange(65536):
       
    89         self.GetCTRoot().logger.write_error("Modbus plugin: Invalid Start Address in server memory area node %(locreqstr)s (Must be in the range [0..65535])\nModbus plugin: Aborting C code generation for this node\n"%request_dict)
       
    90         return None
       
    91     request_dict["count"] = child.GetParamsAttributes()[0]["children"][1]["value"]
       
    92     if int(request_dict["count"]) not in xrange(1, 65536):
       
    93         self.GetCTRoot().logger.write_error("Modbus plugin: Invalid number of channels in server memory area node %(locreqstr)s (Must be in the range [1..65536-start_address])\nModbus plugin: Aborting C code generation for this node\n"%request_dict)
       
    94         return None
       
    95     if (int(request_dict["address"]) + int(request_dict["count"])) not in xrange(1,65537):
       
    96         self.GetCTRoot().logger.write_error("Modbus plugin: Invalid number of channels in server memory area node %(locreqstr)s (Must be in the range [1..65536-start_address])\nModbus plugin: Aborting C code generation for this node\n"%request_dict)
       
    97         return None
       
    98     
       
    99     return ""
       
   100 
       
   101 
       
   102 
       
   103 
       
   104 modbus_serial_baudrate_list = ["110", "300", "600", "1200", "2400", "4800", "9600", "19200", "38400", "57600", "115200"]
       
   105 modbus_serial_stopbits_list = ["1", "2"]
       
   106 modbus_serial_parity_dict   = {"none": 0, "odd": 1, "even": 2}
       
   107 
       
   108 
       
   109 
       
   110 def GetRTUSlaveNodePrinted(self, child):
       
   111     """
       
   112     Outputs a string to be used on C files
       
   113     params: child - the correspondent subplugin in Beremiz
       
   114     """
       
   115     node_init_template = '''/*node %(locnodestr)s*/
       
   116 {"%(locnodestr)s", %(slaveid)s, {naf_rtu, {.rtu = {"%(device)s", %(baud)s /*baud*/, %(parity)s /*parity*/, 8 /*data bits*/, %(stopbits)s, 0 /* ignore echo */}}}, -1 /* mb_nd */, 0 /* init_state */}'''
       
   117 
       
   118     location = ".".join(map(str, child.GetCurrentLocation()))
       
   119     device   = child.GetParamsAttributes()[0]["children"][0]["value"]
       
   120     baud     = child.GetParamsAttributes()[0]["children"][1]["value"]
       
   121     parity   = child.GetParamsAttributes()[0]["children"][2]["value"]
       
   122     stopbits = child.GetParamsAttributes()[0]["children"][3]["value"]
       
   123     slaveid  = child.GetParamsAttributes()[0]["children"][4]["value"]
       
   124     
       
   125     node_dict = {"locnodestr" : location,
       
   126                  "device"     : device,
       
   127                  "baud"       : baud,
       
   128                  "parity"     : modbus_serial_parity_dict[parity],
       
   129                  "stopbits"   : stopbits,
       
   130                  "slaveid"    : slaveid
       
   131                 }
       
   132     return node_init_template % node_dict
       
   133 
       
   134 
       
   135 
       
   136  
       
   137 
       
   138 def GetRTUClientNodePrinted(self, child):
       
   139     """
       
   140     Outputs a string to be used on C files
       
   141     params: child - the correspondent subplugin in Beremiz
       
   142     """
       
   143     node_init_template = '''/*node %(locnodestr)s*/
       
   144 {"%(locnodestr)s", {naf_rtu, {.rtu = {"%(device)s", %(baud)s /*baud*/, %(parity)s /*parity*/, 8 /*data bits*/, %(stopbits)s, 0 /* ignore echo */}}}, -1 /* mb_nd */, 0 /* init_state */, %(coms_period)s /* communication period */}'''
       
   145 
       
   146     location    = ".".join(map(str, child.GetCurrentLocation()))
       
   147     device      = child.GetParamsAttributes()[0]["children"][0]["value"]
       
   148     baud        = child.GetParamsAttributes()[0]["children"][1]["value"]
       
   149     parity      = child.GetParamsAttributes()[0]["children"][2]["value"]
       
   150     stopbits    = child.GetParamsAttributes()[0]["children"][3]["value"]
       
   151     coms_period = child.GetParamsAttributes()[0]["children"][4]["value"]
       
   152     
       
   153     node_dict = {"locnodestr"  : location,
       
   154                  "device"      : device,
       
   155                  "baud"        : baud,
       
   156                  "parity"      : modbus_serial_parity_dict[parity],
       
   157                  "stopbits"    : stopbits,
       
   158                  "coms_period" : coms_period
       
   159                 }
       
   160     return node_init_template % node_dict
       
   161 
       
   162 
       
   163 
       
   164 
       
   165 def GetTCPClientNodePrinted(self, child):
       
   166     """
       
   167     Outputs a string to be used on C files
       
   168     params: child - the correspondent subplugin in Beremiz
       
   169     """
       
   170     node_init_template = '''/*node %(locnodestr)s*/
       
   171 {"%(locnodestr)s", {naf_tcp, {.tcp = {"%(host)s", "%(port)s", DEF_CLOSE_ON_SILENCE}}}, -1 /* mb_nd */, 0 /* init_state */, %(coms_period)s /* communication period */, 0 /* prev_error */}'''
       
   172 
       
   173     location = ".".join(map(str, child.GetCurrentLocation()))
       
   174     host        = child.GetParamsAttributes()[0]["children"][0]["value"]
       
   175     port        = child.GetParamsAttributes()[0]["children"][1]["value"]
       
   176     coms_period = child.GetParamsAttributes()[0]["children"][2]["value"]
       
   177 
       
   178     node_dict = {"locnodestr"  : location,
       
   179                  "host"        : host,
       
   180                  "port"        : port,
       
   181                  "coms_period" : coms_period
       
   182                 }
       
   183     return node_init_template % node_dict
       
   184 
       
   185 
       
   186 
       
   187 
       
   188 def GetClientRequestPrinted(self, child, nodeid):
       
   189     """
       
   190     Outputs a string to be used on C files
       
   191     params: child - the correspondent subplugin in Beremiz
       
   192             nodeid - on C code, each request has it's own parent node (sequential, 0..NUMBER_OF_NODES)
       
   193                      It's this parameter.
       
   194     return: None - if any definition error found
       
   195             The string that should be added on C code - if everything goes allright
       
   196     """
       
   197 
       
   198     req_init_template = '''/*request %(locreqstr)s*/
       
   199 {"%(locreqstr)s", %(nodeid)s, %(slaveid)s, %(iotype)s, %(func_nr)s, %(address)s , %(count)s,
       
   200 DEF_REQ_SEND_RETRIES, 0 /* error_code */, 0 /* prev_code */, {%(timeout_s)d, %(timeout_ns)d} /* timeout */,
       
   201 {%(buffer)s}, {%(buffer)s}}'''
       
   202     
       
   203     timeout      = int(child.GetParamsAttributes()[0]["children"][4]["value"])
       
   204     timeout_s    = int(timeout / 1000)
       
   205     timeout_ms   = timeout - (timeout_s * 1000)
       
   206     timeout_ns   = timeout_ms * 1000000
       
   207     
       
   208     request_dict = {}
       
   209 
       
   210     request_dict["locreqstr" ] = "_".join(map(str, child.GetCurrentLocation()))
       
   211     request_dict["nodeid"    ] = str(nodeid)
       
   212     request_dict["slaveid"   ] =                      child.GetParamsAttributes()[0]["children"][1]["value"]
       
   213     request_dict["address"   ] =                      child.GetParamsAttributes()[0]["children"][3]["value"]
       
   214     request_dict["count"     ] =                      child.GetParamsAttributes()[0]["children"][2]["value"]
       
   215     request_dict["timeout"   ] = timeout
       
   216     request_dict["timeout_s" ] = timeout_s
       
   217     request_dict["timeout_ns"] = timeout_ns
       
   218     request_dict["buffer"    ] = ",".join(['0'] * int(child.GetParamsAttributes()[0]["children"][2]["value"]))
       
   219     request_dict["func_nr"   ] = modbus_function_dict[child.GetParamsAttributes()[0]["children"][0]["value"]][0]
       
   220     request_dict["iotype"    ] = modbus_function_dict[child.GetParamsAttributes()[0]["children"][0]["value"]][1]
       
   221     request_dict["maxcount"  ] = modbus_function_dict[child.GetParamsAttributes()[0]["children"][0]["value"]][2]
       
   222     
       
   223     if int(request_dict["slaveid"]) not in xrange(256):
       
   224         self.GetCTRoot().logger.write_error("Modbus plugin: Invalid slaveID in TCP client request node %(locreqstr)s (Must be in the range [0..255])\nModbus plugin: Aborting C code generation for this node\n"%request_dict)
       
   225         return None
       
   226     if int(request_dict["address"]) not in xrange(65536):
       
   227         self.GetCTRoot().logger.write_error("Modbus plugin: Invalid Start Address in TCP client request node %(locreqstr)s (Must be in the range [0..65535])\nModbus plugin: Aborting C code generation for this node\n"%request_dict)
       
   228         return None
       
   229     if int(request_dict["count"]) not in xrange(1, 1+int(request_dict["maxcount"])):
       
   230         self.GetCTRoot().logger.write_error("Modbus plugin: Invalid number of channels in TCP client request node %(locreqstr)s (Must be in the range [1..%(maxcount)s])\nModbus plugin: Aborting C code generation for this node\n"%request_dict)
       
   231         return None
       
   232     if (int(request_dict["address"]) + int(request_dict["count"])) not in xrange(1,65537):
       
   233         self.GetCTRoot().logger.write_error("Modbus plugin: Invalid number of channels in TCP client request node %(locreqstr)s (start_address + nr_channels must be less than 65536)\nModbus plugin: Aborting C code generation for this node\n"%request_dict)
       
   234         return None
       
   235     
       
   236     return req_init_template % request_dict