def _initialize_slaves(self): """Sets up the slave contexts for the server""" slaves = [] collection = self.cb_system.Collection(self.cb_auth, collectionName=self.cb_slaves) query = Query() if self.ip_address is not None: self.log.debug("Querying ClearBlade based on ip_address: {}".format(self.ip_address)) query.equalTo(COL_PROXY_IP_ADDRESS, self.ip_address) else: self.log.debug("No ip_address found in ClearBlade, querying based on non-empty slave_id") query.notEqualTo(COL_SLAVE_ID, '') rows = collection.getItems(query) self.log.debug("Found {} rows in ClearBlade adapter config".format(len(rows))) for row in rows: slave_id = int(row[COL_SLAVE_ID]) if slave_id not in slaves: slaves.append(slave_id) self[slave_id] = ClearBladeModbusProxySlaveContext(server_context=self, config=row, log=self.log) else: self.log.warning("Duplicate slave_id {} found in RTUs collection - only 1 RTU per server context" .format(slave_id))
def run_async_server(): """ The main loop instantiates one or more PyModbus servers mapped to ClearBlade Modbus proxies based on IP address and port defined in a ClearBlade platform Collection """ log = None virtual_ifs = [] err_msg = None defer_reactor = False try: parser = get_parser() user_options = parser.parse_args() local_ip_address = user_options.ip_address local_tcp_port = user_options.tcp_port net_if = user_options.net_if if user_options.log_level == 'DEBUG': _debug = True else: _debug = False HEARTBEAT = user_options.heartbeat log = headless.get_wrapping_logger(name=ADAPTER_DEVICE_ID, debug=_debug) server_log = headless.get_wrapping_logger(name="pymodbus.server", debug=_debug) log.info("Initializing ClearBlade System connection") cb_system = System(systemKey=user_options.systemKey, systemSecret=user_options.systemSecret, url=user_options.url) cb_auth = cb_system.Device(name=user_options.deviceName, key=user_options.deviceKey) cb_slave_config = user_options.slaves_collection cb_data = user_options.data_collection ip_proxies = [] proxy_ports = [] ip_address = None collection = cb_system.Collection(cb_auth, collectionName=cb_slave_config) query = Query() query.notEqualTo(COL_PROXY_IP_ADDRESS, '') rows = collection.getItems(query) for row in rows: # TODO: allow for possibility of multiple IPs with same port or same IP with multiple ports ip_address = str(row[COL_PROXY_IP_ADDRESS]) tcp_port = int(row[COL_PROXY_IP_PORT]) if ip_address not in ip_proxies: log.info("Found slave at {} on ClearBlade adapter config".format(ip_address)) ip_proxies.append(ip_address) proxy_ports.append(tcp_port) else: log.warning("Duplicate proxy IP address {} found in configuration - ignoring".format(ip_address)) log.debug("Processing {} slaves".format(len(ip_proxies))) for i in range(0, len(ip_proxies)): log.debug("Getting server context for {}".format(ip_proxies[i])) context = ClearBladeModbusProxyServerContext(cb_system=cb_system, cb_auth=cb_auth, cb_slaves_config=cb_slave_config, cb_data=cb_data, ip_address=ip_proxies[i], log=log) # Create IP aliases local_ip_address = ip_proxies[i] ip_mask = '255.255.255.0' local_tcp_port = proxy_ports[i] if sys.platform.startswith('win'): log.info("I'm on Windows!") local_ip_address = 'localhost' elif sys.platform.startswith('linux') or sys.platform.startswith('cygwin'): virtual_if = '{nif}:{alias}'.format(nif=net_if, alias=i) virtual_ifs.append(virtual_if) linux_command = "ifconfig {vif} {ip}".format(vif=virtual_if, ip=local_ip_address) if ip_mask is not None: linux_command += " netmask {mask}".format(mask=ip_mask) log.info("Creating virtual IP address / alias via $ {}".format(linux_command)) subprocess.call(linux_command, shell=True) # Create Server Identification identity = ModbusDeviceIdentification() identity.VendorName = 'PyModbus' identity.VendorUrl = 'http://github.com/bashwork/pymodbus/' identity.ProductName = 'Inmarsat/ClearBlade Modbus Server Adapter' identity.ModelName = ip_proxies[i] identity.MajorMinorRevision = '1.0' # Setup Modbus TCP Server log.info("Starting Modbus TCP server on {}:{}".format(local_ip_address, local_tcp_port)) modbus_server_args = { 'context': context, 'identity': identity, 'address': (local_ip_address, local_tcp_port), # 'console': _debug, 'defer_reactor_run': True, } if modbus_server_args['defer_reactor_run']: defer_reactor = True reactor.callInThread(StartTcpServer, **modbus_server_args) if local_ip_address == 'localhost': log.info("Windows retricted environment prevents IP alias - running localhost for {}" .format(ip_proxies[i])) break reactor.callInThread(_heartbeat, log, time.time(), HEARTBEAT) if defer_reactor: reactor.suggestThreadPoolSize(len(ip_proxies)) reactor.run() except KeyboardInterrupt: err_msg = "modbus_server_adapter.py halted by Keyboard Interrupt" if log is not None: log.info(err_msg) else: print(err_msg) sys.exit("modbus_server_adapter.py halted by Keyboard Interrupt") except Exception as e: err_msg = "EXCEPTION: {}".format(e) if log is not None: log.info(err_msg) else: print(err_msg) sys.exit("modbus_server_adapter.py halted by exception {}".format(e)) finally: if defer_reactor and reactor.running: reactor.stop() for vif in virtual_ifs: debug_msg = "Taking down virtual interface {}".format(vif) if log is not None: log.debug(debug_msg) else: print(debug_msg) linux_command = "ifconfig {} down".format(vif) subprocess.call(linux_command, shell=True) print("Exiting...")