def reader(): c = ModbusTcpClient(host="localhost", port=502) if not c.is_socket_open() and not c.connect(): print("unable to connect to host") if not c.is_socket_open(): return None holdingRegisters = c.read_holding_registers(1, 4) if holdingRegisters.isError(): print('Error reading registers') return None # Imagine we've "energy" value in position 1 with two words energy = ( holdingRegisters.registers[0] << 16) | holdingRegisters.registers[1] # Imagine we've "power" value in position 3 with two words power = ( holdingRegisters.registers[2] << 16) | holdingRegisters.registers[3] out = {"energy": energy, "power": power} print(out) return json.dumps(out)
class SMAModbus: def __init__(self, url, unit_id): logging.info('Connecting to Modbus server: {}'.format(url)) self._client = ModbusTcpClient('192.168.1.88') self._client.connect() logging.info('Connected') self._unit_id = unit_id def readModbus(self): if not self._client.is_socket_open(): self._client.connect() data = {} for tag, cfg in modbus_fields.items(): result = self._client.read_holding_registers(cfg['address'], 2, unit=self._unit_id) if result.isError(): logging.error( 'Reading registers for {} gave an error'.format(tag)) continue w1 = struct.pack('H', result.registers[0] ) # Assuming register values are unsigned short's w2 = struct.pack('H', result.registers[1] ) # Assuming register values are unsigned short's if cfg['datatype'] == 'S32': v = struct.unpack('i', w2 + w1) elif cfg['datatype'] == 'U32': v = struct.unpack('I', w2 + w1) else: raise NotImplementedError( 'Datatype {} is not implemented'.format(cfg['datatype'])) v = v[0] / cfg['scale'] if v < cfg['min'] or v > cfg['max']: logging.error( 'Value out of range for {}: {} < {} < {} [{}]'.format( tag, cfg['min'], v, cfg['max'], cfg['unit'])) logging.error('Raw data: {}.{}'.format(result.registers[0], result.registers[1])) #skip output continue v = (result.registers[0] * 2**16 + result.registers[1]) / cfg['scale'] # check for negative or larger than 100y of output if v < 0 or v > 100 * 3000 * 1000: v = 0. data[tag] = v return data
class ClientDevice(device.Device): def __init__(self, device_type, slave_id=None, name=None, pathlist=None, baudrate=None, parity=None, ipaddr=None, ipport=None, timeout=None, trace=False): device.Device.__init__(self, addr=None) self.type = device_type self.name = name self.pathlist = pathlist self.slave_id = slave_id self.modbus_device = None self.retry_count = 2 self.base_addr_list = [40000, 0, 50000] try: if device_type == RTU: self.modbus_device = ModbusSerialClient(method=RTU) elif device_type == TCP: self.modbus_device = ModbusTcpClient(host=ipaddr, port=ipport) self.modbus_device.trace(trace) except Exception as e: if self.modbus_device is not None: self.modbus_device.close() raise SunSpecClientError('Modbus error: %s' % str(e)) def close(self): if self.modbus_device is not None: self.modbus_device.close() def is_connected(self): return (self.modbus_device is not None) and (self.modbus_device.is_socket_open()) def connect(self): if self.is_connected(): connected = self.modbus_device.connect() if not connected: raise SunSpecClientError('Modbus connection error') def read(self, addr, count): try: if self.modbus_device is not None: response = self.modbus_device.read_holding_registers(address=addr, count=count, unit=self.slave_id) if response.isError(): return str(response) # raise SunSpecClientError(str(response)) return SunSpecDecoder.fromRegisters(response.registers)._payload else: raise SunSpecClientError('No modbus device set for SunSpec device') except Exception as e: raise SunSpecClientError('Modbus read error: %s' % str(e)) def write(self, addr, data): try: if self.modbus_device is not None: return self.modbus_device.write_registers(addr, data) else: raise SunSpecClientError('No modbus device set for SunSpec device') except Exception as e: raise SunSpecClientError('Modbus write error: %s' % str(e)) def read_points(self): for model in self.models_list: model.read_points() def scan(self, progress=None, delay=None): error = '' if delay is not None: time.sleep(delay) if self.base_addr is None: for addr in self.base_addr_list: # print('trying base address %s' % (addr)) try: data = self.read(addr, 3) # print("Data: %s" % str(data)) if data[:4] == b'SunS': self.base_addr = addr # print('device base address = %d' % self.base_addr) break else: error = 'Device responded - not SunSpec register map' except SunSpecClientError as e: if not error: error = str(e) if delay is not None: time.sleep(delay) if self.base_addr is not None: # print('base address = %s' % (self.base_addr)) model_id = util.data_to_u16(data[4:6]) addr = self.base_addr + 2 while model_id != suns.SUNS_END_MODEL_ID: # read model and model len separately due to some devices not supplying # count for the end model id data = self.read(addr + 1, 1) if data and len(data) == 2: if progress is not None: cont = progress('Scanning model %s' % (model_id)) if not cont: raise SunSpecClientError('Device scan terminated') model_len = util.data_to_u16(data) # print('model_id = %s model_len = %s' % (model_id, model_len)) # move address past model id and length model = ClientModel(self, model_id, addr + 2, model_len) # print('loading model %s at %d' % (model_id, addr + 2)) try: model.load() except Exception as e: model.load_error = str(e) self.add_model(model) addr += model_len + 2 data = self.read(addr, 1) if data and len(data) == 2: model_id = util.data_to_u16(data) else: break else: break if delay is not None: time.sleep(delay) else: if not error: error = 'Unknown error' raise SunSpecClientError(error)
class device(): def __init__(self, device='ION7650', code=[], reg=[], cnt=[], indexes=[], ip='127.0.0.1', port=502, timeout=1, hata='hata', address=255): self.device = device #device'in tipi icin kullanilmakta. ION7650 vs. #self.whole = {} #? self.ip = ip #device ip bilgisi tasir self.port = port #okuma yapilacak port 502 default self.timeout = timeout #timeout suresi self.hata = hata #hata tipi ya da cumlesi self.code = code #details config file'dan INPUT ya da HOLDING gibi okuma ayrimlari icin self.reg = reg # register verisini tutar self.cnt = cnt # register okuma adedi self.indexes = indexes # one shot read index addresses self.address = address # modbus address self.con_stat = False #connection status self.DevDict = {} #self.regs = [] self.reg_type = "UINT16" self.delay = 3600 self.old_data = [] self.data = [] self.TempData = [ ] #okunan ancak indexlenmemis ilk veriyi tutmak icin. Ozellikle float verilerde. self.conn = 5 def Connect(self): self.conn = ModbusTcpClient(host=self.ip, port=self.port, timeout=self.timeout) try: self.con_stat = self.conn.connect() except CError: print(self.hata) self.con_stat = False def read_input_U16(self, x): #for the single reads if self.conn.is_socket_open(self): try: self.rr = self.conn.read_input_registers(self.reg[x], self.cnt[x], unit=self.address) except CError: print "no connection try again" #CSVWrite("connection_logs.csv", Error=CError.message) else: if hasattr(self.rr, 'registers'): self.data.append(self.rr.registers[0]) else: print "possible close_wait situation" else: print "No active connection" def read_input_S16(self, x): # for the single reads if self.conn.is_socket_open(): try: self.rr = self.conn.read_input_registers(self.reg[x], self.cnt[x], unit=self.address) except CError: print "no connection try again" #CSVWrite("connection_logs.csv", Error=CError.message) else: if hasattr(self.rr, 'registers'): if self.rr.registers[0] > 32768: self.data.append( ctypes.c_int( (self.rr.registers[0] ^ 0XFFFF) * -1).value) else: self.data.append(self.rr.registers[0]) else: print "possible close_wait situation" else: print "No active connection" def read_holding_floats( self, reg, cnt, index): # for the multi reads x degeri read() fonksiyonundaki if self.conn.is_socket_open(): try: self.TempData = [] self.rr = self.conn.read_holding_registers(reg, cnt * 2, unit=self.address) if hasattr(self.rr, 'registers'): self.decoder = BinaryPayloadDecoder.fromRegisters( self.rr.registers, Endian.Big, wordorder=Endian.Big) for a in range(0, cnt): self.TempData.append( round(self.decoder.decode_32bit_float(), 2)) self.data.append([self.TempData[i] for i in index]) else: self.TempData = [] self.rr = self.conn.read_holding_registers( reg, cnt * 2, unit=self.address) if hasattr(self.rr, 'registers'): self.decoder = BinaryPayloadDecoder.fromRegisters( self.rr.registers, Endian.Big, wordorder=Endian.Big) for a in range(0, cnt): self.TempData.append( round(self.decoder.decode_32bit_float(), 2)) self.data.append([ self.TempData[i] for i in index ]) #a list comprehantion.we may use even an if clause. except CError: print "no connection try again" #CSVWrite("connection_logs.csv", Er #error=CError.message) else: pass else: print "No active connection" #def JustConnect(self): # self.conn, self.con_stat = Connection(self.ip, self.port, self.timeout, self.hata) def read(self): self.data.clear() #cihazdan okunan toplam veri her okumada sifirlanir. for x in range(0, len(self.code)): time.sleep(self.delay) if self.code[x] == "INPUT": # Read input unsigned registers if self.reg_type[x] == "UINT16": #self.read_input_U16(x) pass # Read input registers signed values elif self.reg_type[x] == "INT16": pass # multi float reading aproach from payload Text pass elif self.code[x] == "HOLDING": if self.reg_type[x] == "UINT16": #self.read_input_U16(x) pass # Read input registers signed values elif self.reg_type[x] == "INT16": pass # multi float reading aproach from payload elif self.reg_type[x] == "FLOATS": self.read_holding_floats(x) #_________________________________Dictionary Version def ReadDict(self): if self.conn.is_socket_open(): self.data = [ ] #cihazdan okunan toplam veri her okumada sifirlanir. for x in self.DevDict.keys(): if x == 'HOLDING': for y in self.DevDict[x].keys(): if y == 'UINT16': pass elif y == "INT16": pass elif y == "FLOAT": for z in xrange(0, len(self.DevDict[x][y]['count'])): self.read_holding_floats( self.DevDict[x][y]['start_reg'][z], self.DevDict[x][y]['count'][z], self.DevDict[x][y]['indexes'][z]) else: pass else: print "No active connection" def JustClose(self): self.conn.close() def socket(self): return self.conn.is_socket_open()
class SolarEdge: model = "SolarEdge" stopbits = 1 parity = "N" baud = 115200 wordorder = Endian.Big def __init__( self, host=False, port=False, device=False, stopbits=False, parity=False, baud=False, timeout=TIMEOUT, retries=RETRIES, unit=UNIT, parent=False ): if parent: self.client = parent.client self.mode = parent.mode self.client = parent.client self.timeout = parent.timeout self.retries = parent.retries self.unit = parent.unit if self.mode is connectionType.RTU: self.device = parent.device self.stopbits = parent.stopbits self.parity = parent.parity self.baud = parent.baud elif self.mode is connectionType.TCP: self.host = parent.host self.port = parent.port else: raise NotImplementedError(self.mode) else: self.host = host self.port = port self.device = device if stopbits: self.stopbits = stopbits if parity: self.parity = parity if baud: self.baud = baud self.timeout = timeout self.retries = retries self.unit = unit if device: self.mode = connectionType.RTU self.client = ModbusSerialClient( method="rtu", port=self.device, stopbits=self.stopbits, parity=self.parity, baudrate=self.baud, timeout=self.timeout) else: self.mode = connectionType.TCP self.client = ModbusTcpClient( host=self.host, port=self.port, timeout=self.timeout ) def __repr__(self): if self.mode == connectionType.RTU: return f"{self.model}({self.device}, {self.mode}: stopbits={self.stopbits}, parity={self.parity}, baud={self.baud}, timeout={self.timeout}, retries={self.retries}, unit={hex(self.unit)})" elif self.mode == connectionType.TCP: return f"{self.model}({self.host}:{self.port}, {self.mode}: timeout={self.timeout}, retries={self.retries}, unit={hex(self.unit)})" else: return f"<{self.__class__.__module__}.{self.__class__.__name__} object at {hex(id(self))}>" def _read_holding_registers(self, address, length): for i in range(self.retries): result = self.client.read_holding_registers(address=address, count=length, unit=self.unit) if not isinstance(result, ReadHoldingRegistersResponse): continue if len(result.registers) != length: continue return BinaryPayloadDecoder.fromRegisters(result.registers, byteorder=Endian.Big, wordorder=self.wordorder) return None def _decode_value(self, data, length, dtype, vtype): try: if dtype == registerDataType.UINT16: decoded = data.decode_16bit_uint() elif (dtype == registerDataType.UINT32 or dtype == registerDataType.ACC32): decoded = data.decode_32bit_uint() elif dtype == registerDataType.UINT64: decoded = data.decode_64bit_uint() elif dtype == registerDataType.INT16: decoded = data.decode_16bit_int() elif (dtype == registerDataType.FLOAT32 or dtype == registerDataType.SEFLOAT): decoded = data.decode_32bit_float() elif dtype == registerDataType.STRING: decoded = data.decode_string(length * 2).decode("utf-8").replace("\x00", "").rstrip() else: raise NotImplementedError(dtype) if decoded == SUNSPEC_NOTIMPLEMENTED[dtype.name]: return vtype(False) else: return vtype(decoded) except NotImplementedError: raise def _read(self, value): address, length, rtype, dtype, vtype, label, fmt, batch = value try: if rtype == registerType.INPUT: return self._decode_value(self._read_input_registers(address, length), length, dtype, vtype) elif rtype == registerType.HOLDING: return self._decode_value(self._read_holding_registers(address, length), length, dtype, vtype) else: raise NotImplementedError(rtype) except NotImplementedError: raise except AttributeError: return False def _read_all(self, values, rtype): addr_min = False addr_max = False for k, v in values.items(): v_addr = v[0] v_length = v[1] if addr_min is False: addr_min = v_addr if addr_max is False: addr_max = v_addr + v_length if v_addr < addr_min: addr_min = v_addr if (v_addr + v_length) > addr_max: addr_max = v_addr + v_length results = {} offset = addr_min length = addr_max - addr_min try: if rtype == registerType.INPUT: data = self._read_input_registers(offset, length) elif rtype == registerType.HOLDING: data = self._read_holding_registers(offset, length) else: raise NotImplementedError(rtype) if not data: return results for k, v in values.items(): address, length, rtype, dtype, vtype, label, fmt, batch = v if address > offset: skip_bytes = address - offset offset += skip_bytes data.skip_bytes(skip_bytes * 2) results[k] = self._decode_value(data, length, dtype, vtype) offset += length except NotImplementedError: raise return results def connect(self): return self.client.connect() def disconnect(self): self.client.close() def connected(self): return self.client.is_socket_open() def read(self, key): if key not in self.registers: raise KeyError(key) return {key: self._read(self.registers[key])} def read_all(self, rtype=registerType.HOLDING): registers = {k: v for k, v in self.registers.items() if (v[2] == rtype)} results = {} for batch in range(1, len(registers)): register_batch = {k: v for k, v in registers.items() if (v[7] == batch)} if not register_batch: break results.update(self._read_all(register_batch, rtype)) return results
from pymodbus.client.sync import ModbusTcpClient import time #P2 Adam HL Relay #client = ModbusTcpClient('10.202.55.173', 502) #Cognex Scanner P2 L3 BF #client = ModbusTcpClient('10.202.193.182', 502) #Cognex Insight client = ModbusTcpClient('10.202.180.24', 502) client.connect() if client.is_socket_open(): print('cllent is open') #Cognex Holding Regs (Works) #result = client.read_holding_registers(21566,1) #result = client.read_input_registers(20542,1) #result = client.read_input_registers(20546,1) #Cognex Holding Regs (Works) #result = client.read_holding_registers(20546,1) #Cognex Holding Regs (Works) #result = client.read_holding_registers(2000,100) #Cognex Holding Regs (Works) result = client.read_holding_registers(4000, 4)
class SDM: model = "SDM" stopbits = 1 parity = "N" baud = 38400 registers = {} def __init__(self, host=False, port=False, device=False, stopbits=False, parity=False, baud=False, timeout=TIMEOUT, retries=RETRIES, unit=UNIT, parent=False): if parent: self.client = parent.client self.mode = parent.mode self.timeout = parent.timeout self.retries = parent.retries if unit: self.unit = unit else: self.unit = parent.unit if self.mode is connectionType.RTU: self.device = parent.device self.stopbits = parent.stopbits self.parity = parent.parity self.baud = parent.baud elif self.mode is connectionType.TCP: self.host = parent.host self.port = parent.port else: raise NotImplementedError(self.mode) else: self.host = host self.port = port self.device = device if stopbits: self.stopbits = stopbits if (parity and parity.upper() in ["N", "E", "O"]): self.parity = parity.upper() else: self.parity = False if baud: self.baud = baud self.timeout = timeout self.retries = retries self.unit = unit if device: self.mode = connectionType.RTU self.client = ModbusSerialClient(method="rtu", port=self.device, stopbits=self.stopbits, parity=self.parity, baudrate=self.baud, timeout=self.timeout) else: self.mode = connectionType.TCP self.client = ModbusTcpClient(host=self.host, port=self.port, timeout=self.timeout) def __repr__(self): if self.mode == connectionType.RTU: return f"{self.model}({self.device}, {self.mode}: stopbits={self.stopbits}, parity={self.parity}, baud={self.baud}, timeout={self.timeout}, retries={self.retries}, unit={hex(self.unit)})" elif self.mode == connectionType.TCP: return f"{self.model}({self.host}:{self.port}, {self.mode}: timeout={self.timeout}, retries={self.retries}, unit={hex(self.unit)})" else: return f"<{self.__class__.__module__}.{self.__class__.__name__} object at {hex(id(self))}>" def _read_input_registers(self, address, length): for i in range(self.retries): if not self.connected(): self.connect() time.sleep(0.1) continue result = self.client.read_input_registers(address=address, count=length, unit=self.unit) if not isinstance(result, ReadInputRegistersResponse): continue if len(result.registers) != length: continue return BinaryPayloadDecoder.fromRegisters(result.registers, byteorder=Endian.Big, wordorder=Endian.Big) return None def _read_holding_registers(self, address, length): for i in range(self.retries): if not self.connected(): self.connect() time.sleep(0.1) continue result = self.client.read_holding_registers(address=address, count=length, unit=self.unit) if not isinstance(result, ReadHoldingRegistersResponse): continue if len(result.registers) != length: continue return BinaryPayloadDecoder.fromRegisters(result.registers, byteorder=Endian.Big, wordorder=Endian.Big) return None def _write_holding_register(self, address, value): return self.client.write_registers(address=address, values=value, unit=self.unit) def _encode_value(self, data, dtype): builder = BinaryPayloadBuilder(byteorder=Endian.Big, wordorder=Endian.Big) try: if dtype == registerDataType.FLOAT32: builder.add_32bit_float(data) else: raise NotImplementedError(dtype) except NotImplementedError: raise return builder.to_registers() def _decode_value(self, data, length, dtype, vtype): try: if dtype == registerDataType.FLOAT32: return vtype(data.decode_32bit_float()) else: raise NotImplementedError(dtype) except NotImplementedError: raise def _read(self, value): address, length, rtype, dtype, vtype, label, fmt, batch = value try: if rtype == registerType.INPUT: return self._decode_value( self._read_input_registers(address, length), length, dtype, vtype) elif rtype == registerType.HOLDING: return self._decode_value( self._read_holding_registers(address, length), length, dtype, vtype) else: raise NotImplementedError(rtype) except NotImplementedError: raise def _read_all(self, values, rtype): addr_min = False addr_max = False for k, v in values.items(): v_addr = v[0] v_length = v[1] if addr_min is False: addr_min = v_addr if addr_max is False: addr_max = v_addr + v_length if v_addr < addr_min: addr_min = v_addr if (v_addr + v_length) > addr_max: addr_max = v_addr + v_length results = {} offset = addr_min length = addr_max - addr_min try: if rtype == registerType.INPUT: data = self._read_input_registers(offset, length) elif rtype == registerType.HOLDING: data = self._read_holding_registers(offset, length) else: raise NotImplementedError(rtype) if not data: return results for k, v in values.items(): address, length, rtype, dtype, vtype, label, fmt, batch = v if address > offset: skip_bytes = address - offset offset += skip_bytes data.skip_bytes(skip_bytes * 2) results[k] = self._decode_value(data, length, dtype, vtype) offset += length except NotImplementedError: raise return results def _write(self, value, data): address, length, rtype, dtype, vtype, label, fmt, batch = value try: if rtype == registerType.HOLDING: return self._write_holding_register( address, self._encode_value(data, dtype)) else: raise NotImplementedError(rtype) except NotImplementedError: raise def connect(self): return self.client.connect() def disconnect(self): self.client.close() def connected(self): return self.client.is_socket_open() def read(self, key): if key not in self.registers: raise KeyError(key) return self._read(self.registers[key]) def write(self, key, data): if key not in self.registers: raise KeyError(key) return self._write(self.registers[key], data) def read_all(self, rtype=registerType.INPUT): registers = { k: v for k, v in self.registers.items() if (v[2] == rtype) } results = {} for batch in range(1, len(registers)): register_batch = { k: v for k, v in registers.items() if (v[7] == batch) } if not register_batch: break results.update(self._read_all(register_batch, rtype)) return results
class Danbach_AGV(): def __init__(self, lwheel_scale=1.0, rwheel_scale=1.0, ip='192.168.10.30', port=502, timeout=7e-3): self.client = MbClient(ip, port=port, timeout=timeout) self.lwheel_scale = lwheel_scale self.rwheel_scale = rwheel_scale @property def connected(self): return self.client.is_socket_open() def connect(self): self.client.connect() self.client.write_registers(0x1600, [2, 0x0800, 0, 0]) def disconnect(self): self.client.close() def forward(self, distance, speed=DEFAULT_SPEED): if distance == 0 or speed == 0: return l0, r0 = self.__get_wheel_odo__() t0 = time.time() - CMD_PERIOD while True: l, r = self.__get_wheel_odo__() if l >= l0 + distance or r >= r0 + distance: break if time.time() - t0 > CMD_PERIOD: t0 = time.time() if (distance - l + l0 < GUARD_DIST) or (distance - r + r0 < GUARD_DIST): speed = min(speed, GUARD_SPEED) self.__set_wheel__(speed, speed) self.__set_wheel__(0, 0) def back(self, distance, speed=DEFAULT_SPEED): if distance == 0 or speed == 0: return l0, r0 = self.__get_wheel_odo__() t0 = time.time() - CMD_PERIOD while True: l, r = self.__get_wheel_odo__() if l < l0 - distance or r < r0 - distance: break if time.time() - t0 > CMD_PERIOD: t0 = time.time() if (distance - l0 + l < GUARD_DIST) or (distance - r0 + r < GUARD_DIST): speed = min(speed, GUARD_SPEED) self.__set_wheel__(-speed, -speed) self.__set_wheel__(0, 0) def pivot(self, radian, speed=DEFAULT_SPEED): if radian == 0 or speed == 0: return l0, r0 = self.__get_wheel_odo__() t0 = time.time() - CMD_PERIOD while True: l, r = self.__get_wheel_odo__() if abs(abs(l - l0 - r + r0)) / WHEEL_DIST >= abs(radian): break if time.time() - t0 > CMD_PERIOD: if abs(radian) - abs( abs(l - l0 - r + r0)) / WHEEL_DIST < GUARD_RADIAN: speed = min(speed, GUARD_SPEED) t0 = time.time() if radian > 0: self.__set_wheel__(-speed // 2, speed // 2) else: self.__set_wheel__(speed // 2, -speed // 2) self.__set_wheel__(0, 0) def steer(self, radian, direction=1, speed=DEFAULT_SPEED): if radian == 0 or speed == 0: return l0, r0 = self.__get_wheel_odo__() t0 = time.time() - CMD_PERIOD while True: l, r = self.__get_wheel_odo__() if abs((l - l0) - (r - r0)) / WHEEL_DIST >= abs(radian): print((l - l0 - r + r0) / WHEEL_DIST / pi) break if time.time() - t0 > CMD_PERIOD: if abs(radian) - abs((l - l0) - (r - r0)) / WHEEL_DIST < GUARD_RADIAN: speed = GUARD_SPEED t0 = time.time() if radian > 0 and direction == 1: self.__set_wheel__(0, speed) elif radian < 0 and direction == 1: self.__set_wheel__(speed, 0) elif radian > 0 and direction == -1: self.__set_wheel__(0, -speed) else: self.__set_wheel__(-speed, 0) self.__set_wheel__(0, 0) def turn(self, radian, inner_radius, direction=1, speed=DEFAULT_SPEED): self.__set_wheel__(0, 0) def __get_wheel_odo__(self): while True: try: rq = self.client.read_holding_registers(0x41E, 4, unit=1) L_WHEEL = rq.registers[0] * 0x00010000 + rq.registers[1] R_WHEEL = rq.registers[2] * 0x00010000 + rq.registers[3] except: continue break # 2's complement on int_32 L_WHEEL = L_WHEEL - 0x100000000 if (L_WHEEL & 0x80000000) else L_WHEEL R_WHEEL = R_WHEEL - 0x100000000 if (R_WHEEL & 0x80000000) else R_WHEEL return L_WHEEL * 1e-3 * self.lwheel_scale, R_WHEEL * 1e-3 * self.rwheel_scale def __get_wheel_odo_raw__(self): while True: try: rq = self.client.read_holding_registers(0x41E, 4, unit=1) L_WHEEL = rq.registers[0] * 0x00010000 + rq.registers[1] R_WHEEL = rq.registers[2] * 0x00010000 + rq.registers[3] except: continue break # 2's complement on int_32 L_WHEEL = L_WHEEL - 0x100000000 if (L_WHEEL & 0x80000000) else L_WHEEL R_WHEEL = R_WHEEL - 0x100000000 if (R_WHEEL & 0x80000000) else R_WHEEL return L_WHEEL, R_WHEEL def __set_wheel__(self, lspeed, rspeed): if lspeed == 0 and rspeed == 0: rq = self.client.write_registers(0x1620, [0x0002, 0, 0, 0, 0], unit=0x01) lspeed = lspeed + 0x10000 if lspeed < 0 else lspeed rspeed = rspeed + 0x10000 if rspeed < 0 else rspeed rq = self.client.write_registers(0x1620, [0x0001, lspeed, rspeed, 0, 0], unit=0x01) def __set_wheel__(self, lspeed, rspeed): if lspeed == 0 and rspeed == 0: rq = self.client.write_registers(0x1620, [0x0002, 0, 0, 0, 0], unit=0x01) lspeed = lspeed + 0x10000 if lspeed < 0 else lspeed rspeed = rspeed + 0x10000 if rspeed < 0 else rspeed rq = self.client.write_registers(0x1620, [0x0001, lspeed, rspeed, 0, 0], unit=0x01)
def device_polling(): if DashingEnabled: Dashlog.append("Starting device poller") ui.display() else: log.info("Starting device poller") CONF_ORNO = False CONF_EM370 = True CONF_SOLIS_4G_3P = True CONF_JANITZA_B23 = True CONF_EMONCMS = True RtuclientState = False QuerryNb = 0 HostName = 'emoncms.powerdale.com' ModbusHost = "10.10.10.101" ModbusPort = 502 emon_host = "emoncms.powerdale.com" emon_url = "/input/post.json?node=" emon_privateKey = "4dbf608f20eab1ad1d1bf4512dc85d1c" emon_node = "B4E" # for cycle in range(0, 2000): # vchart.append(50 + 50 * math.sin(cycle / 16.0)) # hchart.append(99.9 * abs(math.sin(cycle / 26.0))) # bchart.append(50 + 50 * math.sin(cycle / 6.0)) # ui.display() OR_WE_514_Registers = testmap.generate_device_dict( './mapping_tools/or_we_514-v1-1_1.json') EEM_MA370_Registers = testmap.generate_device_dict( './mapping_tools/eem_ma370-v1-1_1.json') SOLIS_4G_Registers = testmap.generate_device_dict( './mapping_tools/solis_4g_3p-v1-1_1.json') JANITZA_B23_Registers = testmap.generate_device_dict( './mapping_tools/janitza_b23-v1-1_1.json') #print(OR_WE_514_Registers) #print(json.dumps(EEM_MA370_Registers, indent='\t')) #print(OR_WE_514_Registers) #print(EEM_MA370_Registers) print(SOLIS_4G_Registers) SolarInverter = copy.deepcopy(SOLIS_4G_Registers) Grid = copy.deepcopy(EEM_MA370_Registers) Evse = copy.deepcopy(JANITZA_B23_Registers) SOLIS_Config = { 'Solar': { "NAME": "Inverter", "PORT": "/dev/ttyUSB0", "ADDRESS": 1, "DATA": SolarInverter } } EEM_Config = { 'Grid': { "NAME": "Grid", "PORT": "", "ADDRESS": 1, "DATA": Grid } } B23_Config = { 'Evse': { "NAME": "Evse", "PORT": "/dev/ttyUSB1", "ADDRESS": 2, "DATA": Evse } } # Create modbus clients if CONF_EM370: try: TcpClient = ModbusTcpClient(host=ModbusHost, port=ModbusPort, auto_open=True, timeout=5) if DashingEnabled: Dashlog.append( f"Modbus TCP client connected: {TcpClient.connect()}") else: log.info(f"Modbus TCP client connected: {TcpClient.connect()}") except Exception as e: if DashingEnabled: DashErrors.append("Exception %s" % str(e)) ui.display() else: log.info("Exception %s" % str(e)) pass if CONF_SOLIS_4G_3P: try: Rtuclient = ModbusRtuClient(method='rtu', port='/dev/ttyUSB0', stopbits=1, bytesize=8, parity='N', baudrate=9600, timeout=1) if DashingEnabled: Dashlog.append(f"Modbus RTU port open: {Rtuclient.connect()}") else: print(f"Modbus RTU port open: {Rtuclient.connect()}") except Exception as e: if DashingEnabled: DashErrors.append("Exception %s" % str(e)) ui.display() else: log.info("Exception %s" % str(e)) pass if CONF_JANITZA_B23: try: Rtuclient2 = ModbusRtuClient(method='rtu', port='/dev/ttyUSB1', stopbits=1, bytesize=8, parity='N', baudrate=9600, timeout=1) if DashingEnabled: Dashlog.append(f"Modbus RTU port open: {Rtuclient2.connect()}") else: print(f"Modbus RTU port open: {Rtuclient2.connect()}") except Exception as e: if DashingEnabled: DashErrors.append("Exception %s" % str(e)) ui.display() else: log.info("Exception %s" % str(e)) pass try: while True: #os.system('cls' if os.name == 'nt' else 'clear') QuerryNb = QuerryNb + 1 if CONF_EM370 and TcpClient.is_socket_open(): if DashingEnabled: Dashlog.append("Read EEM_MA370") ui.display() else: log.info("Read EEM_MA370") for x, z in EEM_Config.items(): if DashingEnabled: Dashlog.append(f"Reading slave {z['NAME']}") MyDict = z['DATA'] #print (MyDict) for k, y in MyDict.items(): #Log.append(y['Name']) if (y['Name'] == 'Active power'): if DashingEnabled: Dashlog.append( f"Active power: {y['Value']:.01f}W") hchart.title = f"Active power: {y['Value']:.0f}W" hchart.append(100 * y['Value'] / 5000) ui.display() rr = None try: rr = TcpClient.read_holding_registers( y['Address'], y['Size']) except Exception as e: if DashingEnabled: DashErrors.append("Exception %s" % str(e)) ui.display() else: log.info("Exception %s" % str(e)) if (isinstance(rr, ReadHoldingRegistersResponse) and (len(rr.registers) == y['Size'])): decoder = BinaryPayloadDecoder.fromRegisters( rr.registers, byteorder=Endian.Big, wordorder=Endian.Little) decoded = OrderedDict([ ('string', decoder.decode_string), ('bits', decoder.decode_bits), ('8int', decoder.decode_8bit_int), ('8uint', decoder.decode_8bit_uint), ('16int', decoder.decode_16bit_int), ('16uint', decoder.decode_16bit_uint), ('32int', decoder.decode_32bit_int), ('32uint', decoder.decode_32bit_uint), ('16float', decoder.decode_16bit_float), ('16float2', decoder.decode_16bit_float), ('32float', decoder.decode_32bit_float), ('32float2', decoder.decode_32bit_float), ('64int', decoder.decode_64bit_int), ('64uint', decoder.decode_64bit_uint), ('ignore', decoder.skip_bytes), ('64float', decoder.decode_64bit_float), ('64float2', decoder.decode_64bit_float), ]) y['Value'] = round( decoded[y['Type']]() * y['Scale'], 3) #print ( "Register: " + y['Name'] + " = " + str(y['Value']) + " " + y['Units']) #print ( f"Register: {y['Name']} = {y['Value']:.02f} {y['Units']}") #print("HOME") Rtuclient.connect() if CONF_SOLIS_4G_3P and Rtuclient.is_socket_open(): if DashingEnabled: Dashlog.append("Read Solis-4G inverter") ui.display() else: log.info("Read Solis-4G inverter") for x, z in SOLIS_Config.items(): if DashingEnabled: Dashlog.append(f"Reading slave {z['NAME']}") DashMeas1.append("") DashMeas1.append(f"Reading slave {z['NAME']}") ui.display() else: log.info(f"Reading slave {z['NAME']}") MyDict = z['DATA'] #print (MyDict) for k, y in MyDict.items(): #print(z['ADDRESS'], y['Address'], y['Size']) rr = None try: rr = Rtuclient.read_input_registers( y['Address'], y['Size'], unit=z['ADDRESS']) except Exception as e: if DashingEnabled: DashErrors.append("Exception %s" % str(e)) ui.display() else: log.info("Exception %s" % str(e)) pass if (isinstance(rr, ReadInputRegistersResponse) and (len(rr.registers) == y['Size'])): decoder = BinaryPayloadDecoder.fromRegisters( rr.registers, byteorder=Endian.Big, wordorder=Endian.Big) decoded = OrderedDict([ ('string', decoder.decode_string), ('bits', decoder.decode_bits), ('8int', decoder.decode_8bit_int), ('8uint', decoder.decode_8bit_uint), ('16int', decoder.decode_16bit_int), ('16uint', decoder.decode_16bit_uint), ('32int', decoder.decode_32bit_int), ('32uint', decoder.decode_32bit_uint), ('16float', decoder.decode_16bit_float), ('16float2', decoder.decode_16bit_float), ('32float', decoder.decode_32bit_float), ('32float2', decoder.decode_32bit_float), ('64int', decoder.decode_64bit_int), ('64uint', decoder.decode_64bit_uint), ('ignore', decoder.skip_bytes), ('64float', decoder.decode_64bit_float), ('64float2', decoder.decode_64bit_float), ]) y['Value'] = round( decoded[y['Type']]() * y['Scale'], 3) #print ( "Register: " + y['Name'] + " = " + str(y['Value']) + " " + y['Units']) if DashingEnabled: DashMeas1.append( f"{y['Name']} = {y['Value']:.02f} {y['Units']}" ) ui.display() #print ( f"Register: {y['Name']} = {y['Value']:.02f} {y['Units']}") # Rtuclient.close() Rtuclient2.connect() if CONF_JANITZA_B23 and Rtuclient2.is_socket_open(): if DashingEnabled: Dashlog.append("Read EVSE charger") ui.display() else: log.info("Read EVSE charger...") for x, z in B23_Config.items(): if DashingEnabled: Dashlog.append(f"Reading slave {z['NAME']}") DashMeas1.append("") DashMeas1.append(f"Reading slave {z['NAME']}") ui.display() else: log.info(f"Reading slave {z['NAME']}") MyDict = z['DATA'] print(MyDict) for k, y in MyDict.items(): #print(z['ADDRESS'], y['Address'], y['Size']) rr = None try: rr = Rtuclient2.read_holding_registers( y['Address'], y['Size'], unit=z['ADDRESS']) except Exception as e: if DashingEnabled: DashErrors.append("Exception %s" % str(e)) ui.display() else: log.info("Exception %s" % str(e)) pass if (isinstance(rr, ReadHoldingRegistersResponse) and (len(rr.registers) == y['Size'])): decoder = BinaryPayloadDecoder.fromRegisters( rr.registers, byteorder=Endian.Big, wordorder=Endian.Big) decoded = OrderedDict([ ('string', decoder.decode_string), ('bits', decoder.decode_bits), ('8int', decoder.decode_8bit_int), ('8uint', decoder.decode_8bit_uint), ('16int', decoder.decode_16bit_int), ('16uint', decoder.decode_16bit_uint), ('32int', decoder.decode_32bit_int), ('32uint', decoder.decode_32bit_uint), ('16float', decoder.decode_16bit_float), ('16float2', decoder.decode_16bit_float), ('32float', decoder.decode_32bit_float), ('32float2', decoder.decode_32bit_float), ('64int', decoder.decode_64bit_int), ('64uint', decoder.decode_64bit_uint), ('ignore', decoder.skip_bytes), ('64float', decoder.decode_64bit_float), ('64float2', decoder.decode_64bit_float), ]) y['Value'] = round( decoded[y['Type']]() * y['Scale'], 3) if math.isnan(y['Value']): y['Value'] = 0 #print ( "Register: " + y['Name'] + " = " + str(y['Value']) + " " + y['Units']) if DashingEnabled: DashMeas1.append( f"{y['Name']} = {y['Value']:.02f} {y['Units']}" ) ui.display() #print ( f"Register: {y['Name']} = {y['Value']:.02f} {y['Units']}") # Rtuclient.close() #print("HOME") if DashingEnabled: bchart.append(QuerryNb) else: log.info(f"Poller querry : {QuerryNb}") #print ( "Querry %s" % QuerryNb) if (CONF_EMONCMS and CONF_SOLIS_4G_3P): SolarPower = 0 for x, z in SOLIS_Config.items(): if Rtuclient.is_socket_open(): reqdata = {} #print("Slave: " + z['NAME']) MyDict = z['DATA'] #print (z['DATA']) for k, y in MyDict.items(): #DashErrors.append(y['Name']) if (y['Name'] == 'Freq'): vchart.append(50 + QuerryNb) reqdata[f"{z['NAME']}{y['Name']}"] = y['Value'] #print('reqdata = {reqdata}') req = MyEmon.senddata(reqdata) reqstr = f'http://{emon_host}{emon_url}{emon_node}&json={json.dumps(reqdata)}&apikey={emon_privateKey}' try: r = requests.get(reqstr, timeout=10) r.raise_for_status() except requests.exceptions.HTTPError as errh: print("Http Error:", errh) except requests.exceptions.ConnectionError as errc: print("Error Connecting:", errc) except requests.exceptions.Timeout as errt: print("Timeout Error:", errt) except requests.exceptions.RequestException as err: print("OOps: Something Else", err) #print (r.status_code) #log.info( f"EmonCMS send status {r.status_code}" ) if DashingEnabled: Dashlog.append( f"EmonCMS slave {z['NAME']} push status {r.status_code}" ) ui.display() else: log.info( f"EmonCMS slave {z['NAME']} push status {r.status_code}" ) #print (r.content) else: #log.info( "EmonCMS ORNO unable to send" ) if DashingEnabled: Dashlog.append( f"EmonCMS slave {z['NAME']} nothing to push") ui.display() else: log.info( f"EmonCMS slave {z['NAME']} nothing to push") if (CONF_EMONCMS and CONF_JANITZA_B23): for x, z in B23_Config.items(): if Rtuclient2.is_socket_open(): reqdata = {} #print("Slave: " + z['NAME']) MyDict = z['DATA'] #print (z['DATA']) for k, y in MyDict.items(): #DashErrors.append(y['Name']) if (y['Name'] == 'Freq'): vchart.append(50 + QuerryNb) reqdata[f"{z['NAME']}{y['Name']}"] = y['Value'] #print('reqdata = {reqdata}') req = MyEmon.senddata(reqdata) reqstr = f'http://{emon_host}{emon_url}{emon_node}&json={json.dumps(reqdata)}&apikey={emon_privateKey}' try: r = requests.get(reqstr, timeout=10) r.raise_for_status() except requests.exceptions.HTTPError as errh: print("Http Error:", errh) except requests.exceptions.ConnectionError as errc: print("Error Connecting:", errc) except requests.exceptions.Timeout as errt: print("Timeout Error:", errt) except requests.exceptions.RequestException as err: print("OOps: Something Else", err) #print (r.status_code) #log.info( f"EmonCMS send status {r.status_code}" ) if DashingEnabled: Dashlog.append( f"EmonCMS slave {z['NAME']} push status {r.status_code}" ) ui.display() else: log.info( f"EmonCMS slave {z['NAME']} push status {r.status_code}" ) #print (r.content) else: #log.info( "EmonCMS ORNO unable to send" ) if DashingEnabled: Dashlog.append( f"EmonCMS slave {z['NAME']} nothing to push") ui.display() else: log.info( f"EmonCMS slave {z['NAME']} nothing to push") if (CONF_EMONCMS and CONF_EM370): for x, z in EEM_Config.items(): if TcpClient.is_socket_open(): reqdata = {} #print("Slave: " + z['NAME']) MyDict = z['DATA'] #print (z['DATA']) for k, y in MyDict.items(): #DashErrors.append(y['Name']) reqdata[f"{z['NAME']}{y['Name']}"] = y['Value'] #print('reqdata = {reqdata}') reqstr = f'http://{emon_host}{emon_url}{emon_node}&json={json.dumps(reqdata)}&apikey={emon_privateKey}' try: r = requests.get(reqstr, timeout=10) r.raise_for_status() except requests.exceptions.HTTPError as errh: print("Http Error:", errh) except requests.exceptions.ConnectionError as errc: print("Error Connecting:", errc) except requests.exceptions.Timeout as errt: print("Timeout Error:", errt) except requests.exceptions.RequestException as err: print("OOps: Something Else", err) #print (r.status_code) #log.info( f"EmonCMS send status {r.status_code}" ) if DashingEnabled: Dashlog.append( f"EmonCMS slave {z['NAME']} push status {r.status_code}" ) ui.display() else: log.info( f"EmonCMS slave {z['NAME']} push status {r.status_code}" ) #print (r.content) else: #log.info( "EmonCMS ORNO unable to send" ) if DashingEnabled: Dashlog.append( f"EmonCMS slave {z['NAME']} nothing to push") ui.display() else: log.info( f"EmonCMS slave {z['NAME']} nothing to push") time.sleep(5) except Exception as e: print("Exception %s" % str(e))
class Drive: client = None queue = [] def __init__(self, sensors): self.sensors = sensors self.connect_modbus() def connect_modbus(self): try: self.client = ModbusClient(HOST, port=PORT) print("Opening modbus connection...") self.client.connect() print("Connection opened") except ConnectionException: print("Impossible to connect") def close_modbus(self): print("Closing modbus connection...") self.client.close() print("Connection closed") def add_vehicle(self, vehicle): self.queue.append(vehicle) def read_registers(self, sc): if self.client.is_socket_open(): t = time.time_ns() regs = self.client.read_input_registers(0,1) if isinstance(regs, ReadInputRegistersResponse) and regs: registers = regs.registers if (isinstance(registers, list) and len(registers) > 0): inputs = self.pad_inputs(self.hex_to_binary(registers[0])) inputs = self.parse_input_status(inputs) if len(inputs): for sensor in self.sensors: if sensor.input < len(inputs): updated = sensor.update_state(inputs[sensor.input], t) if updated: self.send_message(sensor, t) try: sc.enter(0.5, 1, self.read_registers, (sc, )) except: print("stop") else: print("Connection to address '" + HOST + "' could not be made") def hex_to_binary(self, hex_code): bin_code = bin(hex_code)[2:] padding = (4 - len(bin_code) % 4) % 4 return '0' * padding + bin_code def pad_inputs(self, inputs): return inputs.zfill(8) def parse_input_status(self, inputs): inputs = inputs[::-1] coils = [] for i in range(len(inputs)): coils.append(inputs[i] == '1') return coils def send_message(self, sensor, timestamp): t = time.localtime(timestamp / 1000000000) print("[" + time.strftime("%m/%d/%Y %H:%M:%S", t) + "] : Detection on channel (" + str(sensor.input) + ") : " + sensor.get_readable_state())
def testTcpClientIsSocketOpen(self): ''' Test the tcp client is_socket_open method''' client = ModbusTcpClient() self.assertFalse(client.is_socket_open())
class ModbusTCPPlcConnector: """ Instantiates the connection to a PLC via modbus/TCP. """ def __init__(self, plc_ip_address, timeout=0): """ :param plc_ip_address: The IP address of the PLC. """ self.modbus_client = ModbusTcpClient(plc_ip_address, timeout=timeout) self.modbus_client.connect() # Map of motor controls to modbus/TCP bits. self.motor_controls = { "control-motor-up": int(ModbusTCPRegisters.ControlMotorUp), "control-motor-down": int(ModbusTCPRegisters.ControlMotorDown), "control-motor-left": int(ModbusTCPRegisters.ControlMotorLeft), "control-motor-right": int(ModbusTCPRegisters.ControlMotorRight) } # The proper names for the motor controls. self.motor_names = ["motorUp", "motorDown", "motorLeft", "motorRight"] # The proper names for the sensors. self.sensor_names = [ "topSensor", "bottomSensor", "leftSensor", "rightSensor" ] def is_connected(self): """ Is the client connected? :return: True is modbus client is connected """ return self.modbus_client.is_socket_open() def get_values(self): """ This function queries the state of the sensors and motor controls. :return: A dictionary of the state of the motor controls and the state of the sensors. """ # Should the HMI not be connected to the PLC return all values as false so that while being in the disconnected # state, all sensors and motors are shown as off sensors = dict( zip(self.sensor_names, ["true"] * len(self.sensor_names))) motors = dict(zip(self.motor_names, ["true"] * len(self.motor_names))) if self.is_connected(): try: # Read the four bits of motor state from the PLC over modbus/TCP. motor_values = self.modbus_client.read_coils( int(ModbusTCPRegisters.PLCInputs), 4).bits[:4] # Read the four bits of sensors state from the PLC over modbus/TCP. sensor_values = self.modbus_client.read_discrete_inputs( int(ModbusTCPRegisters.PLCInputs)).bits[:4] # Separate the limit switches of the punching arm from the light switches limit_switches = sensor_values[:2] # Separate the light switches from the limit switches of the punching arm. light_sensors = sensor_values[2:4] # The values of the motor manual values must be inverted to fit our logic. # The values are converted to strings. corrected_motor_values = [ str(not value).lower() for value in motor_values ] # The values are converted to strings. light_sensor_values = [ str(value).lower() for value in light_sensors ] # The values are converted to strings. limit_switch_values = [ str(value).lower() for value in limit_switches ] # Join the sensor values again. joined_sensor_values = limit_switch_values + light_sensor_values # Join the proper sensor names as keys with the corresponding sensor values into a dictionary. sensors = dict(zip(self.sensor_names, joined_sensor_values)) # Join the proper motor manual names as keys with the corresponding motor manual values into a dictionary. motors = dict(zip(self.motor_names, corrected_motor_values)) except: pass # Return the dictionary that contains the current motor manual values as well as sensor values. return {**sensors, **motors} def set_order(self, count): """ Set an positive numeric value as amount of times the process is to be executed. :param count: Positive integer. :return: The PLC connectors response, so it can be use if of interest. """ if self.is_connected(): try: self.modbus_client.write_register( int(ModbusTCPRegisters.Orders), count) except: pass def set_application_state(self, state): """ Set the current application state. :param state: :return: The PLC connectors response, so it can be use if of interest. """ state_value = application.Disconnected().modbus_value if self.is_connected(): try: state_value = self.modbus_client.write_register( int(ModbusTCPRegisters.HmiApplicationState), state.modbus_value) except: pass return state_value def set_motor(self, motor, motor_state): """ Turn a motor of the process manually on or off. :param motor: Which motor is to be set. :param motor_state: On or Off. :return: The PLC connectors response, so it can be use if of interest. """ register = self.motor_controls[motor] state_value = application.Disconnected().modbus_value if self.is_connected(): try: state_value = self.modbus_client.write_register( register, int(motor_state)) except: pass return state_value def set_reset(self): """ Initiate the reset signal to get the PLC out of the emergency stop state. :return: The PLC connectors response, so it can be use if of interest. """ state_value = application.Disconnected().modbus_value if self.is_connected(): try: state_value = self.modbus_client.write_register( int(ModbusTCPRegisters.Reset), 1) except: pass return state_value def get_orders(self): """ Retrieve the amount of currently placed orders. :return: The order count. """ state_value = application.Disconnected().modbus_value if self.is_connected(): try: state_value = self.modbus_client.read_holding_registers( int(ModbusTCPRegisters.Orders), 1).registers[0] except: pass return state_value def get_process_state(self): """ Query the process state. :return: A dictionary containing the current process state. """ state_value = process.PendingState().modbus_value if self.is_connected(): try: state_value = \ self.modbus_client.read_holding_registers(int(ModbusTCPRegisters.ProcessState), 2).registers[0] except: pass return state_value def get_application_state(self): """ Query the application state. :return: A dictionary containing the current application state. """ state_value = application.Disconnected().modbus_value if self.is_connected(): try: state_value = \ self.modbus_client.read_holding_registers(int(ModbusTCPRegisters.PlcApplicationState), 1).registers[ 0] except: pass return state_value
class ModbusDevice(TerrawareDevice): def __init__(self, host, port, settings, diagnostic_mode, local_sim, spec_file_name): settings_items = settings.split(';') self._host = host self._unit = 1 # aka modbus slave number for setting in settings_items: if setting.startswith('unit='): self._unit = int(setting.split('=')[1]) self.last_update_time = None self._diagnostic_mode = diagnostic_mode framer = ModbusRtuFramer if ('rtu-over-tcp' in settings_items) else ModbusSocketFramer self._modbus_client = ModbusTcpClient(host, port=port, framer=framer) self._read_holding = ('holding' in settings_items) self._seq_infos = [] self._local_sim = local_sim # load seqeuence info with open(spec_file_name) as csvfile: lines = csv.DictReader(csvfile) for line in lines: self._seq_infos.append(line) print('created modbus device (%s:%d, unit: %d)' % (host, port, self._unit)) def reconnect(self): self._modbus_client.close() gevent.sleep(0.5) self._modbus_client.connect() def poll(self): if (not self._local_sim) and ( not self._modbus_client.is_socket_open()): self._modbus_client.connect() values = {} for seq_info in self._seq_infos: address = int(seq_info['address'], 0) value = self.read_register(address, seq_info['type'], self._unit) if value is not None: value *= float(seq_info['scale_factor']) values[seq_info['name']] = value if self._diagnostic_mode: print(' %s/%s: %.2f' % (self.server_path, seq_info['name'], value)) # if int(seq_info['send_to_server']): # self._controller.sequence.create(seq_rel_path, 'numeric', decimal_places=2) # seq_values[full_seq_name] = value if values: self.last_update_time = time.time() print('received %d of %d value(s) from %s' % (len(values), len(self._seq_infos), self.server_path)) if len(values) != len(self._seq_infos): print('received fewer values than expected; reconnecting') self.reconnect() values = { } # when this happens, we seem to get corrupt data; don't want to store that return values def read_register(self, address, register_type, unit): if self._local_sim: return random.randint(1, 100) if register_type.endswith('32'): count = 2 else: count = 1 if self._read_holding: result = self._modbus_client.read_holding_registers(address, count, unit=unit) else: result = self._modbus_client.read_input_registers(address, count, unit=unit) if not hasattr(result, 'registers'): return None if register_type == 'uint16' and len(result.registers) >= 1: return result.registers[0] elif register_type == 'sint16' and len(result.registers) >= 1: v = result.registers[0] if v > 0x7fff: # rough sign manipulation; should check/fix v = v - 0x10000 return v elif register_type == 'uint32' and len(result.registers) >= 2: return result.registers[0] * 0x10000 + result.registers[1] elif register_type == 'sint32' and len(result.registers) >= 2: v = result.registers[0] * 0x10000 + result.registers[1] if v > 0x7fffffff: # rough sign manipulation; should check/fix v = v - 0x100000000 return v else: return None
class spidar_v1(object): """ class for handling modbus connections to Spidar V1 systems parameters ---------- ip : string ip address or domain name of spidar port : int port for modbus access (default 502) unit : int slave number on bus (default 1) """ def __init__(self, ip='', port=502, unit=1, connect=False): self.ip = ip self.port = port self.unit = unit self.init_registers() self.e = '' if connect is True: self.connect() def init_registers(self): self.hr = spidar_registers() def connect(self): """ initialize self.ip, self.port, self.unit """ from pymodbus.client.sync import ModbusTcpClient as ModbusClient self.client = ModbusClient(host=self.ip, port=self.port, unit=self.unit) logger.info("Connecting to {0}".format(self.ip)) try: self.client.connect() if self.client.is_socket_open() is True: logger.info("Connected to Spidar OK") else: self.client.connect() if self.client.is_socket_open is not True: logger.error('Could Not Connect to {0}'.format(self.ip)) raise ValueError('Could Not Connect to {0}'.format(self.ip)) except Exception as e: self.e = e logger.error("Connection failed") logger.debug(self.e) def disconnect(self): logger.info("Disconnecting from {0}".format(self.ip)) try: self.client.close() logger.info("Closed connection") except Exception as e: logger.error("Error closing connection") logger.debug(e) def return_rt_data_readings(self): """ refresh all registers """ start_reg = 0 length = 105 self.read_result = self.read_single_register([start_reg, length], singles=True) self.hr.met_data['pressure']['value'] = self.read_result[1] * self.hr.met_data['pressure']['scaling'] self.hr.met_data['temperature']['value'] = self.read_result[2] * self.hr.met_data['temperature']['scaling'] if self.hr.met_data['temperature']['scaling'] > 100: self.hr.met_data['temperature']['value'] -= 656 self.hr.met_data['humidity']['value'] = self.read_result[3] * self.hr.met_data['humidity']['scaling'] self.hr.met_data['precipitation']['value'] = self.read_result[4] * self.hr.met_data['precipitation']['scaling'] n = 5 for gate in range(1, 10 + 1): self.hr.wind_data_gate[gate]['height']['value'] = self.read_result[n] self.hr.wind_data_gate[gate]['speed']['min']['value'] = self.read_result[n+1] * self.hr.wind_data_gate[gate]['speed']['min']['scaling'] self.hr.wind_data_gate[gate]['speed']['max']['value'] = self.read_result[n+2] * self.hr.wind_data_gate[gate]['speed']['max']['scaling'] self.hr.wind_data_gate[gate]['speed']['avg']['value'] = self.read_result[n+3] * self.hr.wind_data_gate[gate]['speed']['avg']['scaling'] self.hr.wind_data_gate[gate]['speed']['sd']['value'] = self.read_result[n+4] * self.hr.wind_data_gate[gate]['speed']['sd']['scaling'] self.hr.wind_data_gate[gate]['dir']['min']['value'] = self.read_result[n+5] * self.hr.wind_data_gate[gate]['dir']['min']['scaling'] self.hr.wind_data_gate[gate]['dir']['max']['value'] = self.read_result[n+6] * self.hr.wind_data_gate[gate]['dir']['max']['scaling'] self.hr.wind_data_gate[gate]['dir']['avg']['value'] = self.read_result[n+7] * self.hr.wind_data_gate[gate]['dir']['avg']['scaling'] self.hr.wind_data_gate[gate]['dir']['sd']['value'] = self.read_result[n+8] * self.hr.wind_data_gate[gate]['dir']['sd']['scaling'] self.hr.wind_data_gate[gate]['quality']['value'] = self.read_result[n+9] n += 10 def read_single_register(self, register, singles=False): """ wrapper for pymodbus, returns single value """ try: rr = self.client.read_holding_registers(register[0], register[1], unit=1) self.rr = rr if register[1] > 2 and singles is True: flo = rr.registers else: flo = rr.registers[0] return flo except Exception as e: self.e = e self.rr = rr return 9999
class ipackaccess(object): """ class for handling modbus connections to iPackACCESS parameters ---------- ip : string ip address or domain name of iPack port : int port for modbus access (default 502) unit : int slave number on bus (default 1) logger_model : int finished good number of connected Symphonie (default 8206) """ def __init__(self, ip='', port=502, logger_model=8206, unit=1, connect=True): self.ip = ip self.logger_model = logger_model self.port = port self.unit = unit self.init_registers() self.e = '' if connect is True: self.connect() def init_registers(self): """ set registers for all available data manually each is a list 0 = register address 1 = number of registers """ self.hr = ipackaccess_registers() def connect(self): """ initialize self.ip, self.port, self.unit """ from pymodbus.client.sync import ModbusTcpClient as ModbusClient self.client = ModbusClient(host=self.ip, port=self.port, unit=self.unit) logger.info("Connecting to {0}".format(self.ip)) try: self.client.connect() if self.client.is_socket_open() is True: logger.info("Connected OK") else: self.client.connect() if self.client.is_socket_open is not True: logger.error('Could Not Connect to {0}'.format(self.ip)) raise ValueError('Could Not Connect to {0}'.format( self.ip)) except Exception as e: self.e = e logger.error("Connection failed") logger.debug(self.e) def disconnect(self): logger.info("Disconnecting from {0}".format(self.ip)) try: self.client.close() logger.info("Closed connection") except Exception as e: logger.error("Error closing connection") logger.debug(e) def poll(self, interval=4, reconnect=True, stat=True, rt=True, serial=False, diag=False, config=False, db='', save_to_db=False, echo=False): """ regularly poll registers parameters : (default value) interval : seconds to wait between polls (4) reconnect : automatically reconnect on failure (True) stat : poll statistical registers (True) rt : poll real time registers (True) serial : poll serial registers (False) diag : poll diagnostic registers (False) config : poll config registers (False) db : sqlite3 db to save to ('') save_to_db : save to db? (False) echo : print some data to console (False) returns register values as individual and packages arrays """ from time import time, sleep i = 0 # set up database connection if save_to_db is True while True: poll_time = time() i += 1 if self.e != '': if reconnect is True: self.connect() else: return "Disconnected from {0}, reconnect disabled".format( self.ip) self.e = '' if stat is True: self.return_stat_readings() if rt is True: self.return_rt_data_readings() if serial is True: self.return_rt_serial_readings() if diag is True: self.return_diag_readings() if config is True: self.return_config() if save_to_db is True: # do the db things pass if echo is True: if rt is True: print("{0}\t{1}\t{2}\t{3}".format(i, self.date_time, self.rt_ch1, self.rt_ch13)) else: print("Poll # {0}".format(i)) while time() < poll_time + interval: sleep(0.01) def return_diag_readings(self): """ data from diagnostic registers returns ------- dict see ipackaccess.registers for more info """ for i in self.hr.diag: try: self.hr.diag[i]['value'] = self.read_single_register( self.hr.diag[i]['reg']) except KeyError: pass self.hr.diag['datetime'] = { 'value': f"{str(self.hr.diag['year']['value'])}-{str(self.hr.diag['month']['value']).zfill(2)}-{str(self.hr.diag['day']['value']).zfill(2)} {str(self.hr.diag['hour']['value']).zfill(2)}:{str(self.hr.diag['minute']['value']).zfill(2)}:{str(self.hr.diag['second']['value']).zfill(2)}" } def return_system_readings(self): """ logger and ipack system information returns ------- dict see ipackaccess.registers for more info """ start_reg = 0 length = 21 self.read_result = self.read_single_register([start_reg, length], singles=True) self.hr.logger['signed_num_ex']['value'] = combine_u32_registers( self.read_result[0:2]) self.hr.logger['unsigned_num_ex']['value'] = combine_u32_registers( self.read_result[2:4]) self.hr.logger['unsigned_16_num_ex']['value'] = self.read_result[4] self.hr.logger['site_number']['value'] = combine_u32_registers( self.read_result[5:7]) self.hr.logger['sn']['value'] = combine_u32_registers( self.read_result[7:9]) self.hr.logger['model']['value'] = self.read_result[9] self.hr.logger['hw_ver']['value'] = combine_u32_registers( self.read_result[10:12]) self.hr.logger['fw_ver']['value'] = combine_u32_registers( self.read_result[12:14]) self.hr.ipack['sn']['value'] = combine_u32_registers( self.read_result[14:16]) self.hr.ipack['model']['value'] = self.read_result[16] self.hr.ipack['hw_ver']['value'] = combine_u32_registers( self.read_result[17:19]) self.hr.ipack['fw_ver']['value'] = combine_u32_registers( self.read_result[19:21]) def return_all_channel_data(self): """ poll statistical registers """ for ch in self.hr.data_ch: for i in self.hr.data_ch[ch]: self.hr.data_ch[ch][i]['value'] = self.read_single_register( self.hr.data_ch[ch][i]['reg']) def return_channel_data(self, channel): """ poll statistical registers parameters ---------- channel : int channel number to poll returns ------- dict populates value of channel dict example ------- >>> poller.return_channel_data(1) >>> poller.data_ch[1] """ for i in self.hr.data_ch[channel]: self.hr.data_ch[channel][i]['value'] = self.read_single_register( self.hr.data_ch[channel][i]['reg']) def return_time(self): """ returns time from config registers """ self.read_result = self.read_single_register([1500, 6], singles=True) self.hr.samp_time['year']['value'] = self.read_result[0] self.hr.samp_time['month']['value'] = self.read_result[1] self.hr.samp_time['day']['value'] = self.read_result[2] self.hr.samp_time['hour']['value'] = self.read_result[3] self.hr.samp_time['minute']['value'] = self.read_result[4] self.hr.samp_time['second']['value'] = self.read_result[5] self.hr.samp_time['datetime'] = { 'value': f"{str(self.hr.samp_time['year']['value'])}-{str(self.hr.samp_time['month']['value']).zfill(2)}-{str(self.hr.samp_time['day']['value']).zfill(2)} {str(self.hr.samp_time['hour']['value']).zfill(2)}:{str(self.hr.samp_time['minute']['value']).zfill(2)}:{str(self.hr.samp_time['second']['value']).zfill(2)}" } def return_rt_data_readings(self): """ refresh all 'samp' data values """ self.return_time() start_reg = 1506 length = 100 self.read_result = self.read_single_register([start_reg, length], singles=True) ch = 1 for i in range(0, len(self.read_result), 2): self.hr.data_ch[ch]['samp']['value'] = combine_registers( self.read_result[i:i + 2]) ch += 1 start_reg = 3500 length = 20 self.read_result = self.read_single_register([start_reg, length], singles=True) ch = 100 for i in range(0, len(self.read_result), 2): self.hr.data_ch[ch]['samp']['value'] = combine_registers( self.read_result[i:i + 2]) ch += 1 def read_registers(self, list_of_registers_to_read): """ returns array of converted values """ return_values = [] for reg in list_of_registers_to_read: try: return_values.append(self.read_single_register(reg)) except: return_values.append(9999) return return_values def read_single_register(self, register, singles=False): """ wrapper for pymodbus, returns single value """ try: rr = self.client.read_holding_registers(register[0], register[1], unit=1) self.rr = rr if register[1] == 2 and singles is False: flo = combine_registers(rr.registers) elif register[1] > 2 and singles is False: flo = [] for i in range(0, len(rr.registers), 2): temp = combine_registers( [rr.registers[i], rr.registers[i + 1]]) flo.append(temp) elif register[1] > 2 and singles is True: flo = rr.registers else: flo = rr.registers[0] return flo except Exception as e: self.e = e self.rr = rr return 9999 def monitor(self): """ """ pass