def post(self, uid): ''' POST to control module coil output states (Make the blinkey lights go blinkey) ''' # Parse the form data parser = reqparse.RequestParser() parser.add_argument("type") parser.add_argument("output") parser.add_argument("state") parser.add_argument("ips", action='append') args = parser.parse_args() outputs = ['all_clear', 'emergency', 'lightning', 'a', 'b', 'c'] # Iterate over the outputs and send the state for each one # True = On, False = Off for idx, output in enumerate(outputs): if output == args['output']: for ip in args['ips']: c = ModbusClient(host=ip, port=502, auto_open=True, timeout=1) coil = idx + 16 c.write_single_coil(coil, args['state']) return (200)
def testPCL(): c = ModbusClient(host=getIpPLC(), port=getPortPLC(), auto_open=True) print(c) is_ok = c.write_single_coil(0, 1) print(is_ok) if is_ok: return "is ok" else: return "is not ok"
class TestClientServer(unittest.TestCase): def setUp(self): # modbus server self.server = ModbusServer(port=5020, no_block=True) self.server.start() # modbus client self.client = ModbusClient(port=5020) self.client.open() def tearDown(self): self.client.close() def test_read_and_write(self): # word space self.assertEqual(self.client.read_holding_registers(0), [0], 'Default value is 0 when server start') self.assertEqual(self.client.read_input_registers(0), [0], 'Default value is 0 when server start') # single read/write self.assertEqual(self.client.write_single_register(0, 0xffff), True) self.assertEqual(self.client.read_input_registers(0), [0xffff]) # multi-write at max size words_l = [randint(0, 0xffff)] * 0x7b self.assertEqual(self.client.write_multiple_registers(0, words_l), True) self.assertEqual(self.client.read_holding_registers(0, len(words_l)), words_l) self.assertEqual(self.client.read_input_registers(0, len(words_l)), words_l) # write over sized words_l = [randint(0, 0xffff)] * 0x7c self.assertEqual(self.client.write_multiple_registers(0, words_l), None) # bit space self.assertEqual(self.client.read_coils(0), [False], 'Default value is False when server start') self.assertEqual(self.client.read_discrete_inputs(0), [False], 'Default value is False when server start') # single read/write self.assertEqual(self.client.write_single_coil(0, True), True) self.assertEqual(self.client.read_coils(0), [True]) self.assertEqual(self.client.read_discrete_inputs(0), [True]) # multi-write at min size bits_l = [getrandbits(1)] * 0x1 self.assertEqual(self.client.write_multiple_coils(0, bits_l), True) self.assertEqual(self.client.read_coils(0, len(bits_l)), bits_l) self.assertEqual(self.client.read_discrete_inputs(0, len(bits_l)), bits_l) # multi-write at max size bits_l = [getrandbits(1)] * 0x7b0 self.assertEqual(self.client.write_multiple_coils(0, bits_l), True) self.assertEqual(self.client.read_coils(0, len(bits_l)), bits_l) self.assertEqual(self.client.read_discrete_inputs(0, len(bits_l)), bits_l) # multi-write over sized bits_l = [getrandbits(1)] * 0x7b1 self.assertEqual(self.client.write_multiple_coils(0, bits_l), None)
def modbus_write(self, progress_callback): c = ModbusClient(host="localhost", auto_open=False) connection_ok = c.open() if connection_ok: try: c.write_single_coil(0, self.coil) c.close() except: print("modbus exception") pass else: print("other") pass finally: #print("passed") pass else: print("could not open") pass
def __init__(self, modbus_address): self.areTopicsAlive = {} faultTime = rospy.get_time() #Initialise communication with ModbusClient adam = ModbusClient(host="192.168.1.3", port=502, auto_open=True, auto_close=False) rospy.Subscriber("/1/failures", TopicState, self.callback) rospy.Subscriber("/2/failures", TopicState, self.callback) #While System has not been shutdown while not rospy.is_shutdown(): #If for some reason the watchdogs or subscription fail tell PLCs if len(self.areTopicsAlive.keys()) == 0: adam.write_single_coil(int(modbus_address), False) #If watch dogs are functioning else: #Check current state of every topic for key in self.areTopicsAlive.keys(): #If there is a failure in one of the topics if self.areTopicsAlive[key] == 0: #Set pin to LOW adam.write_single_coil(int(modbus_address), False) faultTime = rospy.get_time() rospy.logerr( "System is not working. Topic %s has failed", key) #If there has been no fault for 2 secs set pin back to HIGH if rospy.get_time() - faultTime >= 2: adam.write_single_coil(int(modbus_address), True) sleep(0.2)
def state_off(self, name_x): SERVER_HOST = name_x SERVER_PORT = 502 c = ModbusClient() c.host(SERVER_HOST) c.port(SERVER_PORT) c.open() is_ok = c.write_single_coil(32768, False) if is_ok: self.first_read_label_text = str('kapali') self.image_source = "sf0.png" else: self.first_read_label_text = str('failed')
def callback(marker_array, inputs): output_address = inputs[0] input_address = inputs[1] zone_distance = inputs[2] grid_x = inputs[3] #Create a connection with the ADAM-6052 adam = ModbusClient(host="192.168.1.3", port=502, auto_open=True, auto_close=False) #Calculate distance to nearest object distance = 100 for marker in marker_array.markers: xpos = marker.pose.position.x xsize = marker.scale.x #Set distance if new distance is smaller if xpos - float(xsize) / 2 - grid_x < distance: distance = xpos - float(xsize) / 2 - grid_x a = adam.read_discrete_inputs(int(input_address)) #If it is a valid read if type(a) == type([]): #If HIGH if a[0] == 1: rospy.loginfo("System is in reduced distance mode") #If there is an obstruction less than xm detected write pin False if distance <= int(zone_distance): rospy.logwarn("Obstruction has been detected") adam.write_single_coil(int(output_address), False) #If there is an no obstruction less than xm detected write pin False else: rospy.loginfo("No Obsturction has been detected") adam.write_single_coil(int(output_address), True) #If LOW elif a[0] == 0: #If there is an obstruction detected write pin False if len(marker_array.markers) > 0: rospy.logwarn("Obstruction has been detected") adam.write_single_coil(int(output_address), False) #If there is no obstruction detected write pin True else: rospy.loginfo("No Obsturction has been detected") adam.write_single_coil(int(output_address), True) else: rospy.logwarn("Connection to adam failed")
class TestClientServer(unittest.TestCase): def setUp(self): # modbus server self.server = ModbusServer(port=5020, no_block=True) self.server.start() # modbus client self.client = ModbusClient(port=5020) self.client.open() def tearDown(self): self.client.close() def test_read_and_write(self): # word space self.assertEqual(self.client.read_holding_registers(0), [0], 'Default value is 0 when server start') self.assertEqual(self.client.read_input_registers(0), [0], 'Default value is 0 when server start') # single read/write self.assertEqual(self.client.write_single_register(0, 0xffff), True) self.assertEqual(self.client.read_input_registers(0), [0xffff]) # multi-write at max size words_l = [randint(0, 0xffff)] * 0x7b self.assertEqual(self.client.write_multiple_registers(0, words_l), True) self.assertEqual(self.client.read_holding_registers(0, len(words_l)), words_l) self.assertEqual(self.client.read_input_registers(0, len(words_l)), words_l) # write over sized words_l = [randint(0, 0xffff)] * 0x7c self.assertEqual(self.client.write_multiple_registers(0, words_l), None) # bit space self.assertEqual(self.client.read_coils(0), [False], 'Default value is False when server start') self.assertEqual(self.client.read_discrete_inputs(0), [False], 'Default value is False when server start') # single read/write self.assertEqual(self.client.write_single_coil(0, True), True) self.assertEqual(self.client.read_coils(0), [True]) self.assertEqual(self.client.read_discrete_inputs(0), [True]) # multi-write at min size bits_l = [getrandbits(1)] * 0x1 self.assertEqual(self.client.write_multiple_coils(0, bits_l), True) self.assertEqual(self.client.read_coils(0, len(bits_l)), bits_l) self.assertEqual(self.client.read_discrete_inputs(0, len(bits_l)), bits_l) # multi-write at max size bits_l = [getrandbits(1)] * 0x7b0 self.assertEqual(self.client.write_multiple_coils(0, bits_l), True) self.assertEqual(self.client.read_coils(0, len(bits_l)), bits_l) self.assertEqual(self.client.read_discrete_inputs(0, len(bits_l)), bits_l) # multi-write over sized bits_l = [getrandbits(1)] * 0x7b1 self.assertEqual(self.client.write_multiple_coils(0, bits_l), None)
def state_on(self, name_x): SERVER_HOST = name_x SERVER_PORT = 502 c = ModbusClient() c.host(SERVER_HOST) c.port(SERVER_PORT) c.open() is_ok = c.write_single_coil(32768, True) bits = c.read_holding_registers(32768) if bits: self.first_read_label_text = str('Acik') self.image_source = "sf_yesil.png" else: self.first_read_label_text = str('cannotread')
def write_single_coil(target_ip, port, target_reg, value): client = ModbusClient(host=target_ip, port=port, auto_open=True, auto_close=True, timeout=10) result = client.write_single_coil(int(target_reg), int(value)) # Writing to coil client.close() sleep(0.1) client = ModbusClient(host=target_ip, port=port, auto_open=True, auto_close=True, timeout=10) data = client.read_coils(int(target_reg), 1) # Reading the coil state if result == True: print(f'Write to coil {target_reg} successful! \nCurrent value:{data}') else: print('Write operation failed') client.close()
while True: # open or reconnect TCP to server if not c.is_open(): if not c.open(): print("unable to connect to "+SERVER_HOST+":"+str(SERVER_PORT)) # if open() is ok, write coils (modbus function 0x01) if c.is_open(): # write 4 bits in modbus address 0 to 3 print("") print("write bits") print("----------") print("") for addr in range(4): is_ok = c.write_single_coil(addr, toggle) if is_ok: print("bit #" + str(addr) + ": write to " + str(toggle)) else: print("bit #" + str(addr) + ": unable to write " + str(toggle)) time.sleep(0.5) time.sleep(1) print("") print("read bits") print("---------") print("") bits = c.read_coils(0, 4) if bits: print("bits #0 to 3: "+str(bits))
class AmpSwitch(object): def __init__(self, host, port=502, switches=(), debug=False): """ """ self.host = host self.port = port self.debug = debug self.switches = switches self.dev = None self.connect() def __str__(self): return "AmpSwitch(host=%s, port=%s, dev=%s>" % (self.host, self.port, self.dev) def setDebug(self, state): self.debug = state self.connect() def close(self): if self.dev is not None: self.dev.close() self.dev = None def connect(self): """ (re-) establish a connection to the device. """ if self.dev is None: self.dev = ModbusClient() self.dev.debug(self.debug) self.dev.host(self.host) self.dev.port(self.port) if self.dev.is_open(): return True ret = self.dev.open() if not ret: raise RuntimeError("failed to connect to %s:%s" % (self.host, self.port)) return True def readCoils(self): """ Return the state of all our switches. """ self.connect() regs = self.dev.read_coils(0, 16) return regs def setCoils(self, on=(), off=()): """Turn on and off a given set of switches. Argunents --------- on, off : list-like, or a single integer. Notes: ------ The off set is executed first. . There is a command to change all switchees at once, but I have not made it work yet. """ self.connect() if isinstance(on, int): on = on, if isinstance(off, int): off = off, regs0 = self.readCoils() regs1 = regs0[:] for c in off: ret = self.dev.write_single_coil(c, False) regs1[c] = False for c in on: ret = self.dev.write_single_coil(c, True) regs1[c] = True # ret = self.dev.write_multiple_registers(0, regs1) ret = self.readCoils() return ret def chooseCoil(self, n): return self.setCoils(on=n, off=list(range(16)))
c2.host("192.168.1.30") c2.port(502) c2.unit_id(1) c2.open() c2.close() cmds = ["config switch physical-port", "edit port4", "set status down", "end"] exec_ssh_cmds(cmds) # open or reconnect TCP to server if not c.is_open(): if not c.open(): print("unable to connect to " + SERVER_HOST + ":" + str(SERVER_PORT)) if c.is_open(): while True: # Turn pump on c.write_single_coil(1, True) # Set pump to 100% c.write_single_register(3, 0x64) val = c.read_coils(5)[0] print(val) c2.write_single_coil(1, True) if val: c.write_single_coil(1, False) time.sleep(0.2)
client = ModbusClient("192.168.68.113", 502) print("open:", client.open()) while True: action = input("action: ") client.open() try: adr = int(input("adress: ")) if action == "rr": print(f"value at {adr}: {client.read_holding_registers(adr)}") if action == "rc": print(f"value at {adr}: {client.read_coils(adr)}") if action == "wr": val = int(input("value: ")) print(f"old value at {adr}: {client.read_holding_registers(adr)}") client.write_single_register(adr, val) time.sleep(0.1) print(f"new value at {adr}: {client.read_holding_registers(adr)}") if action == "wc": val = input("value: ") print(f"old value at {adr}: {client.read_coils(adr)}") if val == "t": client.write_single_coil(adr, True) time.sleep(0.1) print(f"new value at {adr}: {client.read_coils(adr)}") else: client.write_single_coil(adr, False) time.sleep(0.1) print(f"new value at {adr}: {client.read_coils(adr)}") except: print("Try again")
SERVER_U_ID = 1 c = ModbusClient() # uncomment this line to see debug message # c.debug(True) # define modbus server host, port and unit_id c.host(SERVER_HOST) c.port(SERVER_PORT) c.unit_id(SERVER_U_ID) speed = 0.1 while True: # open or reconnect TCP to server if not c.is_open(): if not c.open(): print("unable to connect to " + SERVER_HOST + ":" + str(SERVER_PORT)) # if open() is ok, write coils if c.is_open(): is_ok = c.write_single_coil(1, True) # Solenoid on c.write_single_register(3, 0x64) # time.sleep(speed) # is_ok = c.write_single_coil(1, False) # Solenoid off #c.write_single_register(3, 0x0) time.sleep(speed)
class ClienteMODBUS(): """ Classe Cliente MODBUS """ def __init__(self, server_ip, porta, device_id=1, scan_time=0.1, valor=0, dbpath="C:\database.db"): """ Construtor """ self._scan_time = scan_time self._server_ip = server_ip self._device_id = device_id self._port = porta self._cliente = ModbusClient(host=server_ip, port=porta, unit_id=device_id) self._dbpath = dbpath self._valor = valor self._con = sqlite3.connect(self._dbpath) self._cursor = self._con.cursor() def atendimento(self): """ Método para atendimento do usuário """ try: self._cliente.open() print('\n\033[33m --> Cliente Modbus conectado..\033[m\n') except Exception as e: print('\033[31mERRO: ', e.args, '\033[m') try: atendimento = True while atendimento: print('-' * 100) print('\033[34mCliente Modbus\033[m'.center(100)) print('-' * 100) sel = input( "Qual serviço? \n1- Leitura \n2- Escrita \n3- Configuração \n4- Sair \nNº Serviço: " ) if sel == '1': self.createTable() self.createTableF01() self.createTableF02() self.createTableF03() self.createTableF04() print('\nQual tipo de dado deseja ler?') print( "1- Coil Status \n2- Input Status \n3- Holding Register \n4- Input Register" ) while True: tipo = int(input("Type: ")) if tipo > 4: print('\033[31mDigite um tipo válido..\033[m') sleep(0.5) else: break if tipo == 3 or tipo == 4: while True: val = int( input( "\n1- Decimal \n2- Floating Point \n3- Float Swapped \nLeitura: " )) if val > 3: print('\033[31mDigite um tipo válido..\033[m') sleep(0.5) else: break if val == 1: #valores decimais addr = int(input(f'\nAddress: ')) leng = int(input(f'Length: ')) nvezes = input('Quantidade de leituras: ') print('\nComeçando leitura Decimal..\n') sleep(0.5) try: for i in range(0, int(nvezes)): print(f'\033[33mLeitura {i+1}:\033[m', end='') print( self.lerDado(int(tipo), int(addr), leng)) sleep(self._scan_time) print( '\nValores lidos e inseridos no DB com sucesso!!\n' ) sleep(0.5) except Exception as e: print('\033[31mERRO: ', e.args, '\033[m') try: sleep(0.5) print( '\033[33m\nTentando novamente..\033[m') if not self._cliente.is_open(): self._cliente.open() sleep(0.5) for i in range(0, int(nvezes)): print( f'\033[33mLeitura {i + 1}:\033[m', end='') print( self.lerDado( int(tipo), int(addr), leng)) sleep(self._scan_time) print( '\nValores lidos e inseridos no DB com sucesso!!\n' ) sleep(0.5) except Exception as e: print('\033[31mERRO: ', e.args, '\033[m') print( '\nO Cliente não conseguiu receber uma resposta.. \nVoltando ao menu..\n\n' ) sleep(1.5) elif val == 2: #valores FLOAT addr = input(f'\nAddress: ') leng = int(input(f'Length: ')) nvezes = input('Quantidade de leituras: ') print('\nComeçando leitura FLOAT..\n') sleep(0.5) try: for i in range(0, int(nvezes)): print(f'\033[33mLeitura {i + 1}:\033[m', end='') print( self.lerDadoFloat( int(tipo), int(addr), leng)) sleep(self._scan_time) print( '\nValores lidos e inseridos no DB com sucesso!!\n' ) sleep(0.5) except Exception as e: print('\033[31mERRO: ', e.args, '\033[m\n') print( 'O Cliente não conseguiu receber uma resposta.. \nVoltando ao menu..\n\n' ) sleep(1.5) elif val == 3: #valores FLOAT SWAPPED addr = input(f'\nAddress: ') leng = int(input(f'Length: ')) nvezes = input('Quantidade de leituras: ') print('\nComeçando leitura FLOAT SWAPPED..\n') sleep(0.5) try: for i in range(0, int(nvezes)): print(f'\033[33mLeitura {i + 1}:\033[m', end='') print( self.lerDadoFloatSwapped( int(tipo), int(addr), leng)) sleep(self._scan_time) print( '\nValores lidos e inseridos no DB com sucesso!!\n' ) sleep(0.5) except Exception as e: print('\033[31mERRO: ', e.args, '\033[m\n') print( 'O Cliente não conseguiu receber uma resposta.. \nVoltando ao menu..\n\n' ) sleep(1.5) else: print('\033[31mSeleção inválida..\033[m\n') sleep(0.7) else: addr = input(f'\nAddress: ') leng = int(input(f'Length: ')) nvezes = input('Quantidade de leituras: ') print('\nComeçando leitura..\n') sleep(0.5) try: for i in range(0, int(nvezes)): print(f'\033[33mLeitura {i + 1}:\033[m', end='') print(self.lerDado(int(tipo), int(addr), leng)) sleep(self._scan_time) print( '\nValores lidos e inseridos no DB com sucesso!!\n' ) sleep(0.5) except Exception as e: print('\033[31mERRO: ', e.args, '\033[m\n') print( 'O Cliente não conseguiu receber uma resposta.. \nVoltando ao menu..\n\n' ) sleep(1.5) elif sel == '2': print( '\nQual tipo de dado deseja escrever? \n1- Coil Status \n2- Holding Register' ) while True: tipo = int(input("Tipo: ")) if tipo > 2: print('\033[31mDigite um tipo válido..\033[m') sleep(0.5) else: break addr = input(f'Digite o endereço: ') valor = int(input(f'Digite o valor que deseja escrever: ')) try: print('\nEscrevendo..') sleep(0.5) self.escreveDado(int(tipo), int(addr), valor) except Exception as e: print('\033[31mERRO: ', e.args, '\033[m') print( '\nO Cliente não conseguiu escrever.. \nVoltando ao menu..\n\n' ) sleep(1.5) elif sel == '3': print('') print('-' * 100) print('Configurações de Leitura'.center(100)) print( f'\n\033[32m->\033[m Configuração atual: - IP Addrs: \033[35m{self._server_ip}\033[m - TCP Port: \033[35m{self._port}\033[m - Device ID: \033[35m{self._device_id}\033[m - Scan_Time: \033[35m{self._scan_time}\033[ms' ) print( '\nQual tipo de configuração deseja fazer? \n1- Endereço IP \n2- Porta TCP \n3- Device ID \n4- ScanTime \n5- Voltar' ) config = int(input("Configuração: ")) if config == 1: ipserv = str(input(' Novo endereço IP: ')) try: self._cliente.close() self._server_ip = ipserv self._cliente = ModbusClient(host=self._server_ip) self._cliente.open() print( f'\nServer IP alterado para {ipserv} com sucesso!!\n' ) sleep(0.5) except Exception as e: print('\033[31mERRO: ', e.args, '\033[m') print( '\nNão foi possível alterar o endereço IP.. \nVoltando ao menu..\n\n' ) sleep(0.5) elif config == 2: porttcp = input(' Nova porta TCP: ') try: self._cliente.close() self._port = int(porttcp) self._cliente = ModbusClient(port=self._port) self._cliente.open() print( f'\nTCP port alterado para {porttcp} com sucesso!!\n' ) sleep(0.5) except Exception as e: print('\033[31mERRO: ', e.args, '\033[m') print( '\nNão foi possível alterar a porta.. \nVoltando ao menu..\n\n' ) sleep(0.5) elif config == 3: while True: iddevice = input(' Novo device ID: ') if 0 <= int(iddevice) < 256: break else: print( '\033[31mDevice ID deve ser um número inteiro entre 0 e 256.\033[m', end='') sleep(0.5) try: self._cliente.close() self._device_id = int(iddevice) self._cliente = ModbusClient( unit_id=self._device_id) self._cliente.open() print( f'\nDevice ID alterado para {iddevice} com sucesso!!\n' ) sleep(0.5) except Exception as e: print('\033[31mERRO: ', e.args, '\033[m') print( '\nNão foi possível alterar o ID do device.. \nVoltando ao menu..\n\n' ) sleep(0.5) elif config == 4: scant = input(' Novo tempo de varredura [s]: ') try: self._scan_time = float(scant) print( f'\nScan_time alterado para {scant}s com sucesso!!\n' ) except Exception as e: print('\033[31mERRO: ', e.args, '\033[m') print( '\nNão foi possível alterar o tempo de varredura.. \nVoltando ao menu..\n\n' ) sleep(0.5) elif config == 5: print('\nVoltando ao menu inicial..\n') sleep(0.5) else: print('\033[31mSeleção inválida..\033[m\n') sleep(0.7) elif sel == '4': sleep(0.2) print('\n\033[32mFechando sistema..\033[m') sleep(0.5) self._cliente.close() atendimento = False else: print('\033[31mSeleção inválida..\033[m\n') sleep(0.7) except Exception as e: print('\033[31mERRO: ', e.args, '\033[m') def createTable(self): """ Método que cria a tabela para armazenamento dos dados, caso ela não exista """ try: sql_str = f""" CREATE TABLE IF NOT EXISTS pointValues ( ID INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, Address TEXT, Type TEXT, Display TEXT, Value REAL, TimeStamp1 TEXT NOT NULL) """ self._cursor.execute(sql_str) self._con.commit() except Exception as e: print('\033[31mERRO: ', e.args, '\033[m') def inserirDB(self, addrs, tipo, disp, value): """ Método para inserção dos dados no DB """ try: date = str( datetime.datetime.fromtimestamp(int( time.time())).strftime("%Y-%m-%d %H:%M:%S")) str_values = f"'{addrs}', {tipo}, {disp}, {value}, '{date}'" sql_str = f'INSERT INTO pointValues (Address, Type, Display, Value, TimeStamp1) VALUES ({str_values})' self._cursor.execute(sql_str) self._con.commit() except Exception as e: print('\033[31mERRO: ', e.args, '\033[m') def createTableF01(self): """ Método que cria a tabela para armazenamento dos dados, caso ela não exista """ try: sql_str = f""" CREATE TABLE IF NOT EXISTS pointValuesF01 ( ID INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, Address TEXT, Type TEXT, Display TEXT, Value REAL, TimeStamp1 TEXT NOT NULL) """ self._cursor.execute(sql_str) self._con.commit() except Exception as e: print('\033[31mERRO: ', e.args, '\033[m') def createTableF02(self): """ Método que cria a tabela para armazenamento dos dados, caso ela não exista """ try: sql_str = f""" CREATE TABLE IF NOT EXISTS pointValuesF02 ( ID INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, Address TEXT, Type TEXT, Display TEXT, Value REAL, TimeStamp1 TEXT NOT NULL) """ self._cursor.execute(sql_str) self._con.commit() except Exception as e: print('\033[31mERRO: ', e.args, '\033[m') def createTableF03(self): """ Método que cria a tabela para armazenamento dos dados, caso ela não exista """ try: sql_str = f""" CREATE TABLE IF NOT EXISTS pointValuesF03 ( ID INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, Address TEXT, Type TEXT, Display TEXT, Value REAL, TimeStamp1 TEXT NOT NULL) """ self._cursor.execute(sql_str) self._con.commit() except Exception as e: print('\033[31mERRO: ', e.args, '\033[m') def createTableF04(self): """ Método que cria a tabela para armazenamento dos dados, caso ela não exista """ try: sql_str = f""" CREATE TABLE IF NOT EXISTS pointValuesF04 ( ID INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, Address TEXT, Type TEXT, Display TEXT, Value REAL, TimeStamp1 TEXT NOT NULL) """ self._cursor.execute(sql_str) self._con.commit() except Exception as e: print('\033[31mERRO: ', e.args, '\033[m') def inserirDBF0(self, addrs, tipo, disp, value): """ Método para inserção dos dados no DB """ try: if tipo == "'F01-CoilStatus'": date = str( datetime.datetime.fromtimestamp(int( time.time())).strftime("%Y-%m-%d %H:%M:%S")) str_values = f"'{addrs}', {tipo}, {disp}, {value}, '{date}'" sql_str = f'INSERT INTO pointValuesF01 (Address, Type, Display, Value, TimeStamp1) VALUES ({str_values})' self._cursor.execute(sql_str) self._con.commit() elif tipo == "'F02-InputStatus'": date = str( datetime.datetime.fromtimestamp(int( time.time())).strftime("%Y-%m-%d %H:%M:%S")) str_values = f"'{addrs}', {tipo}, {disp}, {value}, '{date}'" sql_str = f'INSERT INTO pointValuesF02 (Address, Type, Display, Value, TimeStamp1) VALUES ({str_values})' self._cursor.execute(sql_str) self._con.commit() elif tipo == "'F03-HoldingRegister'": date = str( datetime.datetime.fromtimestamp(int( time.time())).strftime("%Y-%m-%d %H:%M:%S")) str_values = f"'{addrs}', {tipo}, {disp}, {value}, '{date}'" sql_str = f'INSERT INTO pointValuesF03 (Address, Type, Display, Value, TimeStamp1) VALUES ({str_values})' self._cursor.execute(sql_str) self._con.commit() elif tipo == "'F04-InputRegister'": date = str( datetime.datetime.fromtimestamp(int( time.time())).strftime("%Y-%m-%d %H:%M:%S")) str_values = f"'{addrs}', {tipo}, {disp}, {value}, '{date}'" sql_str = f'INSERT INTO pointValuesF04 (Address, Type, Display, Value, TimeStamp1) VALUES ({str_values})' self._cursor.execute(sql_str) self._con.commit() except Exception as e: print('\033[31mERRO: ', e.args, '\033[m') def inserirDBFP(self, addrs, tipo, disp, value): """ Método para inserção dos dados no DB """ try: if tipo == "'F03-HoldingRegister'": date = str( datetime.datetime.fromtimestamp(int( time.time())).strftime("%Y-%m-%d %H:%M:%S")) str_values = f"'{addrs}', {tipo}, {disp}, {value}, '{date}'" sql_str = f'INSERT INTO pointValuesF03 (Address, Type, Display, Value, TimeStamp1) VALUES ({str_values})' self._cursor.execute(sql_str) self._con.commit() elif tipo == "'F04-InputRegister'": date = str( datetime.datetime.fromtimestamp(int( time.time())).strftime("%Y-%m-%d %H:%M:%S")) str_values = f"'{addrs}', {tipo}, {disp}, {value}, '{date}'" sql_str = f'INSERT INTO pointValuesF04 (Address, Type, Display, Value, TimeStamp1) VALUES ({str_values})' self._cursor.execute(sql_str) self._con.commit() else: print( "Erro ao inserir no DB com Floating Point e Float Swapped!!" ) except Exception as e: print('\033[31mERRO: ', e.args, '\033[m') def lerDado(self, tipo, addr, leng=1): """ Método para leitura MODBUS """ if tipo == 1: co = self._cliente.read_coils(addr - 1, leng) ic = 0 while ic <= leng: if ic == leng: break else: value = co[0 + ic] ic += 1 # print(value) if value == True: value = 1 else: value = 0 ende = str(addr + ic - 1).zfill(5) self.inserirDB(addrs=str(ende), tipo="'F01-CoilStatus'", disp="'Booleano'", value=value) self.inserirDBF0(addrs=str(ende), tipo="'F01-CoilStatus'", disp="'Booleano'", value=value) return co elif tipo == 2: di = self._cliente.read_discrete_inputs(addr - 1, leng) idi = 0 while idi <= leng: if idi == leng: break else: value = di[0 + idi] idi += 1 # print(value) self.inserirDB(addrs=(10000 + addr + idi - 1), tipo="'F02-InputStatus'", disp="'Booleano'", value=value) self.inserirDBF0(addrs=(10000 + addr + idi - 1), tipo="'F02-InputStatus'", disp="'Booleano'", value=value) return di elif tipo == 3: hr = self._cliente.read_holding_registers(addr - 1, leng) ihr = 0 while ihr <= leng: if ihr == leng: break else: value = hr[0 + ihr] ihr += 1 # print(value) self.inserirDB(addrs=(40000 + addr + ihr - 1), tipo="'F03-HoldingRegister'", disp="'Decimal'", value=value) self.inserirDBF0(addrs=(40000 + addr + ihr - 1), tipo="'F03-HoldingRegister'", disp="'Decimal'", value=value) return hr elif tipo == 4: ir = self._cliente.read_input_registers(addr - 1, leng) iir = 0 while iir <= leng: if iir == leng: break else: value = ir[0 + iir] iir += 1 # print(value) self.inserirDB(addrs=(30000 + addr + iir - 1), tipo="'F04-InputRegister'", disp="'Decimal'", value=value) self.inserirDBF0(addrs=(30000 + addr + iir - 1), tipo="'F04-InputRegister'", disp="'Decimal'", value=value) return ir else: print('Tipo de leitura inválido..') def lerDadoFloat(self, tipo, addr, leng): """ Método para leitura FLOAT MODBUS """ i = 0 g = 0 e1 = [] listfloat = [] while i < leng: if tipo == 3: i1 = self._cliente.read_holding_registers(addr - 1 + g, 2) tipore = "'F03-HoldingRegister'" ende = 40000 elif tipo == 4: i1 = self._cliente.read_input_registers(addr - 1 + g, 2) tipore = "'F04-InputRegister'" ende = 30000 else: print('Tipo inválido..') for x in i1: x = bin(x).lstrip("0b") e1.insert(0 + g, x) i += 1 g += 2 e = 0 while e <= leng: e2 = '' for x in e1: e2 = str(f'{e2}{x.rjust(16, "0")} ') e += 1 b2 = str(f'{e2}') e3 = b2.split() y = 0 while y < len(e3): ieee = f'{e3[0+y]}{e3[1+y]}' sign = int(ieee[0]) expo = str(ieee[1:9]) expodec = 0 expopot = 7 for i in range(8): expodec = expodec + (int(expo[i]) * (2**expopot)) expopot -= 1 mant = str(ieee[9:]) mantdec = 0 mantpot = -1 for i in range(23): mantdec = mantdec + (int(mant[i]) * (2**mantpot)) mantpot -= 1 value = ((-1)**sign) * (1 + mantdec) * 2**(expodec - 127) # print(f'{round(value, 3)}') listfloat.append(round(value, 3)) y += 2 self.inserirDB(addrs=(ende + addr + y - 2), tipo=tipore, disp="'Floating Point'", value=round(value, 3)) self.inserirDBFP(addrs=(ende + addr + y - 2), tipo=tipore, disp="'Floating Point'", value=round(value, 3)) return listfloat def lerDadoFloatSwapped(self, tipo, addr, leng): """ Método para leitura FLOAT SWAPPED MODBUS """ i = 0 g = 0 e1 = [] listfloatsp = [] while i < leng: if tipo == 3: i1 = self._cliente.read_holding_registers(addr - 1 + g, 2) tipore = "'F03-HoldingRegister'" ende = 40000 elif tipo == 4: i1 = self._cliente.read_input_registers(addr - 1 + g, 2) tipore = "'F04-InputRegister'" ende = 30000 else: print('Tipo inválido..') i2 = i1[::-1] for x in i2: x = bin(x).lstrip("0b") e1.insert(0 + g, x) i += 1 g += 2 e = 0 while e <= leng: e2 = '' for x in e1: e2 = str(f'{e2}{x.rjust(16, "0")} ') e += 1 b2 = str(f'{e2}') e3 = b2.split() y = 0 while y < len(e3): ieee = f'{e3[0+y]}{e3[1+y]}' sign = int(ieee[0]) expo = str(ieee[1:9]) expodec = 0 expopot = 7 for i in range(8): expodec = expodec + (int(expo[i]) * (2**expopot)) expopot -= 1 mant = str(ieee[9:]) mantdec = 0 mantpot = -1 for i in range(23): mantdec = mantdec + (int(mant[i]) * (2**mantpot)) mantpot -= 1 value = ((-1)**sign) * (1 + mantdec) * 2**(expodec - 127) # print(f'{round(value, 3)}') listfloatsp.append(round(value, 3)) y += 2 self.inserirDB(addrs=(ende + addr + y - 2), tipo=tipore, disp="'Float (Swapped)'", value=round(value, 3)) self.inserirDBFP(addrs=(ende + addr + y - 2), tipo=tipore, disp="'Float (Swapped)'", value=round(value, 3)) return listfloatsp def escreveDado(self, tipo, addr, valor): """ Método para escrita MODBUS """ try: if tipo == 1: print( f'\033[33mValor {valor} escrito no endereço {addr}\033[m\n' ) return self._cliente.write_single_coil(addr - 1, valor) elif tipo == 2: print( f'\033[33mValor {valor} escrito no endereço {addr}\033[m\n' ) return self._cliente.write_single_register(addr - 1, valor) else: print('Tipo de escrita inválido..\n') except Exception as e: print('\033[31mERRO: ', e.args, '\033[m')
def onCommand(self, u, Command, Level, Hue): Domoticz.Debug( str(Devices[u].DeviceID) + ": onCommand called: Parameter '" + str(Command) + "', Level: " + str(Level)) try: client = ModbusClient(host=self.TCP_IP, port=int(self.TCP_PORT), unit_id=int(1), auto_open=True, auto_close=True, timeout=2) except: Domoticz.Error("Can not connect to Modbus TCP/IP: " + self.TCP_IP + ":" + self.TCP_PORT) try: # == ControlMode : man = 1, auto = 0, docasny = 2 | "|Manuál|Automat|Dočasný" if (u == self.uControlMode): #if Level == 10: atreaControlMode = 1 if Level == 20: atreaControlMode = 0 elif Level == 30: atreaControlMode = 2 client.write_single_register(1015, atreaControlMode) client.write_single_register(1016, atreaControlMode) # == HeatingSeason : 0 = non-heating, 1 = heating elif (u == self.uHeatingSeason): client.write_single_coil(1200, int(Command == "On")) # == NightlyCooling : 0 = recuperation, 1 = cooling elif (u == self.uNightlyCooling): client.write_single_coil(902, int(Command == "On")) # == uPowerCur # == uPowerReq elif (u == self.uPowerCur): atreaPower = int(self._powerDomoticzValueToPercent(Level)) Domoticz.Debug( str(Devices[u].DeviceID) + ": COMMAND: Level=" + str(Level) + ", atreaPower='" + str(atreaPower) + "'") client.write_single_register(1009, atreaPower) client.write_single_register(1013, atreaPower) client.write_single_register(1015, 2) client.write_single_register(1016, 2) # == ModeCur "Vypnuto | Periodické větrání | Větrání |Čidlo vlhkosti|IN2|D1|D2|Koupelny+WC|Odsavač kuchyň|Náběh|Doběh|Odmrazování rekuperátoru" # == ModeReq "Vypnuto | Periodické větrání | Větrání" # H01008 Nastavení požadovaného režimu, pokud H01015=1, 0 = Periodické větrání, 1 = Větrání # H01012 Nastavení požadovaného režimu, pokud H01015= 0/2, 0 = Vypnuto, 1 = Periodické větrání, 2 = Větrání elif (u == self.uModeCur): if Level == 0: atreaMode = 1 self.onCommand(self.uPowerCur, Command, 0, Hue) elif Level == 10: atreaMode = 0 elif Level == 20: atreaMode = 1 Domoticz.Debug( str(Devices[u].DeviceID) + ": COMMAND: Level=" + str(Level) + ", atreaMode='" + str(atreaMode) + "'") client.write_single_register(1008, atreaMode) client.write_single_register(1012, atreaMode) client.write_single_register(1015, 2) client.write_single_register(1016, 2) except: Domoticz.Error( str(Devices[u].DeviceID) + ": Modbus update error. Check it out!") # Update values now! self.onHeartbeat() return
SERVER_PORT = 502 SERVER_U_ID = 1 c = ModbusClient() # uncomment this line to see debug message # c.debug(True) # define modbus server host, port and unit_id c.host(SERVER_HOST) c.port(SERVER_PORT) c.unit_id(SERVER_U_ID) # open or reconnect TCP to server if not c.is_open(): if not c.open(): print("unable to connect to " + SERVER_HOST + ":" + str(SERVER_PORT)) # if open() is ok, write coils if c.is_open(): for addr in [0, 1, 2, 3]: if args.on: is_ok = c.write_single_coil(addr, True) # Solenoid on elif args.off: is_ok = c.write_single_coil(addr, False) # Solenoid on else: print("No command given") break # time.sleep(1) # is_ok = c.write_single_coil(addr, False) # Solenoid off
pass elif (keyboard.is_pressed('p')): aux = vOut + 10 vOut = 100 if (aux > 90) else aux pass #System canoIn = True if (vIn > 0) else False canoOut = True if (vOut > 0) else False aux = nivel + vIn / 10 - vOut / 10 nivel = 100 if (aux > 99) else (0 if (aux < 1) else aux) ledAlarm = True if (nivel >= 75) else False vIn = 0 if (nivel == 100) else vIn # Sending Data to ScadaBr c.write_single_register(0, vIn) c.write_single_register(1, vOut) c.write_single_register(2, nivel) c.write_single_coil(3, canoIn) c.write_single_coil(4, canoOut) c.write_single_coil(5, ledAlarm) # Terminal Data Printing print("vIn: %d" % vIn) print("vOut: %d" % vOut) print("Nivel: %d" % nivel) # Waiting Time time.sleep(0.2)
"https://www.googleapis.com/robot/v1/metadata/x509/firebase-adminsdk-ygdjt%40schneider-project.iam.gserviceaccount.com" }) # Initialize the app with a service account, granting admin privileges firebase_admin.initialize_app( cred, {'databaseURL': 'https://schneider-project.firebaseio.com/'}) c = ModbusClient() c.host("192.168.0.112") c.port(502) c.open() ref = db.reference('connect_plc') value = ref.get() app1 = value["appliance1"] app2 = value["appliance2"] app3 = value["appliance3"] c.write_single_coil(0, app1) c.write_single_coil(1, app2) c.write_single_coil(2, app3) status_connect = 0 time_disconnect = "" time_reconnect = "" time_delay = 0 #Callbacks def on_connect(client, userdata, flags, rc): print("Connected with Code :" + str(rc)) # Subscribe Topic client.subscribe("esp8266")
主站写一个 coil,从站读取该值,并将该值赋值给 discrete_input,然后主站再读取该值 主站写一个 holding_reg,从站读取该值,并赋值给 input_reg,然后主站再读取该值 """ coil_value = False holding_reg_value = 1 range_value = 100 while True: if not c.is_open(): logger.error("not open") time.sleep(2) continue # 写 coils c.write_single_coil(1, coil_value) logger.debug(f"写 coil,值为 {coil_value}") coil_value = not coil_value time.sleep(1) # 读 discrete_inputs inputs = c.read_discrete_inputs(1) if inputs is None: logger.error("读 discrete_inputs 失败") time.sleep(2) continue logger.debug(f"读 discrete_input,值为 {inputs[0]}") # 写 holding_registers c.write_single_register(1, holding_reg_value)
class blower(): #_____________________________________________________________________________ def __init__(self, machine, name): builder.SetDeviceName(name) self.com = ModbusClient(host=machine, port=4000, auto_open=True) #4000 self.com.mode(constants.MODBUS_RTU) stat = self.com.open() self.pv_stat = builder.aIn("stat") self.pv_stat.PREC = 1 self.pv_stat.LOPR = 0 self.pv_stat.HOPR = 100 self.pv_temp = builder.aIn("temp") self.pv_temp.PREC = 1 self.pv_temp.LOPR = 0 self.pv_temp.HOPR = 100 self.pv_humi = builder.aIn("humidity") self.pv_humi.PREC = 1 self.pv_humi.LOPR = 0 self.pv_humi.HOPR = 100 self.pv_humi.HSV = "MINOR" self.pv_humi.HHSV = "MAJOR" self.pv_humi.HIGH = 45 self.pv_humi.HIHI = 50 self.pv_flow = builder.aIn("flow") self.pv_flow.PREC = 0 self.pv_flow.LOPR = 0 self.pv_flow.HOPR = 600 self.pv_flow.LOLO = 250 self.pv_flow.LOW = 300 self.pv_flow.HIGH = 480 self.pv_flow.HIHI = 520 self.pv_flow.LSV = "MINOR" self.pv_flow.LLSV = "MAJOR" self.pv_flow.HSV = "MINOR" self.pv_flow.HHSV = "MAJOR" self.stat_pv = builder.boolIn("status", ZNAM="off", ONAM="on", DESC=name) self.stat_pv.ZSV = "MAJOR" self.pv_on = builder.boolOut("on", ZNAM="0", ONAM="1", HIGH=0.1, on_update=self.turnOn) self.pv_off = builder.boolOut("off", ZNAM="0", ONAM="1", HIGH=0.1, on_update=self.turnOff) self.busy = False self.pv_act = builder.boolOut("activity", ZNAM="0", ONAM="1", HIGH=1) self.pv_was_on = builder.boolOut("was_on", ZNAM="0", ONAM="1", HIGH=1.5) self.pv_was_off = builder.boolOut("was_off", ZNAM="0", ONAM="1", HIGH=1.5) self.id_temp = 0 self.id_stat = 1 #_____________________________________________________________________________ def start_monit_loop(self): t = threading.Thread(target=self.monitor_loop) t.daemon = True t.start() #_____________________________________________________________________________ def monitor_loop(self): while True: time.sleep(2) try: if self.busy == False: self.read_YOGOGAWA() except TypeError: print "monitor_loop: reading skipped due to external write, busy:", self.busy self.busy = False #_____________________________________________________________________________ def get_1dig(self, i): #print i return float(i) * 0.1 #_____________________________________________________________________________ def read_YOGOGAWA(self): self.busy = True self.com.unit_id(1) resp = self.com.read_holding_registers(1, 2) self.pv_stat.set(self.get_1dig(resp[self.id_stat])) self.pv_temp.set(self.get_1dig(resp[self.id_temp])) #self.pv_stat.set(resp[0]) #self.pv_temp.set(resp[1]) #self.get_1dig(resp[0]) #self.get_1dig(resp[1]) self.com.unit_id(2) resp = self.com.read_holding_registers(1, 1)[0] self.pv_humi.set(self.get_1dig(resp)) self.com.unit_id(3) self.pv_flow.set(get_2comp(self.com.read_holding_registers(1, 1)[0])) self.busy = False self.pv_act.set(1) #_____________________________________________________________________________ def turnOn(self, val): if val == 0: return while self.busy == True: print "turnOn: waiting for busy to clear" time.sleep(0.2) self.busy = True self.com.unit_id(5) self.com.write_single_coil(0, True) self.busy = False #print("funtion turnOn done") self.pv_was_on.set(1) #_____________________________________________________________________________ def turnOff(self, val): if val == 0: return while self.busy == True: print "turnOff: waiting for busy to clear" time.sleep(0.2) self.busy = True self.com.unit_id(5) self.com.write_single_coil(1, True) self.busy = False #print("function turnOff done") self.pv_was_off.set(1)
def write(ip, output, state): # Initialize modbus c = ModbusClient(host=ip, port=502, auto_open=True, timeout=1) coil = output+16 c.write_single_coil(coil, state)
def set_start_mode_ramp(ModbusClient): # Sets the start mode to ramp # c is ModbusClient wr = ModbusClient.write_single_coil(2,2) print('set_start_mode_ramp:' + str(int(wr))) return wr
class Device(): def __init__(self, host, port, timeout, byteorder=BE): # big_endian : Byte order of the device memory structure # True >> big endian # False >> little endian if byteorder == BE: self.big_endian=True else: self.big_endian=False self.dev = ModbusClient() self.dev.host(host) self.dev.port(port) self.dev.timeout(timeout) self.dev.open() #self.dev.debug = True #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ READ METHODS ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~# #Method to read binary variable def read_bits(self, VarNameList, AddressList, functioncode=2): # Arguments: # VarNameList : list of variable name # AddressList : list of variable register address in decimal (relative address) # functioncode : functioncode for modbus reading operation # 1 >> for Discrete Output (Coils) # 2 >> for Discrete Input # Return : dictionary of variable name and its value self.values = [] if functioncode == 1: for address in AddressList: self.values.extend(self.dev.read_coils(address[0], len(address))) elif functioncode == 2: for address in AddressList: self.values.extend(self.dev.read_discrete_inputs(address[0], len(address))) self.Result = dict(zip(VarNameList, self.values)) return self.Result #Method to read INT16 or UINT16 variable def read_INT16(self, VarNameList, AddressList, MultiplierList, signed=False, roundto=3, functioncode=3): # Arguments: # VarNameList : list of variable name # AddressList : list of variable register address in decimal (relative address) # MultiplierList : list of multiplier # roundto : number of digits after decimal point # any positive integer number >> to limit the number of digits after decimal point # None >> to disable # signed : True >> for signed values # False >> for unsigned values # functioncode : functioncode for modbus reading operation # 3 >> for Holding Register # 4 >> for Input Register # Return : dictionary of variable name and its value self.values = [] if functioncode == 3: for address in AddressList: self.values.extend(self.dev.read_holding_registers(address[0],len(address))) elif functioncode == 4: for address in AddressList: self.values.extend(self.dev.read_input_registers(address[0],len(address))) if signed: self.values = UINT16toINT16(self.values) for i in range(0, len(self.values)): self.values[i] = round(self.values[i]*MultiplierList[i],roundto) self.Result = dict(zip(VarNameList, self.values)) return self.Result #Method to read INT32 or UINT32 variable def read_INT32(self, VarNameList, AddressList, MultiplierList, signed=False, roundto=3, functioncode=3): # Arguments: # VarNameList : list of variable name # AddressList : list of variable register address in decimal (relative address) # MultiplierList : list of multiplier # roundto : number of digits after decimal point # any positive integer number >> to limit the number of digits after decimal point # None >> to disable # signed : True >> for signed values # False >> for unsigned values # functioncode : functioncode for modbus reading operation # 3 >> for Holding Register # 4 >> for Input Register # Return : dictionary of variable name and its value self.values = [] if functioncode == 3: for address in AddressList: self.values.extend(self.dev.read_holding_registers(address[0],len(address))) elif functioncode == 4: for address in AddressList: self.values.extend(self.dev.read_input_registers(address[0],len(address))) self.values = UINT16toINT32(self.values, self.big_endian, signed) for i in range(0, len(self.values)): self.values[i] = round(self.values[i]*MultiplierList[i], roundto) self.Result = dict(zip(VarNameList, self.values)) return self.Result #Method to read INT64 or UINT64 variable def read_INT64(self, VarNameList, AddressList, MultiplierList, signed=False, roundto=3, functioncode=3): # Arguments: # VarNameList : list of variable name # AddressList : list of variable register address in decimal (relative address) # MultiplierList : list of multiplier # roundto : number of digits after decimal point # any positive integer number >> to limit the number of digits after decimal point # None >> to disable # signed : True >> for signed values # False >> for unsigned values # functioncode : functioncode for modbus reading operation # 3 >> for Holding Register # 4 >> for Input Register # Return : dictionary of variable name and its value self.values = [] if functioncode == 3: for address in AddressList: self.values.extend(self.dev.read_holding_registers(address[0],len(address))) elif functioncode == 4: for address in AddressList: self.values.extend(self.dev.read_input_registers(address[0],len(address))) self.values = UINT16toINT64(self.values, self.big_endian, signed) for i in range(0, len(self.values)): self.values[i] = round(self.values[i]*MultiplierList[i], roundto) self.Result = dict(zip(VarNameList, self.values)) return self.Result #Method to read FLOAT16 variable def read_FLOAT16(self, VarNameList, AddressList, MultiplierList, roundto=3, functioncode=3): # Arguments: # VarNameList : list of variable name # AddressList : list of variable register address in decimal (relative address) # MultiplierList : list of multiplier # roundto : number of digits after decimal point # any positive integer number >> to limit the number of digits after decimal point # None >> to disable # functioncode : functioncode for modbus reading operation # 3 >> for Holding Register # 4 >> for Input Register # Return : dictionary of variable name and its value self.values = [] if functioncode == 3: for address in AddressList: self.values.extend(self.dev.read_holding_registers(address[0],len(address))) elif functioncode == 4: for address in AddressList: self.values.extend(self.dev.read_input_registers(address[0],len(address))) self.values = UINT16toFLOAT16(self.values) for i in range(0, len(self.values)): self.values[i] = round(self.values[i]*MultiplierList[i], roundto) self.Result = dict(zip(VarNameList, self.values)) return self.Result #Method to read FLOAT32 variable def read_FLOAT32(self, VarNameList, AddressList, MultiplierList, roundto=3, functioncode=3): # Arguments: # VarNameList : list of variable name # AddressList : list of variable register address in decimal (relative address) # MultiplierList : list of multiplier # roundto : number of digits after decimal point # any positive integer number >> to limit the number of digits after decimal point # None >> to disable # functioncode : functioncode for modbus reading operation # 3 >> for Holding Register # 4 >> for Input Register # Return : dictionary of variable name and its value self.values = [] if functioncode == 3: for address in AddressList: self.values.extend(self.dev.read_holding_registers(address[0],len(address))) elif functioncode == 4: for address in AddressList: self.values.extend(self.dev.read_input_registers(address[0],len(address))) self.values = UINT16toFLOAT32(self.values, self.big_endian) for i in range(0, len(self.values)): self.values[i] = round(self.values[i]*MultiplierList[i], roundto) self.Result = dict(zip(VarNameList, self.values)) return self.Result #Method to read FLOAT64 variable def read_FLOAT64(self, VarNameList, AddressList, MultiplierList, roundto=3, functioncode=3): # Arguments: # VarNameList : list of variable name # AddressList : list of variable register address in decimal (relative address) # MultiplierList : list of multiplier # roundto : number of digits after decimal point # any positive integer number >> to limit the number of digits after decimal point # None >> to disable # functioncode : functioncode for modbus reading operation # 3 >> for Holding Register # 4 >> for Input Register # Return : dictionary of variable name and its value self.values = [] if functioncode == 3: for address in AddressList: self.values.extend(self.dev.read_holding_registers(address[0],len(address))) elif functioncode == 4: for address in AddressList: self.values.extend(self.dev.read_input_registers(address[0],len(address))) self.values = UINT16toFLOAT64(self.values, self.big_endian) for i in range(0, len(self.values)): self.values[i] = round(self.values[i]*MultiplierList[i], roundto) self.Result = dict(zip(VarNameList, self.values)) return self.Result #Method to read STRING variable def read_STRING(self, VarNameList, AddressList, functioncode=3): # Arguments: # VarNameList : list of variable name # AddressList : list of variable register address in decimal (relative address) # functioncode : functioncode for modbus reading operation # 3 >> for Holding Register # 4 >> for Input Register # Return : dictionary of variable name and its value self.values = [] if functioncode == 3: for address in AddressList: _uint16Val = self.dev.read_holding_registers(address[0],len(address)) self.values.append(UINT16toSTRING(_uint16Val, self.big_endian)) elif functioncode == 4: for address in AddressList: _uint16Val = self.dev.read_input_registers(address[0],len(address)) self.values.append(UINT16toSTRING(_uint16Val, self.big_endian)) self.Result = dict(zip(VarNameList, self.values)) return self.Result #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ WRITE METHODS ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~# # Method to write binary value on discrete output register (Coil) def write_bit(self, registerAddress, value): # Arguments: # registerAddress : register address in decimal (relative address) # value : 0 or 1 self.dev.write_single_coil(registerAddress, value) # Method to write numeric value on holding register def write_num(self, registerAddress, value, valueType): # Arguments: # registerAddress : register START address in decimal (relative address) # value : numerical value # valueType : UINT16, UINT32, UINT64, INT16, INT32, INT64, FLOAT16, # FLOAT32, FLOAT64, STRING startAddress = registerAddress val = None if valueType == UINT16: val = [value] elif valueType == INT16: val = INT16toUINT16([value]) elif valueType == UINT32: val = INT32toUINT16(value, self.big_endian, signed=False) elif valueType == INT32: val = INT32toUINT16(value, self.big_endian, signed=True) elif valueType == UINT64: val = INT64toUINT16(value, self.big_endian, signed=False) elif valueType == INT64: val = INT64toUINT16(value, self.big_endian, signed=True) elif valueType == FLOAT16: val = FLOAT16toUINT16([value]) elif valueType == FLOAT32: val = FLOAT32toUINT16(value, self.big_endian) elif valueType == FLOAT64: val = FLOAT64toUINT16(value, self.big_endian) elif valueType == STRING: val = STRINGtoUINT16(value, self.big_endian) # write multiple registers self.dev.write_multiple_registers(startAddress, val) def close(self): self.dev.close()
# define modbus server host, port c.host(SERVER_HOST) c.port(SERVER_PORT) while True: # open or reconnect TCP to server if not c.is_open(): if not c.open(): print("unable to connect to " + SERVER_HOST + ":" + str(SERVER_PORT)) # if open() is ok, read coils (modbus function 0x01) if c.is_open(): if is_ok == False: is_ok = c.write_single_coil(ADDRESS_WR, toggle) if is_ok: print("bit #" + str(ADDRESS_WR) + ": write to " + str(toggle)) else: print("bit #" + str(ADDRESS_WR) + ": unable to write " + str(toggle)) time.sleep(0.5) # read 10 bits at address 0, store result in regs list bits = c.read_coils(ADDRESS_START, LENGHT) # if success display registers if bits: print(is_ok) print("bit from M{} to M{} : ".format(
class ThermiaGenesis: # pylint:disable=too-many-instance-attributes """Main class to perform modbus requests to heat pump.""" def __init__(self, host, port=502, kind='inverter', delay=0.1, max_registers=16): """Initialize.""" self.data = {} self._client = ModbusClient(host, port=port, unit_id=1, auto_open=True) self.firmware = None if(kind == MODEL_MEGA): self.model = "Mega" else: self.model = "Diplomat Inverter" self._host = host self._port = port self._kind = kind self._delay = delay self.MAX_REGISTERS = max_registers _LOGGER.debug("Using host: %s:%d", host, port) async def async_set(self, register, value): # pylint:disable=too-many-branches """Write data to heat pump.""" ret_value = await self._set_data(register, value) self._client.close() async def async_update(self, register_types=REG_TYPES, only_registers = None): # pylint:disable=too-many-branches """Update data from heat pump.""" use_registers = [] if(only_registers != None): #Make sure to sort registers by type and address use_registers = sorted(only_registers, key=(lambda x: f"{REGISTERS[x][KEY_REG_TYPE]}-{REGISTERS[x][KEY_ADDRESS]:03}")) else: use_registers = dict(filter(lambda x: x[1][self._kind], REGISTERS.items())).keys() raw_data = await self._get_data(use_registers) self._client.close() if not raw_data: self.data = {} return {} #_LOGGER.debug("RAW data: %s", raw_data) data = {} try: for i, (name, val) in enumerate(raw_data.items()): data[name] = val self.firmware = f"{self.data[ATTR_INPUT_SOFTWARE_VERSION_MAJOR]}.{self.data[ATTR_INPUT_SOFTWARE_VERSION_MINOR]}.{self.data[ATTR_INPUT_SOFTWARE_VERSION_MICRO]}" _LOGGER.debug("------------- REGISTERS ----------------------") for i, (name, val) in enumerate(self.data.items()): _LOGGER.debug(f"{REGISTERS[name][KEY_ADDRESS]}\t{val}\t{name}") except AttributeError as err: _LOGGER.debug("Incomplete data from modbus.") _LOGGER.debug(err) except KeyError as err: _LOGGER.debug("Incomplete data from modbus.") _LOGGER.debug(err) except TypeError as err: _LOGGER.debug("Incomplete data from modbus.") _LOGGER.debug(err) self.data = data return data @property def available(self): """Return True is data is available.""" return bool(self.data) async def _set_data(self, register, value): meta = REGISTERS[register] regtype = meta[KEY_REG_TYPE] address = meta[KEY_ADDRESS] scale = meta[KEY_SCALE] await asyncio.sleep(self._delay) try: if(regtype == REG_COIL): _LOGGER.debug(f"Set {regtype} register at {address} value {value} ({value})") self._client.write_single_coil(address, value) elif(regtype == REG_HOLDING): converted_value = int(value * scale) if(meta[KEY_DATATYPE] == TYPE_INT): converted_value = num_to_bin(converted_value) _LOGGER.debug(f"Set {regtype} register at {address} value {converted_value} ({value}) {scale}") self._client.write_single_register(address, converted_value) else: raise "This register can not be changed" except Exception as e: _LOGGER.error(f'exception: {e}') print(traceback.format_exc()) return value async def _get_data(self, registers): """Retreive data from heat pump.""" raw_data = {} #Split into requests that reads up to self.MAX_REGISTERS within REGISTER_RANGES (register blocks) for the requested registers first_chunk_address = 0 current_type = None chunks = [] chunk = None for name in registers: meta = REGISTERS[name] if(name == ATTR_HOLDING_FIXED_SYSTEM_SUPPLY_SET_POINT): #This will give an errror unless coil 42 is True, so skip if we don't know this or if it's false enableAttr = ATTR_COIL_ENABLE_FIXED_SYSTEM_SUPPLY_SET_POINT if(enableAttr not in raw_data and enableAttr not in self.data): _LOGGER.debug(f"Will not read {name} since we don't know if {ATTR_COIL_ENABLE_FIXED_SYSTEM_SUPPLY_SET_POINT} is set, include this register in the request to read this") continue if(not raw_data[ATTR_COIL_ENABLE_FIXED_SYSTEM_SUPPLY_SET_POINT] and not self.data[ATTR_COIL_ENABLE_FIXED_SYSTEM_SUPPLY_SET_POINT]): _LOGGER.debug(f"Will not read {name} since {ATTR_COIL_ENABLE_FIXED_SYSTEM_SUPPLY_SET_POINT} is False which disables this register") continue reg_address = meta[KEY_ADDRESS] if(chunk == None #First iteration or chunk[KEY_REG_TYPE] != meta[KEY_REG_TYPE] #New register type or (reg_address - chunk['start']) >= self.MAX_REGISTERS #Exceeds max number of registers per request or reg_address > chunk['range_end']): #Address belongs to another register block if(chunk != None): chunks.append(chunk) start = meta[KEY_ADDRESS] chunk = { KEY_REG_TYPE: meta[KEY_REG_TYPE], 'start': start, 'slots': { name: 0 } } if(meta[KEY_DATATYPE] == TYPE_LONG): chunk['end'] = start + 1 else: chunk['end'] = start in_range = list(filter(lambda x: x[0] <= start and x[1] >= start, REGISTER_RANGES[self._kind][meta[KEY_REG_TYPE]])) chunk['range_end'] = in_range[0][1] else: chunk['slots'][name] = reg_address - start if(meta[KEY_DATATYPE] == TYPE_LONG): chunk['end'] = reg_address + 1 else: chunk['end'] = reg_address if(chunk != None): chunks.append(chunk) _LOGGER.info(f"Will make {len(chunks)} requests to read {len(registers)} registers") #print(f"Will make {len(chunks)} requests to read {len(registers)} registers") try: for chunk in chunks: await asyncio.sleep(self._delay) start_address = chunk['start'] length = chunk['end'] - chunk['start'] + 1 regtype = chunk[KEY_REG_TYPE] _LOGGER.debug(f"Reading {regtype} {start_address} length {length}") read_data = None if(regtype == REG_COIL): read_data = self._client.read_coils(start_address, length) elif(regtype == REG_DISCRETE_INPUT): read_data = self._client.read_discrete_inputs(start_address, length) elif(regtype == REG_INPUT): read_data = self._client.read_input_registers(start_address, length) elif(regtype == REG_HOLDING): read_data = self._client.read_holding_registers(start_address, length) if read_data: for i, (name, address) in enumerate(chunk['slots'].items()): info = REGISTERS[name] datatype = info[KEY_DATATYPE] scale = info[KEY_SCALE] val = read_data[address] if(datatype == TYPE_LONG): regs = read_data[address:(address+2)] val = word_list_to_long(regs)[0] elif(datatype == TYPE_INT): if(val == 32767): val = 0 if(val > 32767): val = val - 65536 elif(datatype == TYPE_STATUS): status_str = "OFF" if val == 1: status_str = "Manual Operation" elif val == 2: status_str = "Defrost" elif val == 3: status_str = "Hot water" elif val == 4: status_str = "Heat" elif val == 5: status_str = "Cool" elif val == 6: status_str = "Pool" elif val == 7: status_str = "Anti legionella" elif val == 98: status_str = "Standby" elif val == 99: status_str = "No demand" val = status_str if(scale != 1): val = val / scale raw_data[name] = val else: if self._client.last_error() > 0: _LOGGER.error(f'error {self._client.last_error()}') raise Exception(f"Failed to read {regtype} {start_address} length {length}", self._client.last_error()) #for regtype in register_types: # last_chunk_address = 0 # values = [] # for chunk in REGISTER_RANGES[self._kind][regtype]: # await asyncio.sleep(self._delay) # start_address = chunk[0] # length = chunk[1] - start_address # #Insert 0 if there is a gap # values.extend([0] * (start_address - last_chunk_address)) # _LOGGER.debug(f"Reading {regtype} {start_address} length {length}") # read_data = None # if(regtype == REG_COIL): # read_data = self._client.read_coils(start_address, length) # elif(regtype == REG_DISCRETE_INPUT): # read_data = self._client.read_discrete_inputs(start_address, length) # elif(regtype == REG_INPUT): # read_data = self._client.read_input_registers(start_address, length) # elif(regtype == REG_HOLDING): # read_data = self._client.read_holding_registers(start_address, length) # if read_data: # values.extend(read_data) # else: # if self._client.last_error() > 0: # print(f'error {self._client.last_error()}') # _LOGGER.error(f'error {self._client.last_error()}') # raise Exception(f"Failed to read {regtype} {start_address} length {length}", self._client.last_error()) # last_chunk_address = chunk[1] # raw_data[regtype] = values except Exception as e: _LOGGER.error(f'exception: {e}') print(traceback.format_exc()) return raw_data
import time from pyModbusTCP.client import ModbusClient # Equipamentos no chão de fábrica valvula1 = False valvula2 = False nivelTanque1 = 0.0 bracoRobotico = 0 # auto_open=True, auto_close=True c = ModbusClient() c.host("172.17.115.225") c.port(502) while True: if not c.is_open(): if not c.open(): print("conexão falhou") # on = c.read_coils(4) if c.is_open(): c.write_single_coil(0, valvula1)
class AmpSwitch(object): def __init__(self, host, port=502, switches=(), debug=False): """ """ self.host = host self.port = port self.debug = debug self.switches = switches self.dev = None self.connect() def __str__(self): return "AmpSwitch(host=%s, port=%s, dev=%s>" % (self.host, self.port, self.dev) def setDebug(self, state): self.debug = state self.connect() def close(self): if self.dev is not None: self.dev.close() self.dev = None def connect(self): """ (re-) establish a connection to the device. """ if self.dev is None: self.dev = ModbusClient() self.dev.debug(self.debug) self.dev.host(self.host) self.dev.port(self.port) if self.dev.is_open(): return True ret = self.dev.open() if not ret: raise RuntimeError("failed to connect to %s:%s" % (self.host, self.port)) return True def readCoils(self): """ Return the state of all our switches. """ self.connect() regs = self.dev.read_coils(0, 16) return regs def setCoils(self, on=(), off=()): """Turn on and off a given set of switches. Argunents --------- on, off : list-like, or a single integer. Notes: ------ The off set is executed first. . There is a command to change all switchees at once, but I have not made it work yet. """ self.connect() if isinstance(on, int): on = on, if isinstance(off, int): off = off, regs0 = self.readCoils() regs1 = regs0[:] for c in off: ret = self.dev.write_single_coil(c, False) regs1[c] = False for c in on: ret = self.dev.write_single_coil(c, True) regs1[c] = True # ret = self.dev.write_multiple_registers(0, regs1) ret = self.readCoils() return ret def chooseCoil(self, n): return self.setCoils(on=n, off=list(range(16)))
class LightController(): """Class to control lights of a single controller""" toggle_time = config.toggle_time def __init__(self, host): self.client = ModbusClient(host=host, auto_open=True, auto_close=False) #self.client.debug(True) logger.info("Connecting to %s", self.client.host()) status = self.check_status() if status & 1<<15: # Watchdog ellapsed, try to reset logger.info("Resetting watchdog") self.client.write_single_register(0x1121, 0xbecf) self.client.write_single_register(0x1121, 0xaffe) # Disable the watchdog, if enabled watchdog_timeout = self.client.read_holding_registers(0x1120)[0] if watchdog_timeout > 0: logger.debug("Watchdog timeout: %d", watchdog_timeout) logger.info("Disabling watchdog") self.client.write_single_register(0x1120, 0) (self.num_outputs, self.num_inputs) = self.client.read_holding_registers(0x1012, 2) logger.info("Number of outputs: %d", self.num_outputs) logger.info("Number of inputs: %d", self.num_inputs) self.client.close() # We use auto close for all subsequent calls after initialization self.client.auto_close(True) def check_status(self): """Read buscoupler status from the module""" buscoupler_status = self.client.read_holding_registers(0x100c)[0] if buscoupler_status & 1<<0: logger.error("Bus terminal error") if buscoupler_status & 1<<1: logger.error("Bus coupler configuration error") if buscoupler_status & 1<<15: logger.error("Fieldbus error, watchdog timer elapsed") return buscoupler_status def inputs(self): """Read input status from module""" inputs = self.client.read_discrete_inputs(0, self.num_inputs) logger.debug("Inputs: %s", "".join(map(lambda c: '1' if c else '0', inputs))) return inputs def outputs(self): """Read output status from module""" coils = self.client.read_coils(0, self.num_outputs) logger.debug("Outputs: %s", "".join(map(lambda c: '1' if c else '0', coils))) return coils def toggle(self, bit_addr): """Toggle a impulse relay connected to a single output""" logger.info("Toggling %d on %s", bit_addr, self.client.host()) self.client.auto_close(False) self.client.write_single_coil(bit_addr, True) logger.debug("On %d", bit_addr) time.sleep(self.toggle_time) self.client.write_single_coil(bit_addr, False) logger.debug("Off %d", bit_addr) self.client.close() self.client.auto_close(True)
class TestClientServer(unittest.TestCase): port = 5020 def setUp(self): # modbus server self.server = ModbusServer(port=TestClientServer.port, no_block=True) self.server.start() # modbus client self.client = ModbusClient(port=TestClientServer.port) self.client.open() # to prevent address taken errors TestClientServer.port += 1 def tearDown(self): self.client.close() @repeat def test_word_init(self): # word space self.assertEqual(self.client.read_holding_registers(0), [0], 'Default value is 0 when server start') self.assertEqual(self.client.read_input_registers(0), [0], 'Default value is 0 when server start') @repeat def test_word_single(self): # single read/write self.assertEqual(self.client.write_single_register(0, 0xffff), True) self.assertEqual(self.client.read_input_registers(0), [0xffff]) @repeat def test_word_multi(self): # multi-write at max size words_l = [randint(0, 0xffff)] * 0x7b self.assertEqual(self.client.write_multiple_registers(0, words_l), True) self.assertEqual(self.client.read_holding_registers(0, len(words_l)), words_l) self.assertEqual(self.client.read_input_registers(0, len(words_l)), words_l) @repeat def test_word_oversize(self): # write over sized words_l = [randint(0, 0xffff)] * 0x7c self.assertEqual(self.client.write_multiple_registers(0, words_l), None) @repeat def test_bit_init(self): # bit space self.assertEqual(self.client.read_coils(0), [False], 'Default value is False when server start') self.assertEqual(self.client.read_discrete_inputs(0), [False], 'Default value is False when server start') @repeat def test_bit_single(self): # single read/write self.assertEqual(self.client.write_single_coil(0, True), True) self.assertEqual(self.client.read_coils(0), [True]) self.assertEqual(self.client.read_discrete_inputs(0), [True]) @repeat def test_bit_multi_min(self): # multi-write at min size bits_l = [getrandbits(1)] * 0x1 self.assertEqual(self.client.write_multiple_coils(0, bits_l), True) self.assertEqual(self.client.read_coils(0, len(bits_l)), bits_l) self.assertEqual(self.client.read_discrete_inputs(0, len(bits_l)), bits_l) @repeat def test_bit_multi_max(self): # multi-write at max size bits_l = [getrandbits(1)] * 0x7b0 self.assertEqual(self.client.write_multiple_coils(0, bits_l), True) self.assertEqual(self.client.read_coils(0, len(bits_l)), bits_l) self.assertEqual(self.client.read_discrete_inputs(0, len(bits_l)), bits_l) @repeat def test_bit_multi_oversize(self): # multi-write over sized bits_l = [getrandbits(1)] * 0x7b1 self.assertEqual(self.client.write_multiple_coils(0, bits_l), None)
while True: # open or reconnect TCP to server if not c.is_open(): if not c.open(): print("unable to connect to " + SERVER_HOST + ":" + str(SERVER_PORT)) # if open() is ok, write coils (modbus function 0x01) if c.is_open(): # write 4 bits in modbus address 0 to 3 print("") print("write bits") print("----------") print("") for addr in range(4): is_ok = c.write_single_coil(addr, toggle) if is_ok: print("bit #" + str(addr) + ": write to " + str(toggle)) else: print("bit #" + str(addr) + ": unable to write " + str(toggle)) time.sleep(0.5) time.sleep(1) print("") print("read bits") print("---------") print("") bits = c.read_coils(0, 4) if bits: print("bits #0 to 3: " + str(bits))
class ET7000: ranges = { 0x00: { 'min': -0.015, 'max': 0.015, 'min_code': 0x8000, 'max_code': 0x7fff, 'units': 'V' }, 0x01: { 'min': -0.05, 'max': 0.05, 'min_code': 0x8000, 'max_code': 0x7fff, 'units': 'V' }, 0x02: { 'min': -0.1, 'max': 0.1, 'min_code': 0x8000, 'max_code': 0x7fff, 'units': 'V' }, 0x03: { 'min': -0.5, 'max': 0.5, 'min_code': 0x8000, 'max_code': 0x7fff, 'units': 'V' }, 0x04: { 'min': -1., 'max': 1., 'min_code': 0x8000, 'max_code': 0x7fff, 'units': 'V' }, 0x05: { 'min': -2.5, 'max': 2.5, 'min_code': 0x8000, 'max_code': 0x7fff, 'units': 'V' }, 0x06: { 'min': -20.0e-3, 'max': 20.0e-3, 'min_code': 0x8000, 'max_code': 0x7fff, 'units': 'A' }, 0x07: { 'units': 'A', 'min': 4.0e-3, 'min_code': 0x0000, 'max_code': 0xffff, 'max': 20.0e-3 }, 0x08: { 'units': 'V', 'min': -10., 'max': 10., 'min_code': 0x8000, 'max_code': 0x7fff, }, 0x09: { 'units': 'V', 'min_code': 0x8000, 'max_code': 0x7fff, 'min': -5., 'max': 5. }, 0x0A: { 'units': 'V', 'min_code': 0x8000, 'max_code': 0x7fff, 'min': -1., 'max': 1. }, 0x0B: { 'units': 'V', 'min_code': 0x8000, 'max_code': 0x7fff, 'min': -.5, 'max': .5 }, 0x0C: { 'units': 'V', 'min_code': 0x8000, 'max_code': 0x7fff, 'min': -.15, 'max': .15 }, 0x0D: { 'min': -20.0e-3, 'max': 20.0e-3, 'min_code': 0x8000, 'max_code': 0x7fff, 'units': 'A' }, 0x0E: { 'units': 'degC', 'min_code': 0xdca2, 'max_code': 0x7fff, 'min': -210.0, 'max': 760.0 }, 0x0F: { 'units': 'degC', 'min_code': 0xe6d0, 'max_code': 0x7fff, 'min': -270.0, 'max': 1372.0 }, 0x10: { 'units': 'degC', 'min_code': 0xa99a, 'max_code': 0x7fff, 'min': -270.0, 'max': 400.0 }, 0x11: { 'units': 'degC', 'min_code': 0xdd71, 'max_code': 0x7fff, 'min': -270.0, 'max': 1000.0 }, 0x12: { 'units': 'degC', 'min_code': 0x0000, 'max_code': 0x7fff, 'min': 0.0, 'max': 1768.0 }, 0x13: { 'units': 'degC', 'min_code': 0x0000, 'max_code': 0x7fff, 'min': 0.0, 'max': 1768.0 }, 0x14: { 'units': 'degC', 'min_code': 0x0000, 'max_code': 0x7fff, 'min': 0.0, 'max': 1820.0 }, 0x15: { 'units': 'degC', 'min_code': 0xe56b, 'max_code': 0x7fff, 'min': -270.0, 'max': 1300.0 }, 0x16: { 'units': 'degC', 'min_code': 0x0000, 'max_code': 0x7fff, 'min': 0.0, 'max': 2320.0 }, 0x17: { 'units': 'degC', 'min_code': 0xe000, 'max_code': 0x7fff, 'min': -200.0, 'max': 800.0 }, 0x18: { 'units': 'degC', 'min_code': 0x8000, 'max_code': 0x4000, 'min': -200.0, 'max': 100.0 }, 0x19: { 'units': 'degC', 'min_code': 0xe38e, 'max_code': 0xffff, 'min': -200.0, 'max': 900.0 }, 0x1A: { 'min': 0.0, 'max': 20.0e-3, 'min_code': 0x0000, 'max_code': 0xffff, 'units': 'A' }, 0x20: { 'units': 'degC', 'min_code': 0x8000, 'max_code': 0x7fff, 'min': -100.0, 'max': 100.0 }, 0x21: { 'units': 'degC', 'min_code': 0x0000, 'max_code': 0x7fff, 'min': 0.0, 'max': 100.0 }, 0x22: { 'units': 'degC', 'min_code': 0x0000, 'max_code': 0x7fff, 'min': 0.0, 'max': 200.0 }, 0x23: { 'units': 'degC', 'min_code': 0x0000, 'max_code': 0x7fff, 'min': 0.0, 'max': 600.0 }, 0x24: { 'units': 'degC', 'min_code': 0x8000, 'max_code': 0x7fff, 'min': -100.0, 'max': 100.0 }, 0x25: { 'units': 'degC', 'min_code': 0x0000, 'max_code': 0x7fff, 'min': 0.0, 'max': 100.0 }, 0x26: { 'units': 'degC', 'min_code': 0x0000, 'max_code': 0x7fff, 'min': 0.0, 'max': 200.0 }, 0x27: { 'units': 'degC', 'min_code': 0x0000, 'max_code': 0x7fff, 'min': 0.0, 'max': 600.0 }, 0x28: { 'units': 'degC', 'min_code': 0x999a, 'max_code': 0x7fff, 'min': -80.0, 'max': 100.0 }, 0x29: { 'units': 'degC', 'min_code': 0x0000, 'max_code': 0x7fff, 'min': 0.0, 'max': 100.0 }, 0x2A: { 'units': 'degC', 'min_code': 0xd556, 'max_code': 0x7fff, 'min': -200.0, 'max': 600.0 }, 0x2B: { 'units': 'degC', 'min_code': 0xeeef, 'max_code': 0x7fff, 'min': -20.0, 'max': 150.0 }, 0x2C: { 'units': 'degC', 'min_code': 0x0000, 'max_code': 0x7fff, 'min': 0.0, 'max': 200.0 }, 0x2D: { 'units': 'degC', 'min_code': 0xeeef, 'max_code': 0x7fff, 'min': -20.0, 'max': 150.0 }, 0x2E: { 'units': 'degC', 'min_code': 0x8000, 'max_code': 0x7fff, 'min': -200.0, 'max': 200.0 }, 0x2F: { 'units': 'degC', 'min_code': 0x8000, 'max_code': 0x7fff, 'min': -200.0, 'max': 200.0 }, 0x80: { 'units': 'degC', 'min_code': 0xd556, 'max_code': 0x7fff, 'min': -200.0, 'max': 600.0 }, 0x81: { 'units': 'degC', 'min_code': 0xd556, 'max_code': 0x7fff, 'min': -200.0, 'max': 600.0 }, 0x82: { 'units': 'degC', 'min_code': 0xd556, 'max_code': 0x7fff, 'min': -50.0, 'max': 150.0 }, 0x83: { 'min_code': 0xd556, 'max_code': 0x7fff, 'units': 'degC', 'min': -60.0, 'max': 180.0 }, 0x30: { 'min_code': 0x0000, 'max_code': 0xffff, 'min': 0.0, 'max': 20.0e-3, 'units': 'A' }, 0x31: { 'min_code': 0x0000, 'max_code': 0xffff, 'min': 4.0e-3, 'max': 20.0e-3, 'units': 'A' }, 0x32: { 'min_code': 0x0000, 'max_code': 0x7fff, 'min': 0.0, 'max': 10.0, 'units': 'V' }, 0x33: { 'min': -10.0, 'max': 10.0, 'min_code': 0x8000, 'max_code': 0x7fff, 'units': 'V' }, 0x34: { 'min': 0.0, 'max': 5.0, 'min_code': 0x0000, 'max_code': 0x7fff, 'units': 'V' }, 0x35: { 'min': -5.0, 'max': 5.0, 'min_code': 0x8000, 'max_code': 0x7fff, 'units': 'V' }, 0xff: { 'min': 0, 'max': 0xffff, 'min_code': 0x0000, 'max_code': 0xffff, 'units': '?' } } devices = {0x7015: {}, 0x7016: {}, 0x7018: {}, 0x7060: {}, 0x7026: {}} @staticmethod def range(r): if r in ET7000.ranges: return (ET7000.ranges[r]) return ET7000.ranges[0xff] # default conversion from quanta to real units @staticmethod def convert(b, amin, amax): b = float(b) # обрабатывается 2 случая - минимум нулевой или больше 0 if amin >= 0 and amax > 0: return amin + (amax - amin) * b / 0xffff # и минимум и максимум разного знака if amin < 0 and amax > 0: range = max(-amin, amax) if b <= 0x7fff: return range * b / 0x7fff else: return range * (0x8000 - b) / 0x8000 # в других случаях ошибка return float('nan') @staticmethod def ai_convert_function(r): v_min = 0 v_max = 0xffff c_min = 0 c_max = 0xffff try: v_min = ET7000.ranges[r]['min'] v_max = ET7000.ranges[r]['max'] c_min = ET7000.ranges[r]['min_code'] c_max = ET7000.ranges[r]['max_code'] except: pass if c_min < c_max: k = (v_max - v_min) / (c_max - c_min) b = v_min - k * c_min return lambda x: k * x + b k_max = v_max / c_max k_min = v_min / (0x10000 - c_min) #return lambda x: (x < 0x8000) * k_max * x + (x >= 0x8000) * k_min * (0x10000 - x) return lambda x: k_max * x if x < 0x8000 else k_min * (0x10000 - x) @staticmethod def ao_convert_function(r): v_min = 0 v_max = 0xffff c_min = 0 c_max = 0xffff try: v_min = ET7000.ranges[r]['min'] v_max = ET7000.ranges[r]['max'] c_min = ET7000.ranges[r]['min_code'] c_max = ET7000.ranges[r]['max_code'] except: pass #print(hex(r), v_min, v_max, c_min, c_max) if c_min < c_max: k = (c_max - c_min) / (v_max - v_min) b = c_min - k * v_min return lambda x: int(k * x + b) k_max = c_max / v_max k_min = (0xffff - c_min) / v_min #return lambda x: int((x >= 0) * k_max * x + (x < 0) * (0xffff - k_min * x)) return lambda x: int(k_max * x) if (x >= 0) else int(0xffff - k_min * x ) @staticmethod def convert_to_raw(v, amin, amax): v = float(v) # обрабатывается 2 случая - минимум нулевой или больше 0 if amin >= 0 and amax > 0: return int((v - amin) / (amax - amin) * 0xffff) # и минимум и максимум разного знака if amin < 0 and amax > 0: if v >= 0.0: return int(v * 0x7fff / amax) else: return int(0x8000 - v / amax * 0x7fff) # в других случаях ошибка return 0 def __init__(self, host, port=502, timeout=0.15, logger=None): self.host = host self.port = port # logger confid if logger is None: logger = logging.getLogger(__name__) self.logger = logger # default device type self._name = 0 self.type = '0000' # default ai self.AI_n = 0 self.AI_masks = [] self.AI_ranges = [] self.AI_min = [] self.AI_max = [] self.AI_units = [] self.AI_raw = [] self.AI_values = [] # default ao self.AO_n = 0 self.AO_masks = [] self.AO_ranges = [] self.AO_min = [] self.AO_max = [] self.AO_units = [] self.AO_raw = [] self.AO_values = [] self.AO_write_raw = [] self.AO_write_values = [] self.AO_write_result = False # default di self.DI_n = 0 self.DI_values = [] # default do self.DO_n = 0 self.DO_values = [] # modbus client self._client = ModbusClient(host, port, auto_open=True, auto_close=True, timeout=timeout) status = self._client.open() if not status: self.logger.error('ET7000 device at %s is offline' % host) return # read module name self._name = self.read_module_name() self.type = hex(self._name).replace('0x', '') if self._name not in ET7000.devices: self.logger.warning( 'ET7000 device type %s probably not supported' % hex(self._name)) # ai self.AI_n = self.read_AI_n() self.AI_masks = [False] * self.AI_n self.AI_ranges = [0xff] * self.AI_n self.AI_raw = [0] * self.AI_n self.AI_values = [float('nan')] * self.AI_n self.AI_units = [''] * self.AI_n self.read_AI_masks() self.read_AI_ranges() self.AI_convert = [lambda x: x] * self.AI_n for n in range(self.AI_n): r = self.AI_ranges[n] self.AI_units[n] = ET7000.ranges[r]['units'] self.AI_convert[n] = ET7000.ai_convert_function(r) # ao self.AO_n = self.read_AO_n() self.AO_masks = [True] * self.AO_n self.read_AO_masks() self.AO_ranges = [0xff] * self.AO_n self.AO_raw = [0] * self.AO_n self.AO_values = [float('nan')] * self.AO_n self.AO_write_values = [float('nan')] * self.AO_n self.AO_units = [''] * self.AO_n self.AO_write = [0] * self.AO_n self.AO_write_raw = [0] * self.AO_n self.read_AO_ranges() self.AO_convert = [lambda x: x] * self.AI_n self.AO_convert_write = [lambda x: 0] * self.AI_n for n in range(self.AO_n): r = self.AO_ranges[n] self.AO_units[n] = ET7000.ranges[r]['units'] self.AO_convert[n] = ET7000.ai_convert_function( r) # !!! ai_convert for reading self.AO_convert_write[n] = ET7000.ao_convert_function( r) # !!! ao_convert for writing # di self.DI_n = self.read_DI_n() self.DI_values = [False] * self.DI_n # do self.DO_n = self.read_DO_n() self.DO_values = [False] * self.DO_n self.DO_write = [False] * self.DO_n def read_module_name(self): regs = self._client.read_holding_registers(559, 1) if regs and regs[0] != 0: return regs[0] regs = self._client.read_holding_registers(260, 1) if regs: return regs[0] return 0 # AI functions def read_AI_n(self): regs = self._client.read_input_registers(320, 1) if regs and regs[0] != 0: return regs[0] regs = self._client.read_input_registers(120, 1) if regs: return regs[0] return 0 def read_AI_masks(self): coils = self._client.read_coils(595, self.AI_n) if coils and len(coils) == self.AI_n: self.AI_masks = coils return coils def read_AI_ranges(self): regs = self._client.read_holding_registers(427, self.AI_n) if regs and len(regs) == self.AI_n: self.AI_ranges = regs return regs def read_AI_raw(self, channel=None): if channel is None: n = self.AI_n channel = 0 else: n = 1 regs = self._client.read_input_registers(0 + channel, n) if regs and len(regs) == n: self.AI_raw[channel:channel + n] = regs if n == 1: return regs[0] return regs def convert_AI(self): for k in range(self.AI_n): if self.AI_masks[k]: self.AI_values[k] = self.AI_convert[k](self.AI_raw[k]) else: self.AI_values[k] = float('nan') return self.AI_values def read_AI(self, channel=None): if channel is None: self.read_AI_raw() self.convert_AI() return self.AI_values return self.read_AI_channel(channel) def read_AI_channel(self, k: int): v = float('nan') if self.AI_masks[k]: regs = self._client.read_input_registers(0 + k, 1) if regs: self.AI_raw[k] = regs[0] v = self.AI_convert[k](regs[0]) self.AI_values[k] = v return v # AO functions def read_AO_n(self): regs = self._client.read_input_registers(330, 1) if regs and regs[0] != 0: return regs[0] regs = self._client.read_input_registers(130, 1) if regs: return regs[0] return 0 def read_AO_masks(self): return self.AO_masks def read_AO_ranges(self): regs = self._client.read_holding_registers(459, self.AO_n) if regs and len(regs) == self.AO_n: self.AO_ranges = regs return regs def read_AO_raw(self, channel=None): if channel is None: n = self.AO_n channel = 0 else: n = 1 regs = self._client.read_holding_registers(0 + channel, n) if regs and len(regs) == n: self.AO_raw[channel:channel + n] = regs if n == 1: return regs[0] return regs def write_AO_raw(self, regs): result = self._client.write_multiple_registers(0, regs) self.AO_write_result = result if len(regs) == self.AO_n: self.AO_write_raw = regs return result def convert_AO(self): raw = self.AO_raw for k in range(self.AO_n): self.AO_values[k] = self.AO_convert[k](raw[k]) return self.AO_values def convert_to_raw_AO(self, values=None): if values is None: values = self.AO_write_values answer = [] for k in range(len(values)): answer.append(self.AO_convert_write[k](values[k])) return answer def read_AO(self): self.AO_raw = self.read_AO_raw() self.convert_AO() return self.AO_values def read_AO_channel(self, k: int): v = float('nan') if self.AO_masks[k]: regs = self._client.read_holding_registers(0 + k, 1) if regs: v = self.AO_convert[k](regs[0]) self.AO_values[k] = v return v def write_AO(self, values): self.AO_write_values = values regs = ET7000.convert_to_raw_AO(values) result = self.write_AO_raw(regs) return result def write_AO_channel(self, k: int, value): raw = self.AO_convert_write[k](value) result = self._client.write_single_register(0 + k, raw) self.AO_write_result = result if result: self.AO_write_values[k] = value self.AO_write_raw[k] = raw pass return result # DI functions def read_DI_n(self): regs = self._client.read_input_registers(300, 1) if regs and regs[0] != 0: return regs[0] regs = self._client.read_input_registers(100, 1) if regs: return regs[0] return 0 def read_DI(self): regs = self._client.read_discrete_inputs(0, self.DI_n) if regs: self.DI_values = regs return self.DI_values def read_DI_channel(self, k: int): reg = self._client.read_discrete_inputs(0 + k, 1) if reg: self.DI_values[k] = reg[0] return reg[0] return None # DO functions def read_DO_n(self): self.DO_time = time.time() regs = self._client.read_input_registers(310, 1) if regs and regs[0] != 0: return regs[0] regs = self._client.read_input_registers(110, 1) if regs: return regs[0] return 0 def read_DO(self): regs = self._client.read_coils(0, self.DI_n) if regs: self.DI_values = regs return self.DI_values def read_DO_channel(self, k: int): reg = self._client.read_coils(0 + k, 1) if reg: self.DO_values[k] = reg[0] return reg[0] return None def write_DO(self, values): self.DO_write = values self.DO_write_result = self._client.write_multiple_coils(0, values) return self.DO_write_result def write_DO_channel(self, k: int, value: bool): result = self._client.write_single_coil(0 + k, value) self.DO_write_result = result if result: self.DO_write[k] = value return result