msousa@1909: #!/usr/bin/env python
msousa@1909: # -*- coding: utf-8 -*-
msousa@1909:
msousa@1909: # This file is part of Beremiz, a Integrated Development Environment for
msousa@1909: # programming IEC 61131-3 automates supporting plcopen standard and CanFestival.
msousa@1909: #
msousa@1909: # Copyright (c) 2016 Mario de Sousa (msousa@fe.up.pt)
msousa@1909: #
msousa@1909: # This program is free software: you can redistribute it and/or modify
msousa@1909: # it under the terms of the GNU General Public License as published by
Edouard@2019: # the Free Software Foundation, either version 2 of the License, or
msousa@1909: # (at your option) any later version.
msousa@1909: #
msousa@1909: # This program is distributed in the hope that it will be useful,
msousa@1909: # but WITHOUT ANY WARRANTY; without even the implied warranty of
msousa@1909: # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
msousa@1909: # GNU General Public License for more details.
msousa@1909: #
msousa@1909: # You should have received a copy of the GNU General Public License
msousa@1909: # along with this program. If not, see .
Edouard@1918: #
msousa@1909: # This code is made available on the understanding that it will not be
msousa@1909: # used in safety-critical situations without a full and competent review.
msousa@1909:
msousa@1909:
Edouard@1919: from __future__ import absolute_import
Edouard@1918: import os
andrej@2432: from six.moves import xrange
andrej@2432:
Edouard@1919: from modbus.mb_utils import *
msousa@1909: from ConfigTreeNode import ConfigTreeNode
Edouard@1919: from PLCControler import LOCATION_CONFNODE, LOCATION_VAR_MEMORY
msousa@1909:
Edouard@1918: base_folder = os.path.split(os.path.dirname(os.path.realpath(__file__)))[0]
Edouard@1918: base_folder = os.path.join(base_folder, "..")
Edouard@1918: ModbusPath = os.path.join(base_folder, "Modbus")
Edouard@1918:
Edouard@1918:
Edouard@1918: #
Edouard@1918: #
Edouard@1918: #
Edouard@1918: # C L I E N T R E Q U E S T #
Edouard@1918: #
Edouard@1918: #
Edouard@1918: #
msousa@1909:
msousa@1909:
Edouard@1919: class _RequestPlug(object):
msousa@1909: XSD = """
msousa@1909:
msousa@1909:
msousa@1909:
msousa@1909:
msousa@1909:
msousa@1909:
msousa@1909:
msousa@1909:
msousa@1909:
msousa@1909:
msousa@1909:
msousa@1909:
msousa@1909:
msousa@1909:
msousa@1909:
msousa@1909:
msousa@1909:
msousa@1909:
msousa@1909:
msousa@1909:
msousa@1909:
msousa@1909:
msousa@1909:
msousa@1909:
msousa@1909:
msousa@1909:
msousa@1909:
msousa@1909:
msousa@1909:
msousa@1909:
msousa@1909:
msousa@1909:
msousa@1909:
msousa@1909:
msousa@1909:
msousa@1909:
msousa@1909:
msousa@1909:
msousa@1909:
msousa@1909: """
msousa@1909:
Edouard@1918: def GetParamsAttributes(self, path=None):
Edouard@1918: infos = ConfigTreeNode.GetParamsAttributes(self, path=path)
msousa@1909: for element in infos:
msousa@1909: if element["name"] == "ModbusRequest":
msousa@1909: for child in element["children"]:
msousa@1909: if child["name"] == "Function":
msousa@1909: list = modbus_function_dict.keys()
msousa@1909: list.sort()
msousa@1909: child["type"] = list
msousa@1909: return infos
Edouard@1918:
msousa@1909: def GetVariableLocationTree(self):
msousa@1909: current_location = self.GetCurrentLocation()
msousa@1909: name = self.BaseParams.getName()
msousa@1909: address = self.GetParamsAttributes()[0]["children"][3]["value"]
Edouard@1918: count = self.GetParamsAttributes()[0]["children"][2]["value"]
Edouard@1918: function = self.GetParamsAttributes()[0]["children"][0]["value"]
msousa@1909: # 'BOOL' or 'WORD'
Edouard@1918: datatype = modbus_function_dict[function][3]
msousa@1909: # 1 or 16
Edouard@1918: datasize = modbus_function_dict[function][4]
msousa@1909: # 'Q' for coils and holding registers, 'I' for input discretes and input registers
Edouard@1919: # datazone = modbus_function_dict[function][5]
msousa@1909: # 'X' for bits, 'W' for words
Edouard@1918: datatacc = modbus_function_dict[function][6]
msousa@1909: # 'Coil', 'Holding Register', 'Input Discrete' or 'Input Register'
Edouard@1918: dataname = modbus_function_dict[function][7]
msousa@1909: entries = []
Edouard@1918: for offset in range(address, address + count):
msousa@1909: entries.append({
msousa@1909: "name": dataname + " " + str(offset),
msousa@1909: "type": LOCATION_VAR_MEMORY,
msousa@1909: "size": datasize,
msousa@1909: "IEC_type": datatype,
msousa@1909: "var_name": "var_name",
msousa@1909: "location": datatacc + ".".join([str(i) for i in current_location]) + "." + str(offset),
msousa@1909: "description": "description",
msousa@1909: "children": []})
Edouard@1918: return {"name": name,
Edouard@1918: "type": LOCATION_CONFNODE,
Edouard@1918: "location": ".".join([str(i) for i in current_location]) + ".x",
Edouard@1918: "children": entries}
msousa@1909:
msousa@1909: def CTNGenerate_C(self, buildpath, locations):
msousa@1909: """
msousa@1909: Generate C code
msousa@1909: @param current_location: Tupple containing plugin IEC location : %I0.0.4.5 => (0,0,4,5)
msousa@1909: @param locations: List of complete variables locations \
msousa@1909: [{"IEC_TYPE" : the IEC type (i.e. "INT", "STRING", ...)
msousa@1909: "NAME" : name of the variable (generally "__IW0_1_2" style)
msousa@1909: "DIR" : direction "Q","I" or "M"
msousa@1909: "SIZE" : size "X", "B", "W", "D", "L"
msousa@1909: "LOC" : tuple of interger for IEC location (0,1,2,...)
msousa@1909: }, ...]
msousa@1909: @return: [(C_file_name, CFLAGS),...] , LDFLAGS_TO_APPEND
msousa@1909: """
msousa@1909: return [], "", False
Edouard@1918:
Edouard@1918:
Edouard@1918: #
Edouard@1918: #
Edouard@1918: #
Edouard@1918: # S E R V E R M E M O R Y A R E A #
Edouard@1918: #
Edouard@1918: #
Edouard@1918: #
Edouard@1918:
Edouard@1918: # dictionary implementing:
Edouard@1918: # key - string with the description we want in the request plugin GUI
Edouard@1918: # list - (modbus function number, request type, max count value)
Edouard@1918: modbus_memtype_dict = {
Edouard@1918: "01 - Coils": ('1', 'rw_bits', 65536, "BOOL", 1, "Q", "X", "Coil"),
Edouard@1918: "02 - Input Discretes": ('2', 'ro_bits', 65536, "BOOL", 1, "I", "X", "Input Discrete"),
Edouard@1918: "03 - Holding Registers": ('3', 'rw_words', 65536, "WORD", 16, "Q", "W", "Holding Register"),
Edouard@1918: "04 - Input Registers": ('4', 'ro_words', 65536, "WORD", 16, "I", "W", "Input Register"),
Edouard@1918: }
Edouard@1918:
msousa@1909:
Edouard@1919: class _MemoryAreaPlug(object):
msousa@1909: XSD = """
msousa@1909:
msousa@1909:
msousa@1909:
msousa@1909:
msousa@1909:
msousa@1909:
msousa@1909:
msousa@1909:
msousa@1909:
msousa@1909:
msousa@1909:
msousa@1909:
msousa@1909:
msousa@1909:
msousa@1909:
msousa@1909:
msousa@1909:
msousa@1909:
msousa@1909:
msousa@1909:
msousa@1909:
msousa@1909:
msousa@1909:
msousa@1909: """
msousa@1909:
Edouard@1918: def GetParamsAttributes(self, path=None):
Edouard@1918: infos = ConfigTreeNode.GetParamsAttributes(self, path=path)
msousa@1909: for element in infos:
msousa@1909: if element["name"] == "MemoryArea":
msousa@1909: for child in element["children"]:
msousa@1909: if child["name"] == "MemoryAreaType":
msousa@1909: list = modbus_memtype_dict.keys()
msousa@1909: list.sort()
msousa@1909: child["type"] = list
msousa@1909: return infos
msousa@1909:
msousa@1909: def GetVariableLocationTree(self):
msousa@1909: current_location = self.GetCurrentLocation()
msousa@1909: name = self.BaseParams.getName()
msousa@1909: address = self.GetParamsAttributes()[0]["children"][2]["value"]
Edouard@1918: count = self.GetParamsAttributes()[0]["children"][1]["value"]
Edouard@1918: function = self.GetParamsAttributes()[0]["children"][0]["value"]
msousa@1909: # 'BOOL' or 'WORD'
Edouard@1918: datatype = modbus_memtype_dict[function][3]
msousa@1909: # 1 or 16
Edouard@1918: datasize = modbus_memtype_dict[function][4]
msousa@1909: # 'Q' for coils and holding registers, 'I' for input discretes and input registers
Edouard@1919: # datazone = modbus_memtype_dict[function][5]
msousa@1909: # 'X' for bits, 'W' for words
Edouard@1918: datatacc = modbus_memtype_dict[function][6]
msousa@1909: # 'Coil', 'Holding Register', 'Input Discrete' or 'Input Register'
Edouard@1918: dataname = modbus_memtype_dict[function][7]
msousa@1909: entries = []
Edouard@1918: for offset in range(address, address + count):
msousa@1909: entries.append({
msousa@1909: "name": dataname + " " + str(offset),
msousa@1909: "type": LOCATION_VAR_MEMORY,
msousa@1909: "size": datasize,
msousa@1909: "IEC_type": datatype,
msousa@1909: "var_name": "var_name",
msousa@1909: "location": datatacc + ".".join([str(i) for i in current_location]) + "." + str(offset),
msousa@1909: "description": "description",
msousa@1909: "children": []})
Edouard@1918: return {"name": name,
Edouard@1918: "type": LOCATION_CONFNODE,
Edouard@1918: "location": ".".join([str(i) for i in current_location]) + ".x",
Edouard@1918: "children": entries}
msousa@1909:
msousa@1909: def CTNGenerate_C(self, buildpath, locations):
msousa@1909: """
msousa@1909: Generate C code
msousa@1909: @param current_location: Tupple containing plugin IEC location : %I0.0.4.5 => (0,0,4,5)
msousa@1909: @param locations: List of complete variables locations \
msousa@1909: [{"IEC_TYPE" : the IEC type (i.e. "INT", "STRING", ...)
msousa@1909: "NAME" : name of the variable (generally "__IW0_1_2" style)
msousa@1909: "DIR" : direction "Q","I" or "M"
msousa@1909: "SIZE" : size "X", "B", "W", "D", "L"
msousa@1909: "LOC" : tuple of interger for IEC location (0,1,2,...)
msousa@1909: }, ...]
msousa@1909: @return: [(C_file_name, CFLAGS),...] , LDFLAGS_TO_APPEND
msousa@1909: """
msousa@1909: return [], "", False
msousa@1909:
Edouard@1918:
Edouard@1918: #
Edouard@1918: #
Edouard@1918: #
Edouard@1918: # T C P C L I E N T #
Edouard@1918: #
Edouard@1918: #
Edouard@1918: #
msousa@1909:
Edouard@1919: class _ModbusTCPclientPlug(object):
msousa@1909: XSD = """
msousa@1909:
msousa@1909:
msousa@1909:
msousa@1909:
msousa@1909:
msousa@1909:
msousa@1909:
msousa@1909:
msousa@1909:
msousa@1909:
msousa@1909:
msousa@1909:
msousa@1909:
msousa@1909:
msousa@1909:
msousa@1909:
msousa@1909: """
Edouard@1918: # NOTE: Max value of 2147483647 (i32_max) for Invocation_Rate_in_ms
Edouard@1918: # corresponds to aprox 25 days.
Edouard@1918: CTNChildrenTypes = [("ModbusRequest", _RequestPlug, "Request")]
msousa@1909: # TODO: Replace with CTNType !!!
msousa@1909: PlugType = "ModbusTCPclient"
msousa@1909:
msousa@1909: # Return the number of (modbus library) nodes this specific TCP client will need
msousa@1909: # return type: (tcp nodes, rtu nodes, ascii nodes)
msousa@1909: def GetNodeCount(self):
msousa@1909: return (1, 0, 0)
msousa@1909:
msousa@1909: def CTNGenerate_C(self, buildpath, locations):
msousa@1909: """
msousa@1909: Generate C code
msousa@1909: @param current_location: Tupple containing plugin IEC location : %I0.0.4.5 => (0,0,4,5)
msousa@1909: @param locations: List of complete variables locations \
msousa@1909: [{"IEC_TYPE" : the IEC type (i.e. "INT", "STRING", ...)
msousa@1909: "NAME" : name of the variable (generally "__IW0_1_2" style)
msousa@1909: "DIR" : direction "Q","I" or "M"
msousa@1909: "SIZE" : size "X", "B", "W", "D", "L"
msousa@1909: "LOC" : tuple of interger for IEC location (0,1,2,...)
msousa@1909: }, ...]
msousa@1909: @return: [(C_file_name, CFLAGS),...] , LDFLAGS_TO_APPEND
msousa@1909: """
msousa@1909: return [], "", False
msousa@1909:
msousa@1909:
Edouard@1918: #
Edouard@1918: #
Edouard@1918: #
Edouard@1918: # T C P S E R V E R #
Edouard@1918: #
Edouard@1918: #
Edouard@1918: #
msousa@1909:
Edouard@1919: class _ModbusTCPserverPlug(object):
msousa@1909: # NOTE: the Port number is a 'string' and not an 'integer'!
Edouard@1918: # This is because the underlying modbus library accepts strings
msousa@1909: # (e.g.: well known port names!)
msousa@1909: XSD = """
msousa@1909:
msousa@1909:
msousa@1909:
msousa@1909:
msousa@1909:
msousa@1909:
msousa@1909:
msousa@1909:
msousa@1909:
msousa@1909:
msousa@1909:
msousa@1909:
msousa@1909:
msousa@1909:
msousa@1909:
msousa@1909:
msousa@1909: """
Edouard@1918: CTNChildrenTypes = [("MemoryArea", _MemoryAreaPlug, "Memory Area")]
msousa@1909: # TODO: Replace with CTNType !!!
msousa@1909: PlugType = "ModbusTCPserver"
msousa@1909:
msousa@1909: # Return the number of (modbus library) nodes this specific TCP server will need
msousa@1909: # return type: (tcp nodes, rtu nodes, ascii nodes)
msousa@1909: def GetNodeCount(self):
msousa@1909: return (1, 0, 0)
msousa@1909:
msousa@1909: # Return a list with a single tuple conatining the (location, port number)
msousa@1909: # location: location of this node in the configuration tree
msousa@1909: # port number: IP port used by this Modbus/IP server
msousa@1909: def GetIPServerPortNumbers(self):
msousa@1909: port = self.GetParamsAttributes()[0]["children"][1]["value"]
Edouard@1918: return [(self.GetCurrentLocation(), port)]
msousa@1909:
msousa@1909: def CTNGenerate_C(self, buildpath, locations):
msousa@1909: """
msousa@1909: Generate C code
msousa@1909: @param current_location: Tupple containing plugin IEC location : %I0.0.4.5 => (0,0,4,5)
msousa@1909: @param locations: List of complete variables locations \
msousa@1909: [{"IEC_TYPE" : the IEC type (i.e. "INT", "STRING", ...)
msousa@1909: "NAME" : name of the variable (generally "__IW0_1_2" style)
msousa@1909: "DIR" : direction "Q","I" or "M"
msousa@1909: "SIZE" : size "X", "B", "W", "D", "L"
msousa@1909: "LOC" : tuple of interger for IEC location (0,1,2,...)
msousa@1909: }, ...]
msousa@1909: @return: [(C_file_name, CFLAGS),...] , LDFLAGS_TO_APPEND
msousa@1909: """
msousa@1909: return [], "", False
msousa@1909:
msousa@1909:
Edouard@1918: #
Edouard@1918: #
Edouard@1918: #
Edouard@1918: # R T U C L I E N T #
Edouard@1918: #
Edouard@1918: #
Edouard@1918: #
msousa@1909:
Edouard@1919: class _ModbusRTUclientPlug(object):
msousa@1909: XSD = """
msousa@1909:
msousa@1909:
msousa@1909:
msousa@1909:
msousa@1909:
msousa@1909:
msousa@1909:
msousa@1909:
msousa@1909:
msousa@1909:
msousa@1909:
msousa@1909:
msousa@1909:
msousa@1909:
msousa@1909:
msousa@1909:
msousa@1909:
msousa@1909:
msousa@1909: """
Edouard@1918: # NOTE: Max value of 2147483647 (i32_max) for Invocation_Rate_in_ms
Edouard@1918: # corresponds to aprox 25 days.
Edouard@1918: CTNChildrenTypes = [("ModbusRequest", _RequestPlug, "Request")]
msousa@1909: # TODO: Replace with CTNType !!!
msousa@1909: PlugType = "ModbusRTUclient"
msousa@1909:
Edouard@1918: def GetParamsAttributes(self, path=None):
Edouard@1918: infos = ConfigTreeNode.GetParamsAttributes(self, path=path)
msousa@1909: for element in infos:
msousa@1909: if element["name"] == "ModbusRTUclient":
msousa@1909: for child in element["children"]:
msousa@1909: if child["name"] == "Baud_Rate":
msousa@1909: child["type"] = modbus_serial_baudrate_list
msousa@1909: if child["name"] == "Stop_Bits":
msousa@1909: child["type"] = modbus_serial_stopbits_list
msousa@1909: if child["name"] == "Parity":
msousa@1909: child["type"] = modbus_serial_parity_dict.keys()
msousa@1909: return infos
Edouard@1918:
msousa@1909: # Return the number of (modbus library) nodes this specific RTU client will need
msousa@1909: # return type: (tcp nodes, rtu nodes, ascii nodes)
msousa@1909: def GetNodeCount(self):
msousa@1909: return (0, 1, 0)
msousa@1909:
msousa@1909: def CTNGenerate_C(self, buildpath, locations):
msousa@1909: """
msousa@1909: Generate C code
msousa@1909: @param current_location: Tupple containing plugin IEC location : %I0.0.4.5 => (0,0,4,5)
msousa@1909: @param locations: List of complete variables locations \
msousa@1909: [{"IEC_TYPE" : the IEC type (i.e. "INT", "STRING", ...)
msousa@1909: "NAME" : name of the variable (generally "__IW0_1_2" style)
msousa@1909: "DIR" : direction "Q","I" or "M"
msousa@1909: "SIZE" : size "X", "B", "W", "D", "L"
msousa@1909: "LOC" : tuple of interger for IEC location (0,1,2,...)
msousa@1909: }, ...]
msousa@1909: @return: [(C_file_name, CFLAGS),...] , LDFLAGS_TO_APPEND
msousa@1909: """
msousa@1909: return [], "", False
msousa@1909:
msousa@1909:
Edouard@1918: #
Edouard@1918: #
Edouard@1918: #
Edouard@1918: # R T U S L A V E #
Edouard@1918: #
Edouard@1918: #
Edouard@1918: #
msousa@1909:
msousa@1909:
Edouard@1919: class _ModbusRTUslavePlug(object):
msousa@1909: XSD = """
msousa@1909:
msousa@1909:
msousa@1909:
msousa@1909:
msousa@1909:
msousa@1909:
msousa@1909:
msousa@1909:
msousa@1909:
msousa@1909:
msousa@1909:
msousa@1909:
msousa@1909:
msousa@1909:
msousa@1909:
msousa@1909:
msousa@1909:
msousa@1909:
msousa@1909: """
Edouard@1918: CTNChildrenTypes = [("MemoryArea", _MemoryAreaPlug, "Memory Area")]
msousa@1909: # TODO: Replace with CTNType !!!
msousa@1909: PlugType = "ModbusRTUslave"
msousa@1909:
Edouard@1918: def GetParamsAttributes(self, path=None):
Edouard@1918: infos = ConfigTreeNode.GetParamsAttributes(self, path=path)
msousa@1909: for element in infos:
msousa@1909: if element["name"] == "ModbusRTUslave":
msousa@1909: for child in element["children"]:
msousa@1909: if child["name"] == "Baud_Rate":
msousa@1909: child["type"] = modbus_serial_baudrate_list
msousa@1909: if child["name"] == "Stop_Bits":
msousa@1909: child["type"] = modbus_serial_stopbits_list
msousa@1909: if child["name"] == "Parity":
msousa@1909: child["type"] = modbus_serial_parity_dict.keys()
msousa@1909: return infos
Edouard@1918:
msousa@1909: # Return the number of (modbus library) nodes this specific RTU slave will need
msousa@1909: # return type: (tcp nodes, rtu nodes, ascii nodes)
msousa@1909: def GetNodeCount(self):
msousa@1909: return (0, 1, 0)
msousa@1909:
msousa@1909: def CTNGenerate_C(self, buildpath, locations):
msousa@1909: """
msousa@1909: Generate C code
msousa@1909: @param current_location: Tupple containing plugin IEC location : %I0.0.4.5 => (0,0,4,5)
msousa@1909: @param locations: List of complete variables locations \
msousa@1909: [{"IEC_TYPE" : the IEC type (i.e. "INT", "STRING", ...)
msousa@1909: "NAME" : name of the variable (generally "__IW0_1_2" style)
msousa@1909: "DIR" : direction "Q","I" or "M"
msousa@1909: "SIZE" : size "X", "B", "W", "D", "L"
msousa@1909: "LOC" : tuple of interger for IEC location (0,1,2,...)
msousa@1909: }, ...]
msousa@1909: @return: [(C_file_name, CFLAGS),...] , LDFLAGS_TO_APPEND
msousa@1909: """
msousa@1909: return [], "", False
msousa@1909:
Edouard@1918:
Edouard@1918: def _lt_to_str(loctuple):
Edouard@1918: return '.'.join(map(str, loctuple))
Edouard@1918:
Edouard@1918:
Edouard@1918: #
Edouard@1918: #
Edouard@1918: #
Edouard@1918: # R O O T C L A S S #
Edouard@1918: #
Edouard@1918: #
Edouard@1918: #
Edouard@1919: class RootClass(object):
msousa@1909: XSD = """
msousa@1909:
msousa@1909:
msousa@1909:
msousa@1909:
msousa@1909:
msousa@1909:
msousa@1909:
msousa@1909:
msousa@1909:
msousa@1909:
msousa@1909:
msousa@1909:
msousa@1909:
msousa@1909:
msousa@1909: """
Edouard@1919: CTNChildrenTypes = [("ModbusTCPclient", _ModbusTCPclientPlug, "Modbus TCP Client"),
Edouard@1919: ("ModbusTCPserver", _ModbusTCPserverPlug, "Modbus TCP Server"),
Edouard@1919: ("ModbusRTUclient", _ModbusRTUclientPlug, "Modbus RTU Client"),
Edouard@1919: ("ModbusRTUslave", _ModbusRTUslavePlug, "Modbus RTU Slave")]
Edouard@1918:
msousa@1909: # Return the number of (modbus library) nodes this specific instance of the modbus plugin will need
msousa@1909: # return type: (tcp nodes, rtu nodes, ascii nodes)
msousa@1909: def GetNodeCount(self):
Edouard@1918: max_remote_tcpclient = self.GetParamsAttributes()[
Edouard@1918: 0]["children"][0]["value"]
msousa@1909: total_node_count = (max_remote_tcpclient, 0, 0)
msousa@1909: for child in self.IECSortedChildren():
msousa@1909: # ask each child how many nodes it needs, and add them all up.
Edouard@1918: total_node_count = tuple(
Edouard@1918: x1 + x2 for x1, x2 in zip(total_node_count, child.GetNodeCount()))
msousa@1909: return total_node_count
msousa@1909:
Edouard@1918: # Return a list with tuples of the (location, port numbers) used by all
Edouard@1918: # the Modbus/IP servers
msousa@1909: def GetIPServerPortNumbers(self):
msousa@1909: IPServer_port_numbers = []
msousa@1909: for child in self.IECSortedChildren():
msousa@1909: if child.CTNType == "ModbusTCPserver":
msousa@1909: IPServer_port_numbers.extend(child.GetIPServerPortNumbers())
msousa@1909: return IPServer_port_numbers
msousa@1909:
msousa@1909: def CTNGenerate_C(self, buildpath, locations):
Edouard@1918: # print "#############"
Edouard@1918: # print self.__class__
Edouard@1918: # print type(self)
Edouard@1918: # print "self.CTNType >>>"
Edouard@1918: # print self.CTNType
Edouard@1918: # print "type(self.CTNType) >>>"
Edouard@1918: # print type(self.CTNType)
Edouard@1918: # print "#############"
Edouard@1918:
Edouard@1919: loc_dict = {"locstr": "_".join(map(str, self.GetCurrentLocation()))}
Edouard@1918:
msousa@1909: # Determine the number of (modbus library) nodes ALL instances of the modbus plugin will need
msousa@1909: # total_node_count: (tcp nodes, rtu nodes, ascii nodes)
msousa@1909: # Also get a list with tuples of (location, IP port numbers) used by all the Modbus/IP server nodes
msousa@1909: # This list is later used to search for duplicates in port numbers!
msousa@1909: # IPServer_port_numbers = [(location ,IPserver_port_number), ...]
msousa@1909: # location: tuple similar to (0, 3, 1) representing the location in the configuration tree "0.3.1.x"
Edouard@1918: # IPserver_port_number: a number (i.e. port number used by the
Edouard@1918: # Modbus/IP server)
msousa@1909: total_node_count = (0, 0, 0)
msousa@1909: IPServer_port_numbers = []
msousa@1909: for CTNInstance in self.GetCTRoot().IterChildren():
msousa@1909: if CTNInstance.CTNType == "modbus":
Edouard@1918: # ask each modbus plugin instance how many nodes it needs, and
Edouard@1918: # add them all up.
Edouard@1918: total_node_count = tuple(x1 + x2 for x1, x2 in zip(
Edouard@1918: total_node_count, CTNInstance.GetNodeCount()))
Edouard@1918: IPServer_port_numbers.extend(
Edouard@1918: CTNInstance.GetIPServerPortNumbers())
msousa@1909:
msousa@1909: # Search for use of duplicate port numbers by Modbus/IP servers
Edouard@1918: # print IPServer_port_numbers
msousa@1909: # ..but first define a lambda function to convert a tuple with the config tree location to a nice looking string
Edouard@1918: # for e.g., convert the tuple (0, 3, 4) to "0.3.4"
Edouard@1918:
Edouard@1918: for i in range(0, len(IPServer_port_numbers) - 1):
Edouard@1918: for j in range(i + 1, len(IPServer_port_numbers)):
msousa@1909: if IPServer_port_numbers[i][1] == IPServer_port_numbers[j][1]:
Edouard@1918: self.GetCTRoot().logger.write_warning(
andrej@2182: _("Error: Modbus/IP Servers %{a1}.x and %{a2}.x use the same port number {a3}.\n").
andrej@2179: format(
andrej@2179: a1=_lt_to_str(IPServer_port_numbers[i][0]),
andrej@2179: a2=_lt_to_str(IPServer_port_numbers[j][0]),
andrej@2179: a3=IPServer_port_numbers[j][1]))
Edouard@1918: raise Exception
Edouard@1918: # TODO: return an error code instead of raising an
Edouard@1918: # exception
Edouard@1918:
Edouard@1918: # Determine the current location in Beremiz's project configuration
Edouard@1918: # tree
msousa@1909: current_location = self.GetCurrentLocation()
Edouard@1918:
msousa@1909: # define a unique name for the generated C and h files
msousa@1909: prefix = "_".join(map(str, current_location))
Edouard@1918: Gen_MB_c_path = os.path.join(buildpath, "MB_%s.c" % prefix)
Edouard@1918: Gen_MB_h_path = os.path.join(buildpath, "MB_%s.h" % prefix)
Edouard@1918: c_filename = os.path.join(os.path.split(__file__)[0], "mb_runtime.c")
Edouard@1918: h_filename = os.path.join(os.path.split(__file__)[0], "mb_runtime.h")
msousa@1909:
msousa@1909: tcpclient_reqs_count = 0
msousa@1909: rtuclient_reqs_count = 0
msousa@1909: ascclient_reqs_count = 0
msousa@1909: tcpclient_node_count = 0
msousa@1909: rtuclient_node_count = 0
msousa@1909: ascclient_node_count = 0
msousa@1909: tcpserver_node_count = 0
msousa@1909: rtuserver_node_count = 0
msousa@1909: ascserver_node_count = 0
msousa@1909: nodeid = 0
msousa@1909: client_nodeid = 0
msousa@1909: client_requestid = 0
msousa@1909: server_id = 0
Edouard@1918:
msousa@1909: server_node_list = []
msousa@1909: client_node_list = []
msousa@1909: client_request_list = []
msousa@1909: server_memarea_list = []
msousa@1909: loc_vars = []
Edouard@1918: loc_vars_list = [] # list of variables already declared in C code!
msousa@1909: for child in self.IECSortedChildren():
Edouard@1918: # print "<<<<<<<<<<<<<"
Edouard@1918: # print "child (self.IECSortedChildren())----->"
Edouard@1918: # print child.__class__
Edouard@1918: # print ">>>>>>>>>>>>>"
Edouard@1918: #
msousa@1909: if child.PlugType == "ModbusTCPserver":
msousa@1909: tcpserver_node_count += 1
msousa@1909: new_node = GetTCPServerNodePrinted(self, child)
msousa@1909: if new_node is None:
Edouard@1918: return [], "", False
msousa@1909: server_node_list.append(new_node)
Edouard@1918: #
msousa@1909: for subchild in child.IECSortedChildren():
Edouard@1918: new_memarea = GetTCPServerMemAreaPrinted(
Edouard@1918: self, subchild, nodeid)
msousa@1909: if new_memarea is None:
Edouard@1918: return [], "", False
msousa@1909: server_memarea_list.append(new_memarea)
Edouard@1918: function = subchild.GetParamsAttributes()[
Edouard@1918: 0]["children"][0]["value"]
msousa@1909: # 'ro_bits', 'rw_bits', 'ro_words' or 'rw_words'
Edouard@1918: memarea = modbus_memtype_dict[function][1]
msousa@1909: for iecvar in subchild.GetLocations():
Edouard@1918: # print repr(iecvar)
msousa@1909: absloute_address = iecvar["LOC"][3]
Edouard@1919: start_address = int(GetCTVal(subchild, 2))
msousa@1909: relative_addr = absloute_address - start_address
Edouard@1918: # test if relative address in request specified range
Edouard@1919: if relative_addr in xrange(int(GetCTVal(subchild, 1))):
msousa@1909: if str(iecvar["NAME"]) not in loc_vars_list:
Edouard@1918: loc_vars.append("u16 *" + str(iecvar["NAME"]) + " = &server_nodes[%d].mem_area.%s[%d];" % (
Edouard@1918: server_id, memarea, absloute_address))
msousa@1909: loc_vars_list.append(str(iecvar["NAME"]))
msousa@1909: server_id += 1
Edouard@1918: #
msousa@1909: if child.PlugType == "ModbusRTUslave":
msousa@1909: rtuserver_node_count += 1
msousa@1909: new_node = GetRTUSlaveNodePrinted(self, child)
msousa@1909: if new_node is None:
Edouard@1918: return [], "", False
msousa@1909: server_node_list.append(new_node)
Edouard@1918: #
msousa@1909: for subchild in child.IECSortedChildren():
Edouard@1918: new_memarea = GetTCPServerMemAreaPrinted(
Edouard@1918: self, subchild, nodeid)
msousa@1909: if new_memarea is None:
Edouard@1918: return [], "", False
msousa@1909: server_memarea_list.append(new_memarea)
Edouard@1918: function = subchild.GetParamsAttributes()[
Edouard@1918: 0]["children"][0]["value"]
msousa@1909: # 'ro_bits', 'rw_bits', 'ro_words' or 'rw_words'
Edouard@1918: memarea = modbus_memtype_dict[function][1]
msousa@1909: for iecvar in subchild.GetLocations():
Edouard@1918: # print repr(iecvar)
msousa@1909: absloute_address = iecvar["LOC"][3]
Edouard@1919: start_address = int(GetCTVal(subchild, 2))
msousa@1909: relative_addr = absloute_address - start_address
Edouard@1918: # test if relative address in request specified range
Edouard@1919: if relative_addr in xrange(int(GetCTVal(subchild, 1))):
msousa@1909: if str(iecvar["NAME"]) not in loc_vars_list:
Edouard@1918: loc_vars.append("u16 *" + str(iecvar["NAME"]) + " = &server_nodes[%d].mem_area.%s[%d];" % (
Edouard@1918: server_id, memarea, absloute_address))
msousa@1909: loc_vars_list.append(str(iecvar["NAME"]))
msousa@1909: server_id += 1
Edouard@1918: #
msousa@1909: if child.PlugType == "ModbusTCPclient":
msousa@1909: tcpclient_reqs_count += len(child.IECSortedChildren())
msousa@1909: new_node = GetTCPClientNodePrinted(self, child)
msousa@1909: if new_node is None:
Edouard@1918: return [], "", False
msousa@1909: client_node_list.append(new_node)
msousa@1909: for subchild in child.IECSortedChildren():
Edouard@1918: new_req = GetClientRequestPrinted(
Edouard@1918: self, subchild, client_nodeid)
msousa@1909: if new_req is None:
Edouard@1918: return [], "", False
msousa@1909: client_request_list.append(new_req)
msousa@1909: for iecvar in subchild.GetLocations():
Edouard@1918: # absloute address - start address
Edouard@1919: relative_addr = iecvar["LOC"][3] - int(GetCTVal(subchild, 3))
Edouard@1918: # test if relative address in request specified range
Edouard@1919: if relative_addr in xrange(int(GetCTVal(subchild, 2))):
msousa@1909: if str(iecvar["NAME"]) not in loc_vars_list:
Edouard@1918: loc_vars.append(
Edouard@1918: "u16 *" + str(iecvar["NAME"]) + " = &client_requests[%d].plcv_buffer[%d];" % (client_requestid, relative_addr))
msousa@1909: loc_vars_list.append(str(iecvar["NAME"]))
msousa@1909: client_requestid += 1
msousa@1909: tcpclient_node_count += 1
msousa@1909: client_nodeid += 1
Edouard@1918: #
msousa@1909: if child.PlugType == "ModbusRTUclient":
msousa@1909: rtuclient_reqs_count += len(child.IECSortedChildren())
msousa@1909: new_node = GetRTUClientNodePrinted(self, child)
msousa@1909: if new_node is None:
Edouard@1918: return [], "", False
msousa@1909: client_node_list.append(new_node)
msousa@1909: for subchild in child.IECSortedChildren():
Edouard@1918: new_req = GetClientRequestPrinted(
Edouard@1918: self, subchild, client_nodeid)
msousa@1909: if new_req is None:
Edouard@1918: return [], "", False
msousa@1909: client_request_list.append(new_req)
msousa@1909: for iecvar in subchild.GetLocations():
Edouard@1918: # absloute address - start address
Edouard@1919: relative_addr = iecvar["LOC"][3] - int(GetCTVal(subchild, 3))
Edouard@1918: # test if relative address in request specified range
Edouard@1919: if relative_addr in xrange(int(GetCTVal(subchild, 2))):
msousa@1909: if str(iecvar["NAME"]) not in loc_vars_list:
Edouard@1918: loc_vars.append(
Edouard@1918: "u16 *" + str(iecvar["NAME"]) + " = &client_requests[%d].plcv_buffer[%d];" % (client_requestid, relative_addr))
msousa@1909: loc_vars_list.append(str(iecvar["NAME"]))
msousa@1909: client_requestid += 1
msousa@1909: rtuclient_node_count += 1
msousa@1909: client_nodeid += 1
msousa@1909: nodeid += 1
Edouard@1918:
Edouard@1918: loc_dict["loc_vars"] = "\n".join(loc_vars)
Edouard@1918: loc_dict["server_nodes_params"] = ",\n\n".join(server_node_list)
Edouard@1918: loc_dict["client_nodes_params"] = ",\n\n".join(client_node_list)
Edouard@1918: loc_dict["client_req_params"] = ",\n\n".join(client_request_list)
msousa@1909: loc_dict["tcpclient_reqs_count"] = str(tcpclient_reqs_count)
msousa@1909: loc_dict["tcpclient_node_count"] = str(tcpclient_node_count)
msousa@1909: loc_dict["tcpserver_node_count"] = str(tcpserver_node_count)
msousa@1909: loc_dict["rtuclient_reqs_count"] = str(rtuclient_reqs_count)
msousa@1909: loc_dict["rtuclient_node_count"] = str(rtuclient_node_count)
msousa@1909: loc_dict["rtuserver_node_count"] = str(rtuserver_node_count)
msousa@1909: loc_dict["ascclient_reqs_count"] = str(ascclient_reqs_count)
msousa@1909: loc_dict["ascclient_node_count"] = str(ascclient_node_count)
msousa@1909: loc_dict["ascserver_node_count"] = str(ascserver_node_count)
Edouard@1918: loc_dict["total_tcpnode_count"] = str(total_node_count[0])
Edouard@1918: loc_dict["total_rtunode_count"] = str(total_node_count[1])
Edouard@1918: loc_dict["total_ascnode_count"] = str(total_node_count[2])
Edouard@1918: loc_dict["max_remote_tcpclient"] = int(
Edouard@1918: self.GetParamsAttributes()[0]["children"][0]["value"])
Edouard@1918:
Edouard@1918: # get template file content into a string, format it with dict
Edouard@1918: # and write it to proper .h file
msousa@1909: mb_main = open(h_filename).read() % loc_dict
Edouard@1918: f = open(Gen_MB_h_path, 'w')
msousa@1909: f.write(mb_main)
msousa@1909: f.close()
Edouard@1918: # same thing as above, but now to .c file
msousa@1909: mb_main = open(c_filename).read() % loc_dict
Edouard@1918: f = open(Gen_MB_c_path, 'w')
msousa@1909: f.write(mb_main)
msousa@1909: f.close()
msousa@1909:
msousa@1909: LDFLAGS = []
msousa@1909: LDFLAGS.append(" \"-L" + ModbusPath + "\"")
andrej@2291: LDFLAGS.append(os.path.join(ModbusPath, "libmb.a"))
msousa@1909: LDFLAGS.append(" \"-Wl,-rpath," + ModbusPath + "\"")
Edouard@1918: # LDFLAGS.append("\"" + os.path.join(ModbusPath, "mb_slave_and_master.o") + "\"")
Edouard@1918: # LDFLAGS.append("\"" + os.path.join(ModbusPath, "mb_slave.o") + "\"")
Edouard@1918: # LDFLAGS.append("\"" + os.path.join(ModbusPath, "mb_master.o") + "\"")
Edouard@1918: # LDFLAGS.append("\"" + os.path.join(ModbusPath, "mb_tcp.o") + "\"")
Edouard@1918: # LDFLAGS.append("\"" + os.path.join(ModbusPath, "mb_rtu.o") + "\"")
Edouard@1918: # LDFLAGS.append("\"" + os.path.join(ModbusPath, "mb_ascii.o") + "\"")
Edouard@1918: # LDFLAGS.append("\"" + os.path.join(ModbusPath, "sin_util.o") + "\"")
Edouard@1913: # Target is ARM with linux and not win on x86 so winsock2 (ws2_32) library is useless !!!
Edouard@1918: # if os.name == 'nt': # other possible values: 'posix' 'os2' 'ce' 'java' 'riscos'
Edouard@1918: # LDFLAGS.append(" -lws2_32 ") # on windows we need to load winsock
Edouard@1918: # library!
Edouard@1918:
Edouard@1918: return [(Gen_MB_c_path, ' -I"' + ModbusPath + '"')], LDFLAGS, True