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 read_all_tags(self): try: c = ModbusClient(host="192.168.2.11", port=502, debug=False) c.open() for name, tag in self.tag_db.items(): mb0 = tag['modbus_start'] -1 mb1 = tag['modbus_stop'] -1 size = 1+mb1-mb0 #print(name, mb0, mb1, size) #print(tag) if 0 <= mb0 < 100000: val = c.read_coils(mb0)[0] elif 100000 <= mb0 < 200000: val = c.read_discrete_inputs(mb0-100000)[0] elif 300000 <= mb0 < 400000: val = c.read_input_registers(mb0-300000, size) if size == 1: val = val[0] elif size == 2: val = utils.word_list_to_long(val, big_endian=False)[0] elif 400000 <= mb0 < 500000: val = c.read_holding_registers(mb0-400000, size ) if size == 1: val = val[0] elif size == 2: val = utils.word_list_to_long(val, big_endian=False)[0] if tag['dtype'] == 'float32': val = utils.decode_ieee(val) #print(name, val) self.settings[name] = val except Exception as err: print("Error in read_all_tags", err) c.close()
def read_valid_registers(target_ip, port, reg): result = False print(f'\n=====Polling remote server for {reg}:=====') for i in range(start_reg, end_reg): client = ModbusClient(host=target_ip, port=port, auto_open=True, auto_close=True, timeout=10) if reg == "hold": data = client.read_holding_registers(i, 1) if reg == "input": data = client.read_input_registers(i, 1) if reg == "discrete": data = client.read_discrete_inputs(i, 1) if reg == "coil": data = client.read_coils(i, 1) if data: result = True print(f'\nValid Registers {reg} detected at {i} with value {data}') client.close() print('/', end='') sleep(0.1) # return valid_list,data_list,permission_list if result != True: print(f'\n No Valid Registers detected in specified range')
def read(ip): # Initialize modbus c = ModbusClient(host=ip, port=502, auto_open=True, timeout=0.1) # Read the outputs (Returns None if timeout) return(c.read_coils(16, 6))
class Pebl: def __init__(self, ip, system, port=502): self.c = ModbusClient(host=ip, port=502, auto_open=True) self.c.host(ip) self.c.port(port) self.c.open() self.system = system print("opened") def run(self): return_values = [] data_points = csv_json_alarms(self.system) for data_point in data_points: print(data_point) if data_point['type'] == 'COIL': value = self.c.read_coils(int(data_point['address'])) print(value) name = data_point['alarm'] return_values.append({'name': name, 'value': value}) f = open('data.json', 'w') f.write(json.dumps(return_values)) f.close() def run_manual(self): return_values = [] data_points = csv_json_alarms(self.system) for data_point in data_points: print(data_point) if data_point['type'] == 'COIL': value = self.c.read_coils(int(data_point['address'])) name = data_point['alarm'] if data_point['trigger'] == 'NC': if not value[0]: return_values.append({'name': name, 'value': 'OK'}) else: return_values.append({'name': name, 'value': 'ALARM'}) elif data_point['trigger'] == 'NO': if value[0]: return_values.append({'name': name, 'value': 'ALARM'}) else: return_values.append({'name': name, 'value': 'OK'}) return return_values
def connect_nexus_machine(self): c = ModbusClient() c.host("192.168.0.147") c.port(502) c.open() input_register = c.read_coils(0, 16) while True: if input_register: self.lineEditIP.setText(str(input_register)) else: print("read error") time.sleep(5)
def read_valid_registers(ip_addr,port,reg): valid_list=[] data_list=[] permission_list=[] client=ModbusClient(host=ip_addr,port=port,auto_open=True,auto_close=True,timeout=10) for i in tqdm(range(1,500)): if reg == "hold": data=client.read_holding_registers(i,1) if reg == "input": data=client.read_input_registers(i,1) if reg == "discrete": data=client.read_discrete_inputs(i,1) if reg == "coil": data=client.read_coils(i,1) if data: valid_list.append(i) data_list.append(data[0]) permission_list.append("Read") client.close() return valid_list,data_list,permission_list
def monitor_register(target_ip, port, address, delay, reg): while True: client = ModbusClient(host=target_ip, port=port, auto_open=True, auto_close=True, timeout=1) if reg == "Holding": data = client.read_holding_registers(address, 1) if reg == "Input": data = client.read_input_registers(address, 1) if reg == "Discrete": data = client.read_discrete_inputs(address, 1) if reg == "Coil": data = client.read_coils(address, 1) if data: print(f'{reg} register at address {address} has value {data}') #print ('.', end='') client.close() sleep(delay)
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()
def get(self, uid): try: # Get alert details from database by ip try: alert_document = self.DB['devices_aggregated'].find_one( {'device._id': ObjectId(uid)}) if alert_document == None: return ({'found': False}) alert_document['found'] = True except: return ({'found': False}) ip = alert_document['device']['ip'] c = ModbusClient(host=ip, port=502, auto_open=True, timeout=1) try: bits = c.read_coils(16, 6) alert_document["online"] = True alert_document["all_clear"] = bits[0] alert_document["emergency"] = bits[1] alert_document["lightning"] = bits[2] alert_document["a"] = bits[3] alert_document["b"] = bits[4] alert_document["c"] = bits[5] except: alert_document["online"] = False alert_document["all_clear"] = False alert_document["emergency"] = False alert_document["lightning"] = False alert_document["a"] = False alert_document["b"] = False alert_document["c"] = False return (jsonify(json.loads(dumps(alert_document)))) except: return (Response('{"success": false}', status=500, mimetype='application/json'))
def pollModbus( ip: str ) -> list: ''' Get output states via modbus over IP ''' # Setup modbus connection try: c = ModbusClient(host=ip, port=502, auto_open=True, timeout=1) except: c = False if c: # Read Modbus outputs bits = c.read_coils(16, 6) else: bits = False # If there is a response if bits: # Set output states return([bits[0], bits[1], bits[2], bits[3], bits[4], bits[5]]) else: return([])
# TCP auto connect on first modbus request #c = ModbusClient(host="localhost", port=502, auto_open=True) # TCP auto connect on modbus request, close after it #c = ModbusClient(host="127.0.0.1", auto_open=True, auto_close=True) c = ModbusClient() c.host("192.168.58.10") c.port(502) # managing TCP sessions with call to c.open()/c.close() c.open() # to debug the communication #c.debug(True) inputRegD = c.read_discrete_inputs(0, 16) coil = c.read_coils(0, 16) read_holding = c.read_holding_registers(0, 2) inputReg = c.read_input_registers(0, 2) while True: if inputRegD: #print(regs) #print(type(regs)) #print(type(coil)) x = inputRegD[5] print("Read Input: " + str(inputRegD)) print("Read Coils: " + str(coil)) print("Holding Register: " + str(read_holding)) print("Read Input Register: " + str(inputReg)) else:
import time SERVER_HOST = "192.168.181.76" SERVER_PORT = 502 c = ModbusClient() # uncomment this line to see debug message #c.debug(True) # 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(): # read 10 bits at address 0, store result in regs list bits = c.read_coils(43, 14) # if success display registers if bits: print("bit ad #43 to 56: "+str(bits)) # sleep 2s before next polling time.sleep(2)
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)))
def get_points(conn, devices): for device in devices: try: cur = conn.cursor() #Get IP Address and port from database cur.execute("SELECT ip, port FROM device WHERE devID = ?", (device, )) request = cur.fetchone() ip = request[0] port = request[1] client = ModbusClient() client.host(ip) client.port(port) client_connected = True if (client.is_open() == False): if (client.open() == False): print("Unable to connect to " + ip + ":" + str(port)) client_connected = False if (client_connected == True): cur.execute( "SELECT name, address, type, pointID, mult_factor FROM device_points WHERE devID = ? AND deleted = 0", (device, )) rows = cur.fetchall() for row in rows: if (row[2] == "dig_in"): req = client.read_discrete_inputs(int(row[1]), 1) datetime_str = datetime.now().strftime( "%Y-%m-%d %H:%M:%S.%f") point_ID = row[3] value = req[0] * row[4] print(datetime_str + " | " + str(point_ID) + " | " + str(value)) cur.execute("INSERT INTO point_data VALUES (?, ?, ?)", (datetime_str, point_ID, value)) #print("Value for point " + row[0] + ": " + str(req) + datetime.now().strftime("%Y-%m-%d %H:%M:%S.%f")) if (row[2] == "dig_out"): req = client.read_coils(int(row[1]), 1) datetime_str = datetime.now().strftime( "%Y-%m-%d %H:%M:%S.%f") point_ID = row[3] value = req[0] * row[4] error_code = 0 print(datetime_str + " | " + str(point_ID) + " | " + str(value)) cur.execute("INSERT INTO point_data VALUES (?, ?, ?)", (datetime_str, point_ID, value)) #print("Value for point " + row[0] + ": " + str(req) + datetime.now().strftime("%Y-%m-%d %H:%M:%S.%f")) if (row[2] == "an_in"): req = client.read_input_registers(int(row[1]), 1) datetime_str = datetime.now().strftime( "%Y-%m-%d %H:%M:%S.%f") point_ID = row[3] value = req[0] * row[4] error_code = 0 print(datetime_str + " | " + str(point_ID) + " | " + str(value)) cur.execute("INSERT INTO point_data VALUES (?, ?, ?)", (datetime_str, point_ID, value)) #print("Value for point " + row[0] + ": " + str(req) + datetime.now().strftime("%Y-%m-%d %H:%M:%S.%f")) if (row[2] == "an_out"): req = client.read_holding_registers(int(row[1]), 1) datetime_str = datetime.now().strftime( "%Y-%m-%d %H:%M:%S.%f") point_ID = row[3] value = req[0] * row[4] error_code = 0 print(datetime_str + " | " + str(point_ID) + " | " + str(value)) cur.execute("INSERT INTO point_data VALUES (?, ?, ?)", (datetime_str, point_ID, value)) #print("Value for point " + row[0] + ": " + str(req) + datetime.now().strftime("%Y-%m-%d %H:%M:%S.%f")) print("\n") conn.commit() cur.close() client.close() except Error as e: print(e) conn.close() database = "./database/SCADADB.db" conn = create_connection(database)
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
from pyModbusTCP.client import ModbusClient import time try: c = ModbusClient(host="localhost", port=502) except ValueError: print("Error with host or port parameters") while (1): if not c.is_open(): if not c.open(): print("unable to connect to ") if c.is_open(): hreg = c.read_holding_registers(0, 10) creg = c.read_coils(0, 5) if hreg: print("Holding registers 0 to 9:" + str(hreg)) if creg: print("Coil registers 0 to 4:" + str(creg)) time.sleep(1) # uncomment this line to see debug message # c.debug(True) # while True: # # open or reconnect TCP to server # if not c.is_open(): # if not c.open():
SERVER_HOST = "localhost" SERVER_PORT = 502 c = ModbusClient() # uncomment this line to see debug message #c.debug(True) # 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(): # read 10 bits at address 0, store result in regs list bits = c.read_coils(0, 10) # if success display registers if bits: print("bit ad #0 to 9: " + str(bits)) # sleep 2s before next polling time.sleep(2)
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( ADDRESS_START - 2000, ADDRESS_START + LENGHT - 2001) + str(bits)) if bits[0] == False: is_ok = False bit_no = ADDRESS_START for i in bits: print('Bit M{} - {}'.format(bit_no - 2000, i)) bit_no += 1
# 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)) else: print("unable to read") toggle = not toggle # sleep 2s before next polling time.sleep(2)
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)
# 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(8192, 8198): 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(8192, 6) if bits: print("bits #8193 to 8196: " + str(bits)) else: print("unable to read") toggle = not toggle # sleep 2s before next polling time.sleep(2)
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')
host_client = "192.168.1.220" c = ModbusClient() if not c.host(host_client): print("host error") if not c.port(502): print("port error") # Автоматическое соединение TCP по запросу Modbus, закрытие после него\TCP auto connect on modbus request, close after it c = ModbusClient(host=host_client, auto_open=True, auto_close=True) while True: if c.is_open(): # считываем регистры установки\read setup registers in_regs_read = c.read_input_registers(0, 51) hl_regs_read = c.read_holding_registers(0, 3) coil_regs_read = c.read_coils(0, 25) dis_in_regs = c.read_discrete_inputs(0, 72) # присвоить переменным значение\assign variables a value CL_POWER, CL_TIMER, CL_WEEK, CL_Boost_MODE, CL_FPLC_MODE, CL_IntRH_CTRL, CL_ExtRH_CTRL, CL_IntCO2_CTRL, CL_ExtCO2_CTRL, CL_IntPM2_5_CTRL, CL_ExtPM2_5_CTRL, CL_IntVOC_CTRL, CL_ExtVOC_CTRL, CL_BoostSWITCH_CTRL, CL_FplcSWITCH_CTRL, CL_FireALARM_CTRL, CL_10V_SENSOR_CTRL, CL_RESET_FILTER_TIMER, CL_RESET_ALARM, CL_RESTORE_FACTORY, CL_CLOUD_CTRL, CL_MinSuAirOutTEMP_CTRL, CL_WaterPRESS_CTRL, CL_WaterFLOW_CTRL, CL_WaterHeaterAutoRestart = coil_regs_read IR_CurSelTEMP, IR_CurTEMP_SuAirIn, IR_CurTEMP_SuAirOut, IR_CurTEMP_ExAirIn, IR_CurTEMP_ExAirOut, unknown6in, unknown7in, IR_CurTEMP_Ext, IR_CurTEMP_Water, IR_CurVBAT, IR_CurRH_Int, IR_CurRH_Ext, IR_CurCO2_Int, IR_CurCO2_Ext, IR_CurPM2_5_Int, IR_CurPM2_5_Ext, IR_CurVOC_Int, IR_CurVOC_Ext, IR_Cur10V_SENSOR, IR_CurSuAirFLOW, IR_CurExAirFLOW, IR_CurSuPRESS, IR_CurExPRESS, IR_SuRPM, IR_ExRPM, unknown26in, IR_CurTIMER_TIME, unknown28in, IR_CurFILTER_TIMER, unknown30in, IR_TotalWorkingTime, IR_StateFILTER, IR_CurWeekSpeed, IR_CurWeekSetTemp, unknown35in, unknown36in, IR_VerMAIN_FMW, IR_DeviceTYPE, IR_ALARM, IR_RH_U, IR_CO2_U, IR_PM2_5_U, IR_VOC_U, IR_PreHeater_U, IR_MainHeater_U, IR_BPS_ROTOR_U, IR_KKB_U, IR_ReturnWater_U, IR_SuAirOutSetTemp, IR_WaterStandbySetTemp, IR_WaterStartSetTemp = in_regs_read DI_CurBoostSWITCH, DI_CurFplcSWITCH, DI_CurFireALARM, DI_StatusRH, DI_StatusCO2, DI_StatusPM2_5, DI_StatusVOC, DI_StatusHEATER, DI_StatusCOOLER, DI_StatusFanBLOWING, DI_CurPreHeaterThermostat, DI_CurMainHeaterThermostat, DI_CurSuFilterPRESS, DI_CurExFilterPRESS, DI_CurWaterPRESS, DI_CurWaterFLOW, DI_CurSuFanPRESS, DI_CurExFanPRESS, DI_WaterPreheatingStatus, DI_AlarmCODE0, DI_AlarmCODE1, DI_AlarmCODE2, DI_AlarmCODE3, DI_AlarmCODE4, DI_AlarmCODE5, DI_AlarmCODE6, DI_AlarmCODE7, DI_AlarmCODE8, DI_AlarmCODE9, DI_AlarmCODE10, DI_AlarmCODE11, DI_AlarmCODE12, DI_AlarmCODE13, DI_AlarmCODE14, DI_AlarmCODE15, DI_AlarmCODE16, DI_AlarmCODE17, DI_AlarmCODE18, DI_AlarmCODE19, DI_AlarmCODE20, DI_AlarmCODE21, DI_AlarmCODE22, DI_AlarmCODE23, DI_AlarmCODE24, DI_AlarmCODE25, DI_AlarmCODE26, DI_AlarmCODE27, DI_AlarmCODE28, DI_AlarmCODE29, DI_AlarmCODE30, DI_AlarmCODE31, DI_AlarmCODE32, DI_AlarmCODE33, DI_AlarmCODE34, DI_AlarmCODE35, DI_AlarmCODE36, DI_AlarmCODE37, DI_AlarmCODE38, DI_AlarmCODE39, DI_AlarmCODE40, DI_AlarmCODE41, DI_AlarmCODE42, DI_AlarmCODE43, DI_AlarmCODE44, DI_AlarmCODE45, DI_AlarmCODE46, DI_AlarmCODE47, DI_AlarmCODE48, DI_AlarmCODE49, DI_AlarmCODE50, DI_AlarmCODE51, DI_AlarmCODE52 = dis_in_regs HR_VENTILATION_MODE, HR_MaxSPEED_MODE, HR_SPEED_MODE = hl_regs_read print("Время", real_time) print("Время", type(real_time)) # тест списков :) # print("количество значений списка переменних", len(other)) print("количество значений списка регистров", type(hl_regs_read)) print(CL_POWER) print(IR_CurSelTEMP) print(DI_StatusHEATER) print(HR_SPEED_MODE)
# 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)) else: print("unable to read") toggle = not toggle # sleep 2s before next polling time.sleep(2)
client = ModbusClient("192.168.0.124", 502) client.open() print("Connected to Modbus!") Cap = cv2.VideoCapture(1, cv2.CAP_DSHOW) # Initialize camera window = Kuka.init_GUI() # Initialize GUI calibrate_thread = threading.Thread(target=calibration) # Declare a thread to run the calibration function calibrate_thread.start() # Start the thread # Main loop while True: color, x, y, = 0, 0, 0 # Set x, y, and color variables to zero # read from modbus try: yellow = client.read_coils(10)[0] green = client.read_coils(11)[0] red = client.read_coils(12)[0] blue = client.read_coils(13)[0] except TypeError: print("Modbus Error") yellow, green, red, blue = False, False, False, False pass print(yellow, green, red, blue) ret, frame = Cap.read() # Read from camera blur = cv2.GaussianBlur(frame, (25, 25), 10) # Blur the image hsv = cv2.cvtColor(blur, cv2.COLOR_BGR2HSV) # make a HSV version of the blurred image Kuka.show_cal_screen(hsv, frame, thresholds) # Show the calibration screen
class modbusTCP_Slave: # Working Modes RESULT_MODE__RETURN_ALL_VALUES__RAW = 0 RESULT_MODE__RETURN_ALL_VALUES__FORMATTED = 1 RESULT_MODE__RETURN_ONLY_NEW_VALUES__RAW = 2 RESULT_MODE__RETURN_ONLY_NEW_VALUES__FORMATTED = 3 def __init__(self, _jsonFile, _resultMode=0): # JSON object with the information of the Modbus Master device self.deviceHost = _jsonFile["host"] self.devicePort = _jsonFile["port"] self.deviceData = _jsonFile["data"] # Result Mode self.resultMode = int(_resultMode) # For Polling Process self.deviceData_LastValues = "" self.deviceData_CurrentValues = "" # Connect with Modbus Server def SetupPollingProcess(self): # Initialize deviceData values for data_point in self.deviceData: data_point["value"] = 0 try: # Create Client self.client = ModbusClient() self.client.host(self.deviceHost) self.client.port(self.devicePort) self.client.unit_id(1) except ValueError as e: # Problems accessing Modbus Server return -1 # Initialize copies of data self.deviceData_LastValues = copy.deepcopy(self.deviceData) self.deviceData_CurrentValues = copy.deepcopy(self.deviceData) # Setup process OK return 0 # Poll data from Modbus Master device def PollDataFromDevice(self): # Open or reconnect TCP to server if not self.client.is_open(): if not self.client.open(): return -1 if self.client.is_open(): # Go through data and act correspondingly for data_point in self.deviceData_CurrentValues: addr = data_point["address"] if data_point["type"] == "coil": read_data = self.client.read_coils(addr, 1) if read_data != None: data_point["value"] = read_data elif data_point["type"] == "register": read_data = self.client.read_holding_registers(addr, 1) if read_data != None: data_point["value"] = read_data ### Process Data and return it depending on Working Mode # RESULT_MODE__RETURN_ALL_VALUES__RAW ==> 0 # RESULT_MODE__RETURN_ALL_VALUES__FORMATTED ==> 0 # RESULT_MODE__RETURN_ONLY_NEW_VALUES__RAW ==> 0 # RESULT_MODE__RETURN_ONLY_NEW_VALUES__FORMATTED ==> 0 ################################################################## if (self.resultMode == self.RESULT_MODE__RETURN_ALL_VALUES__RAW): # All data - RAW return self.deviceData_CurrentValues elif (self.resultMode == self.RESULT_MODE__RETURN_ALL_VALUES__FORMATTED): # All data - FORMATTED return self.__formatDataValue(self.deviceData_CurrentValues) else: # Check for new values newValues = self.__checkForNewValues( self.deviceData_LastValues, self.deviceData_CurrentValues) # Copy of the latest received data self.deviceData_LastValues = copy.deepcopy( self.deviceData_CurrentValues) # Process new values, if there is any if (newValues != []): # RAW or FORMATTED? if (self.resultMode == self.RESULT_MODE__RETURN_ONLY_NEW_VALUES__RAW): return newValues elif (self.resultMode == self.RESULT_MODE__RETURN_ONLY_NEW_VALUES__FORMATTED): return self.__formatDataValue(newValues) else: return [] # Format RAW data according to this template # [ 07/16/20 @ 12:48:06 - 126 ] ---> localhost @ Port: 11503 ==> Value: False from coil at address: 1520 def __formatDataValue(self, values): # Prepare timestamp temp = datetime.datetime.now() x = temp.strftime("%x") y = temp.strftime("%X") z = temp.strftime("%f") timestamp = "[ " + x + " @ " + y + " - " + z[:3] + " ]" result = [] for data_point in values: # Prepare value value = str(data_point["value"][0]).rjust(5) # Format information formattedValue = (str(timestamp) + " ---> " + str(self.deviceHost) + " @ Port: " + str(self.devicePort) + " ==> Value: " + value + " from " + (data_point["type"]).rjust(8) + " at address: " + str(data_point["address"])) result.append(formattedValue) return result # Check if latest polled data is new # Only new data will be taken into account def __checkForNewValues(self, lastValues, currentValues): # Temp array newValues = [] for current_DP in currentValues: # Check if the value changed for last_DP in lastValues: if (last_DP["type"] == current_DP["type"]) and ( last_DP["address"] == current_DP["address"]) and ( last_DP["value"] != current_DP["value"]): # --- New value newValues.append(current_DP) return newValues
class ModbusTCPSensor(OMPluginBase): """ Get sensor values form modbus """ name = 'modbusTCPSensor' version = '1.0.18' interfaces = [('config', '1.0')] config_description = [{ 'name': 'modbus_server_ip', 'type': 'str', 'description': 'IP or hostname of the ModBus server.' }, { 'name': 'modbus_port', 'type': 'int', 'description': 'Port of the ModBus server. Default: 502' }, { 'name': 'debug', 'type': 'int', 'description': 'Turn on debugging (0 = off, 1 = on)' }, { 'name': 'sample_rate', 'type': 'int', 'description': 'How frequent (every x seconds) to fetch the sensor data, Default: 60' }, { 'name': 'sensors', 'type': 'section', 'description': 'OM sensor ID (e.g. 4), a sensor type and a Modbus Address', 'repeat': True, 'min': 0, 'content': [{ 'name': 'sensor_id', 'type': 'int' }, { 'name': 'sensor_type', 'type': 'enum', 'choices': ['temperature', 'humidity', 'brightness', 'validation_bit'] }, { 'name': 'modbus_address', 'type': 'int' }, { 'name': 'modbus_register_length', 'type': 'int' }] }, { 'name': 'bits', 'type': 'section', 'description': 'OM validation bit ID (e.g. 4), and a Modbus Coil Address', 'repeat': True, 'min': 0, 'content': [{ 'name': 'validation_bit_id', 'type': 'int' }, { 'name': 'modbus_coil_address', 'type': 'int' }] }] default_config = {'modbus_port': 502, 'sample_rate': 60} def __init__(self, webinterface, logger): super(ModbusTCPSensor, self).__init__(webinterface, logger) self.logger('Starting ModbusTCPSensor plugin...') self._config = self.read_config(ModbusTCPSensor.default_config) self._config_checker = PluginConfigChecker( ModbusTCPSensor.config_description) py_modbus_tcp_egg = '/opt/openmotics/python/plugins/modbusTCPSensor/pyModbusTCP-0.1.7-py2.7.egg' if py_modbus_tcp_egg not in sys.path: sys.path.insert(0, py_modbus_tcp_egg) self._client = None self._samples = [] self._save_times = {} self._read_config() self.logger("Started ModbusTCPSensor plugin") def _read_config(self): self._ip = self._config.get('modbus_server_ip') self._port = self._config.get( 'modbus_port', ModbusTCPSensor.default_config['modbus_port']) self._debug = self._config.get('debug', 0) == 1 self._sample_rate = self._config.get( 'sample_rate', ModbusTCPSensor.default_config['sample_rate']) self._sensors = [] # Load Sensors for sensor in self._config.get('sensors', []): if 0 <= sensor['sensor_id'] < 32: self._sensors.append(sensor) # Load Validation bits self._validation_bits = [] for validation_bit in self._config.get('bits', []): if 0 <= validation_bit['validation_bit_id'] < 256: self._validation_bits.append(validation_bit) self._enabled = len(self._sensors) > 0 try: from pyModbusTCP.client import ModbusClient self._client = ModbusClient(self._ip, self._port, auto_open=True, auto_close=True) self._client.open() self._enabled = self._enabled & True except Exception as ex: self.logger('Error connecting to Modbus server: {0}'.format(ex)) self.logger('ModbusTCPSensor is {0}'.format( 'enabled' if self._enabled else 'disabled')) @background_task def run(self): while True: try: if not self._enabled or self._client is None: time.sleep(5) continue # Process all configured sensors self.process_sensors() # Process all validation bits self.process_validation_bits() time.sleep(self._sample_rate) except Exception as ex: self.logger('Could not process sensor values: {0}'.format(ex)) time.sleep(15) def clamp_sensor(self, value, sensor_type): clamping = { 'temperature': [-32, 95.5, 1], 'humidity': [0, 100, 1], 'brightness': [0, 100, 0] } return round( max(clamping[sensor_type][0], min(value, clamping[sensor_type][1])), clamping[sensor_type][2]) def process_sensors(self): om_sensors = {} for sensor in self._sensors: registers = self._client.read_holding_registers( sensor['modbus_address'], sensor['modbus_register_length']) if registers is None: continue sensor_value = struct.unpack( '>f', struct.pack('BBBB', registers[1] >> 8, registers[1] & 255, registers[0] >> 8, registers[0] & 255))[0] if not om_sensors.get(sensor['sensor_id']): om_sensors[sensor['sensor_id']] = { 'temperature': None, 'humidity': None, 'brightness': None } sensor_value = self.clamp_sensor(sensor_value, sensor['sensor_type']) om_sensors[sensor['sensor_id']][ sensor['sensor_type']] = sensor_value if self._debug == 1: self.logger('The sensors values are: {0}'.format(om_sensors)) for sensor_id, values in om_sensors.iteritems(): result = json.loads( self.webinterface.set_virtual_sensor(sensor_id, **values)) if result['success'] is False: self.logger( 'Error when updating virtual sensor {0}: {1}'.format( sensor_id, result['msg'])) def process_validation_bits(self): for validation_bit in self._validation_bits: bit = self._client.read_coils( validation_bit['modbus_coil_address'], 1) if bit is None or len(bit) != 1: if self._debug == 1: self.logger('Failed to read bit {0}, bit is {1}'.format( validation_bit['validation_bit_id'], bit)) continue result = json.loads( self.webinterface.do_basic_action( None, 237 if bit[0] else 238, validation_bit['validation_bit_id'])) if result['success'] is False: self.logger('Failed to set bit {0} to {1}'.format( validation_bit['validation_bit_id'], 1 if bit[0] else 0)) else: if self._debug == 1: self.logger('Successfully set bit {0} to {1}'.format( validation_bit['validation_bit_id'], 1 if bit[0] else 0)) @om_expose def get_config_description(self): return json.dumps(ModbusTCPSensor.config_description) @om_expose def get_config(self): return json.dumps(self._config) @om_expose def set_config(self, config): config = json.loads(config) for key in config: if isinstance(config[key], six.string_types): config[key] = str(config[key]) self._config_checker.check_config(config) self.write_config(config) self._config = config self._read_config() return json.dumps({'success': 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)
# define o cliente c = ModbusClient() # debug do cliente #c.debug(True) # ip/porta do servidor modbus (para se conectar) c.host("10.13.110.215") c.port(502) while True: # para abrir a conexao TCP com o servidor if not c.is_open(): if not c.open(): print ("unable to connect") # read_coils() para leitura de dados vindo do servidor/supervisorio on = c.read_coils(6) # se a conexao estabelecida, entao escreve (write coils (modbus function 0x01)) if c.is_open(): c.write_single_coil(v1, valvula1) c.write_single_coil(v2, valvula2) c.write_single_register(n1, nivel1) c.write_single_register(n2, nivel2) # ... logica de codigo a implementar de acordo com a aplicacao a ser desenvolvida
def onHeartbeat(self): Domoticz.Debug("onHeartbeat called") 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: c207 = BinaryPayloadDecoder.fromRegisters( client.read_coils(207, 1), byteorder=Endian.Big, wordorder=Endian.Big).decode_16bit_int() c211 = BinaryPayloadDecoder.fromRegisters( client.read_coils(211, 1), byteorder=Endian.Big, wordorder=Endian.Big).decode_16bit_int() c902 = BinaryPayloadDecoder.fromRegisters( client.read_coils(902, 1), byteorder=Endian.Big, wordorder=Endian.Big).decode_16bit_int() c1200 = BinaryPayloadDecoder.fromRegisters( client.read_coils(1200, 1), byteorder=Endian.Big, wordorder=Endian.Big).decode_16bit_int() #Domoticz.Debug("Modbus response: v='" + str(i201) + "'") except: Domoticz.Error( "Modbus TCP/IP communication error (input registers). Check it out!" ) try: i200 = BinaryPayloadDecoder.fromRegisters( client.read_input_registers(200, 1), byteorder=Endian.Big, wordorder=Endian.Big).decode_16bit_int() i201 = BinaryPayloadDecoder.fromRegisters( client.read_input_registers(201, 1), byteorder=Endian.Big, wordorder=Endian.Big).decode_16bit_int() i203 = BinaryPayloadDecoder.fromRegisters( client.read_input_registers(203, 1), byteorder=Endian.Big, wordorder=Endian.Big).decode_16bit_int() i204 = BinaryPayloadDecoder.fromRegisters( client.read_input_registers(204, 1), byteorder=Endian.Big, wordorder=Endian.Big).decode_16bit_int() i205 = BinaryPayloadDecoder.fromRegisters( client.read_input_registers(205, 1), byteorder=Endian.Big, wordorder=Endian.Big).decode_16bit_int() i206 = BinaryPayloadDecoder.fromRegisters( client.read_input_registers(206, 1), byteorder=Endian.Big, wordorder=Endian.Big).decode_16bit_int() #Domoticz.Debug("Modbus response: v='" + str(i201) + "'") except: Domoticz.Error( "Modbus TCP/IP communication error (input registers). Check it out!" ) try: d200 = BinaryPayloadDecoder.fromRegisters( client.read_discrete_inputs(200, 1), byteorder=Endian.Big, wordorder=Endian.Big).decode_16bit_int() d201 = BinaryPayloadDecoder.fromRegisters( client.read_discrete_inputs(201, 1), byteorder=Endian.Big, wordorder=Endian.Big).decode_16bit_int() d202 = BinaryPayloadDecoder.fromRegisters( client.read_discrete_inputs(202, 1), byteorder=Endian.Big, wordorder=Endian.Big).decode_16bit_int() d203 = BinaryPayloadDecoder.fromRegisters( client.read_discrete_inputs(203, 1), byteorder=Endian.Big, wordorder=Endian.Big).decode_16bit_int() #Domoticz.Debug("Modbus response: v='" + str(i201) + "'") except: Domoticz.Error( "Modbus TCP/IP communication error (discrete inputs). Check it out!" ) try: v1000 = BinaryPayloadDecoder.fromRegisters( client.read_holding_registers(1000, 1), byteorder=Endian.Big, wordorder=Endian.Big).decode_16bit_int() v1001 = BinaryPayloadDecoder.fromRegisters( client.read_holding_registers(1001, 1), byteorder=Endian.Big, wordorder=Endian.Big).decode_16bit_int() #v1008 = BinaryPayloadDecoder.fromRegisters(client.read_holding_registers(1008, 1), byteorder=Endian.Big, wordorder=Endian.Big).decode_16bit_int() #v1009 = BinaryPayloadDecoder.fromRegisters(client.read_holding_registers(1009, 1), byteorder=Endian.Big, wordorder=Endian.Big).decode_16bit_int() #v1012 = BinaryPayloadDecoder.fromRegisters(client.read_holding_registers(1012, 1), byteorder=Endian.Big, wordorder=Endian.Big).decode_16bit_int() #v1013 = BinaryPayloadDecoder.fromRegisters(client.read_holding_registers(1013, 1), byteorder=Endian.Big, wordorder=Endian.Big).decode_16bit_int() v1015 = BinaryPayloadDecoder.fromRegisters( client.read_holding_registers(1015, 1), byteorder=Endian.Big, wordorder=Endian.Big).decode_16bit_int() v1016 = BinaryPayloadDecoder.fromRegisters( client.read_holding_registers(1016, 1), byteorder=Endian.Big, wordorder=Endian.Big).decode_16bit_int() #Domoticz.Debug("Modbus response: v='" + str(v1015) + "'") except: Domoticz.Error( "Modbus TCP/IP communication error (holding registers). Check it out!" ) # == Temperatures Devices[self.uTU1].Update(0, str(i200 / 10)) Devices[self.uTU2].Update(0, str(i201 / 10)) Devices[self.uTEa].Update(0, str(i203 / 10)) Devices[self.uTEb].Update(0, str(i204 / 10)) # == IN1 & IN2 # I00205 Stav vstupu IN1 (0-10V): Analogový vstup: U= DATA/1000, Kontaktní vstup: rozepnuto ~ 3350 až 3450, sepnuto ~ do 20 # I00206 Stav vstupu IN2 (0-10V): Analogový vstup: U= DATA/1000, Kontaktní vstup: rozepnuto ~ 3350 až 3450, sepnuto ~ do 20 # kontakt: Statuses: Open: nValue = 1, Closed: nValue = 0 if self.uIN1 in Devices: if self.atreaIN1type == "Contact": Devices[self.uIN1].Update(int(not (i205 <= 50)), "") elif Devices[self.uIN1].Type == 81: # Humidity_status can be one of: # 0=Normal 45~50, 55~60 # 1=Comfortable 50~55 # 2=Dry <45 # 3=Wet >60 if i205 < 4500: humstat = 2 elif i205 > 6000: humstat = 3 elif i205 >= 5000 and i205 <= 5500: humstat = 1 else: humstat = 0 Devices[self.uIN1].Update(int(i205 / 100), str(humstat)) else: Devices[self.uIN1].Update(int(i205 / 100), str(i205 / 100)) if self.uIN2 in Devices: if self.atreaIN2type == "Contact": Devices[self.uIN2].Update(int(not (i206 <= 50)), "") elif Devices[self.uIN2].Type == 81: if i206 < 4500: humstat = 2 elif i206 > 6000: humstat = 3 elif i206 >= 5000 and i206 <= 5500: humstat = 1 else: humstat = 0 Devices[self.uIN2].Update(int(i206 / 100), str(humstat)) else: Devices[self.uIN2].Update(int(i206 / 100), str(i206 / 100)) # == D1 & D2 & D3 & D4 # D00200 Stav vstupu D1 : 0/1 : Vypnuto/Zapnuto # D00201 Stav vstupu D2 : 0/1 : Vypnuto/Zapnuto # D00202 Stav vstupu D3 : 0/1 : Vypnuto/Zapnuto # D00203 Stav vstupu D4 : 0/1 : Vypnuto/Zapnuto if self.uD1 in Devices: Devices[self.uD1].Update(int(d200 == 1), "") if self.uD2 in Devices: Devices[self.uD2].Update(int(d201 == 1), "") if self.uD3 in Devices: Devices[self.uD3].Update(int(d202 == 1), "") if self.uD4 in Devices: Devices[self.uD4].Update(int(d203 == 1), "") # == ZVT # C00207 0 = ZVT if self.uZVT in Devices: Devices[self.uZVT].Update(int(c207 == 0), "") # == Bypass # C00211 1 = Bypass flap if self.uBypass in Devices: Devices[self.uBypass].Update(int(c211 == 1), "") # == Heating season # C01200 1 = heating, 0 = non-heating if self.uHeatingSeason in Devices: Devices[self.uHeatingSeason].Update(int(c1200 == 1), "") # == Nightly Cooling # C00902 0 = recuperation, 1 = cooling if self.uNightlyCooling in Devices: Devices[self.uNightlyCooling].Update(int(c902 == 1), "") # == ControlMode if v1015 == 2 or v1016 == 2: Devices[self.uControlMode].Update(1, str(30)) elif v1015 == 1 or v1016 == 1: Devices[self.uControlMode].Update(0, str(10)) else: Devices[self.uControlMode].Update(0, str(20)) # == PowerReq # H01009 Nastavení požadovaného výkonu, pokud H01016=1, 0 = Vyp, 12=12%,..., 100 = 100% # H01013 Nastavení požadovaného výkonu, pokud H01016=0/2, 0 = Vyp, 12=12%,..., 100 = 100% #if (v1016 == 1): Devices[self.uPowerReq].Update(int(int(v1009) >= 10), str(self._powerPercentToDomoticzValue(v1009))) #elif (v1016 == 2): Devices[self.uPowerReq].Update(int(int(v1013) >= 10), str(self._powerPercentToDomoticzValue(v1013))) #elif (v1016 == 0): Devices[self.uPowerReq].Update(int(int(v1001) >= 10), str(self._powerPercentToDomoticzValue(v1001))) # == PowerCur # "0|60|90|120|150|180|240|300" Devices[self.uPowerCur].Update( int(int(v1001) >= 10), str(self._powerPercentToDomoticzValue(v1001))) # == 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í #if (v1015 == 1): # if v1008 == 0: Devices[self.uModeReq].Update(1, str(10)) # elif v1008 == 1: Devices[self.uModeReq].Update(1, str(20)) #elif (v1015 == 2): # if v1012 == 0: Devices[self.uModeReq].Update(1, str(10)) # elif v1012 == 1: Devices[self.uModeReq].Update(1, str(20)) #elif (v1015 == 0): # if v1000 == 0: Devices[self.uModeReq].Update(1, str(10)) # elif v1000 == 1: Devices[self.uModeReq].Update(1, str(20)) # == ModeCur # "|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" if (v1000 == 0): Devices[self.uModeCur].Update(int(int(v1001) >= 10), str(10)) elif (v1000 == 1): Devices[self.uModeCur].Update(int(int(v1001) >= 10), str(20)) elif (v1000 == 10): Devices[self.uModeCur].Update(1, str(30)) elif (v1000 == 11): Devices[self.uModeCur].Update(1, str(40)) elif (v1000 == 12): Devices[self.uModeCur].Update(1, str(50)) elif (v1000 == 13): Devices[self.uModeCur].Update(1, str(60)) elif (v1000 == 14): Devices[self.uModeCur].Update(1, str(70)) elif (v1000 == 15): Devices[self.uModeCur].Update(1, str(80)) elif (v1000 == 20): Devices[self.uModeCur].Update(1, str(90)) elif (v1000 == 21): Devices[self.uModeCur].Update(1, str(100)) elif (v1000 == 22): Devices[self.uModeCur].Update(1, str(110)) # == Filter alarm from XML via HTTP try: bChangedFilter = True timeOfChange = 0 xml = xmltodict.parse( requests.get("http://" + str(self.TCP_IP) + "/config/alarms.xml").content) for i in xml['root']['errors']['i']: if int(i['@i']) == 100: bChangedFilter = int(i['@p']) == 1 timeOfChange = i['@t'] dtOfChange = datetime.fromtimestamp(int(timeOfChange)) if bChangedFilter: Devices[self.uAlarmFilter].Update( 1, self._("Filter last changed") + ": " + str(dtOfChange)) else: Devices[self.uAlarmFilter].Update( 4, self._("Filter to change since") + ": " + str(dtOfChange)) except: Domoticz.Error( "Failed to get or process XML via HTML to get state of filter alarm." ) return
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()
#!/usr/bin/env python # -*- coding: utf-8 -*- import time # min_read_bit # minimal code for read 10 bits on IPv4 192.168.0.200 and print result on stdout from pyModbusTCP.client import ModbusClient c = ModbusClient(host="192.168.0.200", auto_open=True) while True: # read 10 bits at address 20480 bits = c.read_coils(20480, 10) print("bit ad #0 to 9: "+str(bits) if bits else "read error") # sleep 2s time.sleep(2)
# Taken from https://stackoverflow.com/a/16444786/4504053 binary_string = bin(struct.unpack('!i', struct.pack('!f', num))[0]) return [True if x == '1' else False for x in binary_string] if __name__ == '__main__': logging.basicConfig(level=logging.DEBUG) SERVER_HOST = '10.72.21.89' SERVER_PORT = 5020 logging.debug('Open connection to {}:{}'.format(SERVER_HOST, SERVER_PORT)) client = ModbusClient(host=SERVER_HOST, port=SERVER_PORT, auto_open=True) logging.debug('Send gyro data') # plyer.gyroscope.enable() # x, y, z = plyer.gyroscope.orientation # plyer.gyroscope.disable() x, y, z = 1, 0.1, -0.1 address_offset = 1 for data in (x, y, z): logging.debug('Sending value: {}'.format(data)) binary_data = number_to_array_bits(data) logging.debug('Data length: {}'.format(len(binary_data))) rr = client.write_multiple_registers(address_offset, binary_data) address_offset += len(binary_data) bits = client.read_coils(1, 32) logging.debug('Reading data from slave: {}'.format(bits)) logging.debug('Script complete.')
import time SERVER_HOST = "localhost" SERVER_PORT = 502 c = ModbusClient() # uncomment this line to see debug message #c.debug(True) # 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(): # read 10 bits at address 0, store result in regs list bits = c.read_coils(0, 10) # if success display registers if bits: print("bit ad #0 to 9: "+str(bits)) # sleep 2s before next polling time.sleep(2)