from pycomm3 import LogixDriver, CommError from datetime import datetime as dt from time import sleep from random import choice from string import ascii_lowercase from threading import Thread from thingsboard_gateway.connectors.connector import Connector, log from thingsboard_gateway.tb_utility.tb_utility import TBUtility from thingsboard_gateway.tb_utility.tb_loader import TBModuleLoader class EthernetIPConnector(Thread, Connector): def __init__(self, gateway, config, connector_type): super().__init__() self.statistics = {'MessagesReceived': 0, 'MessagesSent': 0} # Dictionary, will save information about count received and sent messages. self.__config = config # Save configuration from the configuration file. self.__gateway = gateway # Save gateway object, we will use some gateway methods for adding devices and saving data from them. self.setName(self.__config.get("name","%s connector " % self.get_name() + ''.join(choice(ascii_lowercase) for _ in range(5)))) # get from the configuration or create name for logs. log.info("Starting Custom %s connector", self.get_name()) # Send message to logger self.daemon = True # Set self thread as daemon self.stopped = True # Service variable for check state self.__connected = False # Service variable for check connection to device self.__devices = {} # Dictionary with devices, will contain devices configurations, converters for devices and serial port objects self.__load_converters(connector_type) # Call function to load converters and save it into devices dictionary self.__connect_to_devices() # Call function for connect to devices self._connector_type = connector_type self.utilities = TBModuleLoader.import_module(self._connector_type, "Utilities") log.info('Custom connector %s initialization success.', self.get_name()) # Message to logger log.info("Devices in configuration file found: %s ", '\n'.join(device for device in self.__devices)) # Message to logger def __connect_to_devices(self): for device in self.__devices: try: log.debug(self.__devices[device]["device_config"]["ip"]) self.__devices[device]["logixdriver"] = LogixDriver(self.__devices[device]["device_config"]["ip"]) except Exception as e: log.exception(e) else: self.__gateway.add_device(self.__devices[device]["device_config"]["name"], {"connector": self}, self.__devices[device]["device_config"]["type"]) self.__connected = True def get_name(self): return self.name #self.__config["name"] def is_connected(self): return self.__connected def __load_converters(self, connector_type): devices_config = self.__config.get('devices') try: if devices_config is not None: for device_config in devices_config: if device_config.get('converter') is not None: log.debug(device_config) converter = TBModuleLoader.import_module(connector_type, device_config['converter']) self.__devices[device_config['name']] = {'converter': converter(device_config), 'device_config': device_config} else: log.error('Converter configuration for the custom connector %s -- not found, please check your configuration file.', self.get_name()) else: log.error('Section "devices" in the configuration not found. A custom connector %s has being stopped.', self.get_name()) self.close() except Exception as e: log.exception(e) def run(self): try: while True: if int(dt.timestamp(dt.now())) % self.__config["publishInterval"] == 0: log.debug("Publish Interval: %s" % dt.now()) for device in self.__devices: try: tags = list(self.__devices[device]["device_config"]["telemetry"].keys()) with self.__devices[device]["logixdriver"] as driver: data_from_device = driver.read(*tags) log.debug(data_from_device) converted_data = self.__devices[device]['converter'].convert(self.__devices[device]['device_config'], data_from_device) log.debug(converted_data) self.__gateway.send_to_storage(self.get_name(), converted_data) except CommError as ce: log.error("Error connecting to PLC") log.error(ce) except Exception as e: log.error("Error in for loop of run()") log.error(e) sleep(1) except Exception as e: log.exception(e) def open(self): log.info("Starting EthernetIP Connector") self.stopped = False self.start() def close(self): pass def on_attributes_update(self, content): log.info(content) if self.__devices.get(content["device"]) is not None: device_config = self.__devices[content["device"]].get("device_config") if device_config is not None and device_config.get("attributeUpdates") is not None: requests = device_config["attributeUpdates"] for request in requests: attribute = request.get("attributeOnThingsBoard") log.debug(attribute) if attribute is not None and attribute in content["data"]: try: value = content["data"][attribute] str_to_send = str(request["stringToDevice"].replace("${" + attribute + "}", str(value))).encode("UTF-8") self.__devices[content["device"]]["serial"].write(str_to_send) log.debug("Attribute update request to device %s : %s", content["device"], str_to_send) time.sleep(.01) except Exception as e: log.exception(e) def server_side_rpc_handler(self, content): log.debug(content) method_split = content["method"].split('_') method = None if len(method_split) > 0: method = "_".join(method_split[1:]) commands = { "ping": self.utilities.ping, "write_plc": self.utilities.write_plc } if method is not None: #check utilities for function log.debug(f"Looking for {method} in Utilities") log.info(self.__devices) if method in commands.keys(): if method == "write_plc": log.info(commands[method](self.__devices["Big Test Boi"]["logixdriver"],content)) else: log.info(commands[method](content))