def setValues(self, address, values): ''' Sets the requested values of the datastore :param address: The starting address :param values: The new values to be set ''' if not isinstance(values, list): values = [values] start = address - self.address self.values[start:start + len(values)] = values if start <= 550 < start + len(values): if self.values[500] != values[550-start]: logInfo.debug("ModbusMySequentialDataBlock.setValues updating 500({0}) with new value {1}".format(self.values[500],values[550-start])) self.values[500] = values[550-start] if start <= 552 < start + len(values): global g_Time global s_Time decoder = BinaryPayloadDecoder.fromRegisters(self.values[502:503],endian=Endian.Little) bits_502 = decoder.decode_bits() bits_502 += decoder.decode_bits() decoder = BinaryPayloadDecoder.fromRegisters(self.values[506:507],endian=Endian.Little) bits_506 = decoder.decode_bits() bits_506 += decoder.decode_bits() decoder = BinaryPayloadDecoder.fromRegisters(values[552-start:553-start],endian=Endian.Little) bits_552 = decoder.decode_bits() bits_552 += decoder.decode_bits() logInfo.debug("ModbusMySequentialDataBlock.setValues updating 552({0}) {1}".format(values[552-start], bits_552)) if bits_552[2]: print "ModbusMySequentialDataBlock.setValues start iniettore da remoto" logInfo.debug("ModbusMySequentialDataBlock.setValues start iniettore da remoto") g_Time = 0 bits_502[7] = 1 # START INIETTORE self.hndlr.pumpStarted = True bits_506[2] = 1 bits_506[3] = 0 bits_552[2] = 0 bits_builder = BinaryPayloadBuilder(endian=Endian.Little) bits_builder.add_bits(bits_502) bits_builder.add_bits(bits_506) bits_builder.add_bits(bits_552) bits_reg = bits_builder.to_registers() self.values[502:503]=[bits_reg[0]] self.values[506:507]=[bits_reg[1]] self.values[552:553]=[bits_reg[2]] if bits_552[3]: print "ModbusMySequentialDataBlock.setValues stop iniettore da remoto" logInfo.debug("ModbusMySequentialDataBlock.setValues stop iniettore da remoto") bits_502[7] = 0 # STOP INIETTORE bits_506[2] = 0 self.hndlr.pumpStarted = False bits_506[3] = 1 bits_552[3] = 0 bits_builder = BinaryPayloadBuilder(endian=Endian.Little) bits_builder.add_bits(bits_502) bits_builder.add_bits(bits_506) bits_builder.add_bits(bits_552) bits_reg=bits_builder.to_registers() self.values[502:503]=[bits_reg[0]] self.values[506:507]=[bits_reg[1]] self.values[552:553]=[bits_reg[2]]
def checkPump(self,client_p): rr = client_p.read_holding_registers(500,100,unit=1) # conversione in bit array da 552 decoder = BinaryPayloadDecoder.fromRegisters(rr.registers[52:53],endian=Endian.Little) bits_552 = decoder.decode_bits() bits_552 += decoder.decode_bits() bits_552[10] = True b_builder = BinaryPayloadBuilder(endian=Endian.Little) b_builder.add_bits(bits_552) reg_552 = b_builder.to_registers() rr.registers[52:53] = reg_552 for it in range(10): self.p_count += 1 rr.registers[50] = self.p_count rq = client_p.write_registers(500, rr.registers,unit=1) if rq.function_code < 0x80: rr_p = client_p.read_holding_registers(500,100,unit=1) if len(rr_p.registers)==100 and rr_p.registers[0]==self.p_count: decoder = BinaryPayloadDecoder.fromRegisters(rr_p.registers[2:7],endian=Endian.Little) # 502 bits_502 = decoder.decode_bits() bits_502 += decoder.decode_bits() # 503 bits_503 = decoder.decode_bits() bits_503 += decoder.decode_bits() # 504 bits_504 = decoder.decode_bits() bits_504 += decoder.decode_bits() # 505 bits_505 = decoder.decode_bits() bits_505 += decoder.decode_bits() # 506 bits_506 = decoder.decode_bits() bits_506 += decoder.decode_bits() if bits_502[7]: builder.get_object("switchPumpStatus").set_active(True) else: builder.get_object("switchPumpStatus").set_active(False) if bits_502[4] == False and bits_502[10] == True: builder.get_object("switchPumpStatus").set_sensitive(True) else: builder.get_object("switchPumpStatus").set_sensitive(False) else: print "errore checkPump %d" % self.p_count bits_552[10] = False print str(bits_552) b_builder = BinaryPayloadBuilder(endian=Endian.Little) b_builder.add_bits(bits_552) reg_552_2 = b_builder.to_registers() rr.registers[52:53] = reg_552_2 rq = client_p.write_registers(500, rr.registers,unit=1) if rq.function_code < 0x80: pass else: print "checkPump terminato con scrittura KO"
def on_switchPumpStatus_state_set(self, switch,gparam): self.ret_p=self.client_p.connect() rr = self.client_p.read_holding_registers(500,100,unit=1) # conversione in bit array da 552 decoder = BinaryPayloadDecoder.fromRegisters(rr.registers[52:53],endian=Endian.Little) bits_552 = decoder.decode_bits() bits_552 += decoder.decode_bits() decoder = BinaryPayloadDecoder.fromRegisters(rr.registers[2:7],endian=Endian.Little) # 502 bits_502 = decoder.decode_bits() bits_502 += decoder.decode_bits() # 503 bits_503 = decoder.decode_bits() bits_503 += decoder.decode_bits() # 504 bits_504 = decoder.decode_bits() bits_504 += decoder.decode_bits() # 505 bits_505 = decoder.decode_bits() bits_505 += decoder.decode_bits() # 506 bits_506 = decoder.decode_bits() bits_506 += decoder.decode_bits() bSendCommand = False if switch.get_active(): # %MW552:X2 START INIET. DA REMOTO bSendCommand = not bits_502[7] bits_552[2] = True bits_552[3] = False bits_552[14] = True else: # %MW552:X2 STOP INIET. DA REMOTO bSendCommand = bits_502[7] bits_552[2] = False bits_552[3] = True bits_552[14] = False builder = BinaryPayloadBuilder(endian=Endian.Little) # builder.add_bits(bits_502) builder.add_bits(bits_552) reg_552 = builder.to_registers() rr.registers[52:53] = reg_552 self.p_count += 1 rr.registers[50] = self.p_count if bSendCommand: self.client_p.write_registers(500, rr.registers,unit=1) if bits_552[2]: bits_552[2] = False builder = BinaryPayloadBuilder(endian=Endian.Little) # builder.add_bits(bits_502) builder.add_bits(bits_552) reg_552 = builder.to_registers() self.client_p.write_register(552, reg_552[0]) print "pulsante rilasciato" self.client_p.close()
def testPayloadDecoderRegisterFactory(self): """ Test the payload decoder reset functionality """ payload = [1, 2, 3, 4] decoder = BinaryPayloadDecoder.fromRegisters(payload, endian=Endian.Little) encoded = "\x00\x01\x00\x02\x00\x03\x00\x04" self.assertEqual(encoded, decoder.decode_string(8)) decoder = BinaryPayloadDecoder.fromRegisters(payload, endian=Endian.Big) encoded = "\x00\x01\x00\x02\x00\x03\x00\x04" self.assertEqual(encoded, decoder.decode_string(8)) self.assertRaises(ParameterException, lambda: BinaryPayloadDecoder.fromRegisters("abcd"))
def on_btnSetPump_clicked(self,button): rr_p = self.client_p.read_holding_registers(500,100,unit=1) decoder = BinaryPayloadDecoder.fromRegisters(rr_p.registers[52:53],endian=Endian.Little) bits_552 = decoder.decode_bits() bits_552 += decoder.decode_bits() self.pmax = self.adjustPMax.get_value() _qmax = self.adjustQMax.get_value() self.p_count += 1 rr_p.registers[50] = self.p_count rr_p.registers[60] = int(self.pmax) if self.chkPAna.get_active(): self.qmax = getFlowRateAsVolts(_qmax) v = getVoltsFromFlowRate(self.qmax) print "_qmax => {0} self.qmax => {1} c => {2}".format(_qmax, self.qmax, v) rr_p.registers[64] = self.qmax rr_p.registers[62] = int(_qmax/litCiclo) bits_552[12] = True else: self.qmax = _qmax rr_p.registers[62] = int(self.qmax) rr_p.registers[64] = cicli_volt[int(self.qmax)] bits_552[12] = False b_builder = BinaryPayloadBuilder(endian=Endian.Little) # builder.add_bits(bits_502) b_builder.add_bits(bits_552) reg_552 = b_builder.to_registers() rr_p.registers[52:53] = reg_552 rr_p = self.client_p.write_registers(500,rr_p.registers,unit=1)
def decode(self, formatters, byte_order='big', word_order='big'): """ Decode the register response to known formatters. :param formatters: int8/16/32/64, uint8/16/32/64, float32/64 :param byte_order: little/big :param word_order: little/big :return: Decoded Value """ # Read Holding Registers (3) # Read Input Registers (4) # Read Write Registers (23) if not isinstance(formatters, (list, tuple)): formatters = [formatters] if self.function_code not in [3, 4, 23]: print_formatted_text( HTML("<red>Decoder works only for registers!!</red>")) return byte_order = (Endian.Little if byte_order.strip().lower() == "little" else Endian.Big) word_order = (Endian.Little if word_order.strip().lower() == "little" else Endian.Big) decoder = BinaryPayloadDecoder.fromRegisters(self.data.get('registers'), byteorder=byte_order, wordorder=word_order) for formatter in formatters: formatter = FORMATTERS.get(formatter) if not formatter: print_formatted_text( HTML("<red>Invalid Formatter - {}" "!!</red>".format(formatter))) return decoded = getattr(decoder, formatter)() self.print_result(decoded)
def checkPump(self,client_p): self.p_count += 1 rq = client_p.write_register(550,self.p_count,unit=1) if rq.function_code < 0x80: rr_p = client_p.read_holding_registers(500,100,unit=1) if len(rr_p.registers)==100 and rr_p.registers[0]==self.p_count: decoder = BinaryPayloadDecoder.fromRegisters(rr_p.registers[2:7],endian=Endian.Little) # 502 bits_502 = decoder.decode_bits() bits_502 += decoder.decode_bits() # 503 bits_503 = decoder.decode_bits() bits_503 += decoder.decode_bits() # 504 bits_504 = decoder.decode_bits() bits_504 += decoder.decode_bits() # 505 bits_505 = decoder.decode_bits() bits_505 += decoder.decode_bits() # 506 bits_506 = decoder.decode_bits() bits_506 += decoder.decode_bits() if bits_502[7]: builder.get_object("switchPumpStatus").set_active(True) else: builder.get_object("switchPumpStatus").set_active(False) if bits_502[4] == False and bits_502[10] == True: builder.get_object("switchPumpStatus").set_sensitive(True) else: builder.get_object("switchPumpStatus").set_sensitive(False) self.setPumpFlowAndPressure()
def read(self): count = COUNTS.get(self.data_type) if not count: raise ValueError('Unsupported data type {}'.format(self.data_type)) _logger.debug('read register: {}, count: {}, slave: {}, function: {}'.format(self.register_addr, count, self.slave_id, self.function_code)) if self.function_code == 3: result = self.client.read_holding_registers(self.register_addr, count, unit=self.slave_id) elif self.function_code == 4: result = self.client.read_input_registers(self.register_addr, count, unit=self.slave_id) else: raise ValueError('Unsupported function code {}'.format(self.function_code)) if result is None: raise IOError('No modbus reponse') if isinstance(result, ExceptionResponse): raise IOError(str(result)) d = BinaryPayloadDecoder.fromRegisters(result.registers, endian=self.endian) if self.data_type == '16bit_int': value = d.decode_16bit_int() elif self.data_type == '16bit_uint': value = d.decode_16bit_uint() elif self.data_type == '32bit_int': value = d.decode_32bit_int() elif self.data_type == '32bit_uint': value = d.decode_32bit_uint() elif self.data_type == '32bit_float': value = d.decode_32bit_float() elif self.data_type == '64bit_float': value = d.decode_64bit_float() else: raise ValueError('Unsupported data type {}'.format(self.data_type)) return value
def requestLoop(self): meterreadings = {} try: if (self.client.connect() is False): print('not connected') self.client = self.client.connect() print('trying to connecto to ' + str(self.pfcIp)) #============================================================================== # Read current timestamp from PFC #============================================================================== timestampSys = round(time.time() * 1000) result2 = self.client.read_holding_registers(4, 4) decoder = BinaryPayloadDecoder.fromRegisters(result2.registers, endian=Endian.Little) timestampPFC = decoder.decode_64bit_int() #============================================================================== # Reads the values from modbus registers clamp by clamp # and buffers the results in meterreadings{} # It is not possible to read all registers in one request because of the limitation of the Modbus-Message size to 255kb # When the results of all clamps are buffered, they are published #============================================================================== result = self.client.read_coils(16, 4) meterreadings = {'shStarr': result.bits[0], 'sh4QS': result.bits[1], 'speicherSh': result.bits[2], 'speicher4QS': result.bits[3]} #============================================================================== # standardize both TimestampPFC and TimestampSYS precision to be millisecond #============================================================================== meterreadingsToCompare = {} meterreadingsToCompare = meterreadings self.oldState.pop('TimestampPFC', None) self.oldState.pop('TimestampSYS', None) self.elapsedTime = self.elapsedTime + 1 if (self.oldState != meterreadingsToCompare) or (self.elapsedTime >= 60): self.elapsedTime = 0 self.oldState = meterreadingsToCompare meterreadings['TimestampPFC'] = str(timestampPFC)[0:13] meterreadings['TimestampSYS'] = round(time.time() * 1000) self.publish(u'eshl.wago.v1.readout.misc.4qs', json.dumps(meterreadings, sort_keys=True)) self.publish(u'eshl.wago.v2.readout.misc.4qs', meterreadings) #============================================================================== # If there is no connection to the pfc-modbus slave or no connection to the pfc at all # the blankDataSet is published #============================================================================== #============================================================================== # except ConnectionException as connErr: # for x in range(0, len(self.clamps)): # meterreadings[self.clamps[x]] = json.dumps(self.blankDataSetGen(), sort_keys=True) # self.publish(u'org.eshl.wago.readout.meter.494', json.dumps(meterreadings,sort_keys=True)) # print(str(connErr)) #============================================================================== except Exception as err: print("error: {}".format(err), file=sys.stdout) traceback.print_exc(file=sys.stdout)
def start_iniettore(p_client): b_ok = False n_numReg = 5 # leggo 502 a 506 per verificare bit di controllo p_rr = p_client.read_holding_registers(502,n_numReg,unit=1) decoder = BinaryPayloadDecoder.fromRegisters(p_rr.registers,endian=Endian.Little) reg={} regnum = 502 for i in range(n_numReg): bits_50x = decoder.decode_bits() bits_50x += decoder.decode_bits() reg[regnum+i] = bits_50x if reg[502][4]: log.error("Pompa in allarme") else: if reg[502][6]: log.debug("Pompa olio on") if reg[502][7]: log.error("Ciclo Iniettore ON") else: log.debug("Ciclo Iniettore OFF") # %MW502:X10 Macchina pronta per comando remoto b_ok = reg[502][10] if b_ok: log.debug("Macchina pronta per comando remoto") else: log.error(u"Macchina non è pronta per comando remoto") b_ok &= check_alarms(reg[504]) if b_ok: log.debug("...nessun allarme rilevato") p_comandi_remoto = p_client.read_holding_registers(560,3,unit=1) remote_reg = [0]*3 remote_reg = p_comandi_remoto.registers log.debug("COMANDO BAR DA REMOTO IMPOSTATO a %d" % remote_reg[0]) # %MW560 16 bit 0-100 bar COMANDO BAR DA REMOTO log.debug("COMANDO NUMERO CICLI MINUTO DA REMOTO a %d" % remote_reg[2]) # %MW562 16 bit < 40 COMANDO NUMERO CICLI MINUTO DA REMOTO remote_reg[0] = DEFAULT_BAR remote_reg[2] = DEFAULT_CICLI rq = p_client.write_registers(560, remote_reg,unit=1) b_ok = rq.function_code < 0x80 # test that we are not an error if b_ok: bits_552 = [False]*16 bits_552[2] = True # %MW552:X2 START INIET. DA REMOTO builder = BinaryPayloadBuilder(endian=Endian.Little) builder.add_bits(bits_552) reg_552 = builder.to_registers() rq = p_client.write_register(552, reg_552[0],unit=1) b_ok = rq.function_code < 0x80 # test that we are not an error else: log.error("start_iniettore SET Comandi BAR e CICLI REMOTO fallito!") else: log.debug("...verificare allarmi rilevati") else: log.error("Pompa olio OFF") return b_ok
def __decodeData(self, data): """Decode MODBUS data to float Function decodes a list of MODBUS 32bit float data passed to it into its respective list of floats. Arguments: :param data: Data to be decoded :type data: list """ returnData = [0]*(len(data)/2) decoder = BinaryPayloadDecoder.fromRegisters(data, endian=Endian.Little) for i in range(0,len(data)/2): returnData[i] = round(decoder.decode_32bit_float(),2) return returnData
def readChannels(): # voltage phase 1 to neutral print "New measurement readings: ", int(time.time()) listValues = list() for c in listChannels: handle = client.read_holding_registers(c['register'],c['words'],unit=c['unit']) # print c['description'], ":" if c['words'] > 1: decoder = BinaryPayloadDecoder.fromRegisters(handle.registers, endian=Endian.Big) value = decoder.decode_32bit_int()/float(c['factor']) else: value = handle.registers[0]/float(c['factor']) #print c['description'], ":", str(value) listValues.append(value) for i, channel in enumerate(listChannels): # print channel['description'],":", channel['uuid'], int(time.time()*1000), listValues[i] # Here fire values into VZ middleware addValue(channel['uuid'], int(time.time()*1000), listValues[i])
def read_register(client, register): logger.info("---- Read register " + str(register)) try: rr = client.read_holding_registers(register, count=2, unit=1) logger.info("Register value are " + str(rr.registers)) # build register reg=[0, 0] reg[0] = rr.registers[0] reg[1] = rr.registers[1] # decode register decoder = BinaryPayloadDecoder.fromRegisters(reg, endian=Endian.Big) decoded = decoder.decode_32bit_float() logger.info("Register decoded value are " + str(decoded)) return decoded except ConnectionException: raise ConnectionExceptionRest("")
def __get_mc602(self, address): try: log.debug("Attempting to read from MC602") registers = self.read_input_registers( \ address = address, count = 2, unit = 65) log.debug("Successfully read registers from MC602") log.debug("Attempting to convert registers") # registers are encoded in "big endian", however the attribute # to convert to a float value in pymodbus therefore is Little # (and not Big as expected)! Details in class Constants/Endian decoder = BinaryPayloadDecoder.fromRegisters(registers, \ endian = Endian.Little) float_value = decoder.decode_32bit_float() log.debug("Successfully converted registers to float_value") return float_value except: log.error("Failed to read from MC602")
def on_btnSetPump_clicked(self,button): rr_p = self.client_p.read_holding_registers(500,100,unit=1) decoder = BinaryPayloadDecoder.fromRegisters(rr_p.registers[52:53],endian=Endian.Little) bits_552 = decoder.decode_bits() bits_552 += decoder.decode_bits() self.pmax = self.adjustPMax.get_value() self.qmax = self.adjustQMax.get_value() self.p_count += 1 rr_p.registers[50] = self.p_count rr_p.registers[60] = int(self.pmax) if self.chkPAna.get_active(): rr_p.registers[64] = int(self.qmax) # TODO rr_p.registers[62] = equivalente mappato bits_552[12] = True else: rr_p.registers[62] = int(self.qmax) rr_p.registers[64] = cicli_volt[int(self.qmax)] bits_552[12] = False b_builder = BinaryPayloadBuilder(endian=Endian.Little) # builder.add_bits(bits_502) b_builder.add_bits(bits_552) reg_552 = b_builder.to_registers() rr_p.registers[52:53] = reg_552 rr_p = self.client_p.write_registers(500,rr_p.registers,unit=1)
def read(self): for x in self.queue[self.current]: try: t = util.now() self.res = self.client.read_holding_registers(3900, 100, unit=x.Id) if (self.res == None): continue for y in xrange(0, len(self.parameters[x.Model])): read_reg = self.reading_registers[x.Model][y] if (read_reg == 92 or read_reg == 98): decoder = BinaryPayloadDecoder.fromRegisters(self.res.registers[read_reg:read_reg + 2], endian=Endian.Big) value = decoder.decode_32bit_uint() else: value = convert((self.res.registers[read_reg + 1] << 16) + self.res.registers[read_reg]) self.add('/Meter' + str(x.Id) + '/' + self.parameters[x.Model][y], t, value) except SerialException: print "Serial Exception encountered. Looking for new tty port." self.client = self.CONNECT_TO_METER() pop_count = len(self.queue[self.current]) for x in xrange(0, pop_count): popped = self.queue[self.current].pop(0) next_index = (self.current + popped.Rate) % self.SLOWEST_POSSIBLE_RATE while (len(self.queue[next_index]) == 2): next_index += 1 if (next_index == self.SLOWEST_POSSIBLE_RATE): next_index = 0 self.queue[next_index].append(popped) self.current = (self.current + 1) % self.SLOWEST_POSSIBLE_RATE
def on_btnShow_clicked(self,button): # show dlgRegistries self.lstDialog = builder.get_object("dlgRegistries") self.liststore = builder.get_object("liststore1") if not self.ret_p: self.ret_p = self.client_p.connect() if self.ret_p: self.liststore.clear() rr = self.client_p.read_holding_registers(500,100,unit=1) for idx in [0,2,4,6,12,13,14,15,16,20,50,52,60,62]: if idx in (2,4,6,52): decoder = BinaryPayloadDecoder.fromRegisters(rr.registers[idx:idx+1],endian=Endian.Little) bits = decoder.decode_bits() bits += decoder.decode_bits() for ib, b in enumerate(bits): if b: sCode = "%MW5{0:02d}:X{1}".format(idx,ib) self.liststore.append([sCode,reg_descr[sCode], str( b ) ]) else: sCode = "%MW5{0:02d}".format(idx) self.liststore.append([sCode, reg_descr[sCode], str( rr.registers[idx]) ]) response = self.lstDialog.run() self.lstDialog.hide()
def get_pv_data(config): host = config.get('inv_host') port = config.get('inv_port', 502) modbusid = config.get('inv_modbus_id', 3) manufacturer = config.get('inv_manufacturer', 'Default') # registers = [ ['30057', 'U32', 'RAW', 'serial', ''], ['30201','U32','ENUM','Status',''], ['30051','U32','ENUM','DeviceClass',''], ['30053','U32','ENUM','DeviceID',''], ['40631', 'STR32', 'UTF8', 'Device Name', ''], ['30775', 'S32', 'FIX0', 'AC Power', 'W'], ['30813', 'S32', 'FIX0', 'AC apparent power', 'VA'], ['30977', 'S32', 'FIX3', 'AC current', 'A'], ['30783', 'S32', 'FIX2', 'AC voltage', 'V'], ['30803', 'U32', 'FIX2', 'grid frequency', 'Hz'], ['30773', 'S32', 'FIX0', 'DC power', 'W'], ['30771', 'S32', 'FIX2', 'DC input voltage', 'V'], ['30777', 'S32', 'FIX0', 'Power L1', 'W'], ['30779', 'S32', 'FIX0', 'Power L2', 'W'], ['30781', 'S32', 'FIX0', 'Power L3', 'W'], ['30953', 'S32', 'FIX1', u'device temperature', u'\xb0C'], ['30517', 'U64', 'FIX3', 'daily yield', 'kWh'], ['30513', 'U64', 'FIX3', 'total yield', 'kWh'], ['30521', 'U64', 'FIX0', 'operation time', 's'], ['30525', 'U64', 'FIX0', 'feed-in time', 's'], ['30975', 'S32', 'FIX2', 'intermediate voltage', 'V'], ['30225', 'S32', 'FIX0', 'Isolation resistance', u'\u03a9'] ] registerconfig = config.get('registers') registers = None if registerconfig: registers = eval(registerconfig) if None in (registers, host, port, modbusid): print("Modbus: missing modbus parameter inv_") return None client = ModbusClient(host=host, port=port) try: client.connect() except: print('Modbus Connection Error', 'could not connect to target. Check your settings, please.') return None data = {} ## empty data store for current values for myreg in registers: ## if the connection is somehow not possible (e.g. target not responding) # show a error message instead of excepting and stopping try: addr = int(myreg[0]) dt = myreg[1] received = client.read_input_registers(address=addr, count=modbusdatatype[dt], unit=int(modbusid)) except Exception as e: thisdate = str(datetime.datetime.now()).partition('.')[0] thiserrormessage = thisdate + 'Modbus: Connection not possible. Check settings or connection.' print(thiserrormessage) print(traceback.format_exc()) return None ## prevent further execution of this function name = myreg[3] message = BinaryPayloadDecoder.fromRegisters(received.registers, byteorder=Endian.Big, wordorder=Endian.Big) ## provide the correct result depending on the defined datatype if myreg[1] == 'S32': interpreted = message.decode_32bit_int() elif myreg[1] == 'U32': interpreted = message.decode_32bit_uint() elif myreg[1] == 'U64': interpreted = message.decode_64bit_uint() elif myreg[1] == 'STR32': interpreted = message.decode_string(32) elif myreg[1] == 'S16': interpreted = message.decode_16bit_int() elif myreg[1] == 'U16': interpreted = message.decode_16bit_uint() else: ## if no data type is defined do raw interpretation of the delivered data interpreted = message.decode_16bit_uint() ## check for "None" data before doing anything else if ((interpreted == MIN_SIGNED) or (interpreted == MAX_UNSIGNED)): value = None else: ## put the data with correct formatting into the data table if myreg[2] == 'FIX3': value = float(interpreted) / 1000 elif myreg[2] == 'FIX2': value = float(interpreted) / 100 elif myreg[2] == 'FIX1': value = float(interpreted) / 10 elif myreg[2] == 'UTF8': value = str(interpreted, 'UTF-8').rstrip("\x00") elif myreg[2] == 'ENUM': e = pvenums.get(name, {}) value = e.get(interpreted, str(interpreted)) else: value = interpreted data[name] = value client.close() return data
def __get_mc602(self, address): registers = self.read_input_registers(address = address, count = 2, unit = 65) decoder = BinaryPayloadDecoder.fromRegisters(registers, endian = Endian.Little) float_value = decoder.decode_32bit_float() return float_value
modbus = ModbusClient(method='rtu', port='/dev/ttyUSB0', baudrate=2400, timeout=1) modbus.connect() with open('log.csv','w') as f: thewriter=csv.writer(f) thewriter.writerow(['year,month,date,hr:min:sec:ms,frequency ','voltage ','current ']) while True: v = modbus.read_input_registers(0x00,2 , unit=1) f = modbus.read_input_registers(0x46,2 , unit=1) i = modbus.read_input_registers(0x06,2 , unit=1) now= datetime.datetime.now() print(now.strftime("-------%Y-%m-%d %H:%M:%S----------")) decoder = BinaryPayloadDecoder.fromRegisters(v.registers,byteorder=Endian.Big) v1=decoder.decode_32bit_float() print ("voltage is ", v1, "V") decoder = BinaryPayloadDecoder.fromRegisters(f.registers,byteorder=Endian.Big) f1=decoder.decode_32bit_float() print ("frequency is ",f1, "hz") decoder = BinaryPayloadDecoder.fromRegisters(i.registers,byteorder=Endian.Big) i1=decoder.decode_32bit_float() print ("current is ", i1, "I") print"" thewriter.writerow([now,f1,v1,i1])
def adquisicion(): while True: # tic = tm.default_timer() #Inicializacion de variables antes de entrar al loop y obtener los promedios IpanelT = 0 VpanelT = 0 VpanelV = 0 IcargaT = 0 VcargaT = 0 IbatT = 0 VbatT = 0 Vsensor = 0 for j in range(501): ##potencia del panel solar Ipanel = mcp.read_adc(7) ## Corriente del panel solar Ipanel = ((Ipanel) * (5.15 / 1023)) #IpanelV=IpanelV+Ipanel Ipanel = Ipanel + 0.03 #Ipanel=(-25.3+10*Ipanel)-0.2 Ipanel = (-2.6 + Ipanel) * (1 / 0.09693) if Ipanel > 0: IpanelT = IpanelT + Ipanel ## Suma de corriente del panel solar sin promediar Vpanel = mcp.read_adc(4) Vpanel = Vpanel * (5.15 / 1023) VpanelV = VpanelV + Vpanel #Vpanel=Vpanel*(37.5/7.5) ## voltaje del panel solar Vpanel = 4.093 * Vpanel + 3.4444 VpanelT = VpanelT + Vpanel # Suma de voltaje del panel solar sin promediar ## potencia de la carga Vcarga = mcp.read_adc(6) Vcarga = ((Vcarga) * (5.15 / 1023)) * (37000.0 / 7500.0) VcargaT = VcargaT + Vcarga Icarga = mcp.read_adc(0) ## Icarga = round(((Icarga) * (5.15 / 1023)), 2) ## voltaje desde el MCP #S_5=(-25.3+10*S_5m)-0.2 Icarga = (-2.54 + Icarga) * (1 / 0.095 ) ## calculo de corriente de la carga IcargaT = IcargaT + Icarga ## potencia de la bateria Ibat = mcp.read_adc(1) Ibat = ((Ibat) * (5.15 / 1023)) #Vsensor=Vsensor+Ibat #Ibat=(-2.54+Ibat)*(1/0.1852) IbatT = IbatT + Ibat Vbat = mcp.read_adc(5) Vbat = ((Vbat) * (5.15 / 1023)) * (37000.0 / 7500.0) VbatT = VbatT + Vbat #j=j+1 # toc = tm.default_timer() ## potencia del panel solar PStotal = round(((IpanelT) * ((VpanelT) + 0.5) / j) / j, 2) ## potencia del panel solar promedio ## potencia de la carga IcargaT = (IcargaT / j) - 0.2 PLtotal = (VcargaT / j) * IcargaT #PLtotal=round((-6.96327 + 0.742732*PLtotal + 0.00062677*PLtotal*PLtotal)+2,2) PLtotal = round( (4.80352 + 0.624629 * PLtotal + 0.000745302 * PLtotal * PLtotal), 2) ## potencia de la bateria IbatT = IbatT / j if IbatT < 2.4: IbatT = 6 * IbatT - 14.28 elif IbatT > 2.46: IbatT = 5 * IbatT - 11.86 else: Ibat = 0 PBtotal = round((IbatT * VbatT) / (j), 2) - 13 #print(IbatT/j) if PLtotal < 2: PLtotal = 0 elif PStotal < 2: PStotal = 0 result = client.read_holding_registers(11729, 2, unit=1) #Current A 1100 decoder = BinaryPayloadDecoder.fromRegisters(result.registers, byteorder=Endian.Big) PTred = decoder.decode_32bit_float() sw = 1 return PTred, PStotal, PBtotal, PLtotal
def __read_s16(self, adr_dec): result = self.client.read_holding_registers(adr_dec, 1, unit=71) s16_value = BinaryPayloadDecoder.fromRegisters(result.registers, byteorder=Endian.Big,wordorder=Endian.Little) return s16_value.decode_16bit_uint()
def read(self, iO='in'): #print('DATEN VOM BUS LESEN') i = 0 if iO !='in': x = 'out' else: x = 'in' try: for byte in self._db[x]: rr = self._modbuspy.read_holding_registers(byte,2) pprint(rr) decodert2 = BinaryPayloadDecoder.fromRegisters(rr.registers, endian=Endian.Little) ##prüfen welcher dpt typ vorliegt und dann das registerabfrage ergebnis aufdröseln: #->decode_16bit_uint() -> 7 / 8 #->decode_8bit_uint() -> 5 | 5.001 #->decode_8bit_int() -> 6 #->decode_bits() -> 1 for bit in self._db[x][byte]: ##eintraege in dict durchgehen bitpos = bit[0] type = bit[1] name = bit[3] if type == '5' or type == '5.001' or type == '6': ##8bit uint / int length = 8 if type == '6': lb = decodert2.decode_8bit_int() hb = decodert2.decode_8bit_int() elif type == '5' or type == '5.001': lb = decodert2.decode_8bit_uint() hb = decodert2.decode_8bit_uint() if bitpos < 8:#lb value = hb #logger.debug('MODBUS: byte{0} startpos{1} wert (5) {2}'.format(bit, bitpos,value)) else:#hb value = lb #logger.debug('MODBUS: byte{0} startpos{1} wert (5) {2}'.format(bit, bitpos,value)) if type == '5.001': #print('lb/hb Daten gelesen', value) value = self.en5001(value) #logger.debug('MODBUS: byte{0} startpos{1} wert (5.001) {2}'.format(bit, bitpos, value)) elif type == '7' or type == '8': #16bit uint / int length = 16 if type == '7': #0...65535 value = decodert2.decode_16bit_uint() #logger.debug('MODBUS: 16bit uint{0}'.format(value)) else: #-32768...32767 value = decodert2.decode_16bit_int() #logger.debug('MODBUS: 16bit int{0}'.format(value)) elif type == '1': length = 1 hb = decodert2.decode_bits() lb = decodert2.decode_bits() bits = lb+hb value = bits[bitpos] #logger.debug('MODBUS: Bits{0}'.format(bits)) bit[2] = value #zurückspeichern decodert2.reset() ##Debug################################################################################# bit[3](value, caller='modbus') i = i+1 lb = decodert2.decode_bits() hb = decodert2.decode_bits() bits = hb+lb decodert2.reset() logger.debug('MODBUS: read from PLC {0}-{1} {2}'.format(byte, bits, bytes)) except Exception as e: logger.error('MODBUS: Could not read an InputWord, because {}'.format( e)) self._lock.release() return None i = 0 return None
async def modbus_loop(self): while True: sleep(0.005) try: reading = self._client.read_holding_registers(40069, 39) if reading: data = BinaryPayloadDecoder.fromRegisters( reading, byteorder=Endian.Big, wordorder=Endian.Big) data.skip_bytes(4) #data.decode_16bit_uint() #data.decode_16bit_uint() #40072-40075 ac_total_current = data.decode_16bit_uint() ac_current_phase_a = data.decode_16bit_uint() ac_current_phase_b = data.decode_16bit_uint() ac_current_phase_c = data.decode_16bit_uint() #40076 ac_current_scalefactor = 10**data.decode_16bit_int() values['ac_total_current'] = self.round( ac_total_current * ac_current_scalefactor) values['ac_current_phase_a'] = self.round( ac_current_phase_a * ac_current_scalefactor) values['ac_current_phase_b'] = self.round( ac_current_phase_b * ac_current_scalefactor) values['ac_current_phase_c'] = self.round( ac_current_phase_c * ac_current_scalefactor) #40077-40079, AC Voltage AB, BC and CA ac_voltage_phase_ab = data.decode_16bit_uint() ac_voltage_phase_bc = data.decode_16bit_uint() ac_voltage_phase_ca = data.decode_16bit_uint() #40080-40082, AC Voltage AN, BN and CN ac_voltage_phase_a = data.decode_16bit_uint() ac_voltage_phase_b = data.decode_16bit_uint() ac_voltage_phase_c = data.decode_16bit_uint() #40083 ac_voltage_phase_scalefactor = 10**data.decode_16bit_int() values['ac_voltage_phase_ab'] = self.round( ac_voltage_phase_ab * ac_voltage_phase_scalefactor) values['ac_voltage_phase_bc'] = self.round( ac_voltage_phase_bc * ac_voltage_phase_scalefactor) values['ac_voltage_phase_ca'] = self.round( ac_voltage_phase_ca * ac_voltage_phase_scalefactor) values['ac_voltage_phase_a'] = self.round( ac_voltage_phase_a * ac_voltage_phase_scalefactor) values['ac_voltage_phase_b'] = self.round( ac_voltage_phase_b * ac_voltage_phase_scalefactor) values['ac_voltage_phase_c'] = self.round( ac_voltage_phase_c * ac_voltage_phase_scalefactor) #40084 ac_power_output = data.decode_16bit_int() #40085 ac_power_scalefactor = 10**data.decode_16bit_int() values['ac_power_output'] = self.round( ac_power_output * ac_power_scalefactor) #40086 frequency freq = data.decode_16bit_uint() freq_scalefactor = 10**data.decode_16bit_int() values['ac_frequency'] = self.round(freq * freq_scalefactor) #AC VA ac_va = data.decode_16bit_int() ac_va_scalefactor = 10**data.decode_16bit_int() values['ac_va'] = self.round(ac_va * ac_va_scalefactor) #AC VAR ac_var = data.decode_16bit_int() ac_var_scalefactor = 10**data.decode_16bit_int() values['ac_var'] = self.round(ac_var * ac_var_scalefactor) #AC PF ac_pf = data.decode_16bit_int() ac_pf_scalefactor = 10**data.decode_16bit_int() values['ac_pf'] = self.round(ac_pf * ac_pf_scalefactor) #40094 lifetime = data.decode_32bit_uint() #40095 lifetimeScaleFactor = 10**data.decode_16bit_uint() #Total production entire lifetime values['ac_lifetimeproduction'] = self.round( lifetime * lifetimeScaleFactor) #40097 DC Current dc_current = data.decode_16bit_uint() dc_current_scalefactor = 10**data.decode_16bit_int() values['dc_current'] = self.round(dc_current * dc_current_scalefactor) #40099 DC Voltage dc_voltage = data.decode_16bit_uint() dc_voltage_scalefactor = 10**data.decode_16bit_int() values['dc_voltage'] = self.round(dc_voltage * dc_voltage_scalefactor) #40101 DC Power input dc_power = data.decode_16bit_int() dc_power_scalefactor = 10**data.decode_16bit_int() values['dc_power_input'] = self.round(dc_power * dc_power_scalefactor) #skip some bytes to read heat sink temperature and scale factor data.skip_bytes(2) temp_sink = data.decode_16bit_int() data.skip_bytes(4) temp_sink_scalefactor = 10**data.decode_16bit_int() values['heat_sink_temperature'] = self.round( temp_sink * temp_sink_scalefactor) #40108 Status values['status'] = data.decode_16bit_uint() #calculate efficiency if values['dc_power_input'] > 0: values['computed_inverter_efficiency'] = self.round( values['ac_power_output'] / values['dc_power_input'] * 100) else: values['computed_inverter_efficiency'] = 0 #debug-print entire dictionary #for x in values.keys(): # print(x +" => " + str(values[x])) # Skip if increamenting counter has gone to 0, or become too large, this happens on inverter startup validValue = values['ac_lifetimeproduction'] > 0 try: float(values['ac_lifetimeproduction']) except Exception as e: validValue = False if validValue: #main state is power production, other values can be fetched as attributes self._state = values['ac_power_output'] self._device_state_attributes = values #tell HA there is new data self.async_schedule_update_ha_state() else: if self._client.last_error() > 0: _LOGGER.error(f'error {self._client.last_error()}') except Exception as e: _LOGGER.error(f'exception: {e}') print(traceback.format_exc()) await asyncio.sleep(self._scan_interval)
def __read_float(self, adr_dec): result = self.client.read_holding_registers(adr_dec, 2, unit=71) float_value = BinaryPayloadDecoder.fromRegisters(result.registers,byteorder=Endian.Big,wordorder=Endian.Little) return round(float_value.decode_32bit_float(), 2)
async def modbus_loop(self): while True: sleep(0.005) try: reading = self._client.read_holding_registers(40188, 107) if reading: data = BinaryPayloadDecoder.fromRegisters( reading, byteorder=Endian.Big, wordorder=Endian.Big) # Identification # 40188 C_SunSpec_DID (unit16) # 40189 C_SunSpec_Length (unit16) data.skip_bytes(4) # Current # #40190 - #40193 m1_ac_current = data.decode_16bit_int() m1_ac_current_phase_a = data.decode_16bit_int() m1_ac_current_phase_b = data.decode_16bit_int() m1_ac_current_phase_c = data.decode_16bit_int() # #40194 m1_ac_current_scalefactor = 10**data.decode_16bit_int() meter1_values['ac_current'] = self.round( m1_ac_current * m1_ac_current_scalefactor) meter1_values['ac_current_phase_a'] = self.round( m1_ac_current_phase_a * m1_ac_current_scalefactor) meter1_values['ac_current_phase_b'] = self.round( m1_ac_current_phase_b * m1_ac_current_scalefactor) meter1_values['ac_current_phase_c'] = self.round( m1_ac_current_phase_c * m1_ac_current_scalefactor) ################ # Voltage ################ #40195-40198, AC Voltage LN, AB, BC and CA m1_ac_voltage_phase_ln = data.decode_16bit_uint() m1_ac_voltage_phase_an = data.decode_16bit_uint() m1_ac_voltage_phase_bn = data.decode_16bit_uint() m1_ac_voltage_phase_cn = data.decode_16bit_uint() #40199-40202, AC Voltage LN, AN, BN and CN m1_ac_voltage_phase_ll = data.decode_16bit_uint() m1_ac_voltage_phase_ab = data.decode_16bit_uint() m1_ac_voltage_phase_bc = data.decode_16bit_uint() m1_ac_voltage_phase_ca = data.decode_16bit_uint() #40203 m1_ac_voltage_phase_scalefactor = 10**data.decode_16bit_int( ) meter1_values['ac_voltage_phase_ll'] = self.round( m1_ac_voltage_phase_ll * m1_ac_voltage_phase_scalefactor) meter1_values['ac_voltage_phase_ab'] = self.round( m1_ac_voltage_phase_ab * m1_ac_voltage_phase_scalefactor) meter1_values['ac_voltage_phase_bc'] = self.round( m1_ac_voltage_phase_bc * m1_ac_voltage_phase_scalefactor) meter1_values['ac_voltage_phase_ca'] = self.round( m1_ac_voltage_phase_ca * m1_ac_voltage_phase_scalefactor) meter1_values['ac_voltage_phase_ln'] = self.round( m1_ac_voltage_phase_ln * m1_ac_voltage_phase_scalefactor) meter1_values['ac_voltage_phase_an'] = self.round( m1_ac_voltage_phase_an * m1_ac_voltage_phase_scalefactor) meter1_values['ac_voltage_phase_bn'] = self.round( m1_ac_voltage_phase_bn * m1_ac_voltage_phase_scalefactor) meter1_values['ac_voltage_phase_cn'] = self.round( m1_ac_voltage_phase_cn * m1_ac_voltage_phase_scalefactor) #40204, Frequency m1_ac_frequency = data.decode_16bit_int() ################ # Power ################ #40205 m1_ac_frequency_scalefactor = 10**data.decode_16bit_int() meter1_values['ac_frequency'] = self.round( m1_ac_frequency * m1_ac_frequency_scalefactor) #40206 m1_ac_power_output = data.decode_16bit_int() data.skip_bytes(6) # Skip the phases #40210 m1_ac_power_scalefactor = 10**data.decode_16bit_int() meter1_values['ac_power_output'] = self.round( m1_ac_power_output * m1_ac_power_scalefactor) #40211 Apparent Power m1_ac_va = data.decode_16bit_uint() data.skip_bytes(6) # Skip the phases m1_ac_va_scalefactor = 10**data.decode_16bit_int() meter1_values['ac_va'] = self.round(m1_ac_va * m1_ac_va_scalefactor) #40216 Reactive Power m1_ac_var = data.decode_16bit_uint() data.skip_bytes(6) # Skip the phases m1_ac_var_scalefactor = 10**data.decode_16bit_int() meter1_values['ac_var'] = self.round(m1_ac_var * m1_ac_var_scalefactor) #40221 Power Factor m1_ac_pf = data.decode_16bit_uint() data.skip_bytes(6) # Skip the phases m1_ac_pf_scalefactor = 10**data.decode_16bit_int() meter1_values['ac_pf'] = self.round(m1_ac_pf * m1_ac_pf_scalefactor) ################ # Accumulated Energy ################ # Real Energy # --------------- #40226 m1_exported = data.decode_32bit_uint() data.skip_bytes(12) # Skip phases #40234 m1_imported = data.decode_32bit_uint() data.skip_bytes(12) # Skip phases #40095 m1_energy_scalefactor = 10**data.decode_16bit_uint() # Total production entire lifetime meter1_values['exported'] = self.round( m1_exported * m1_energy_scalefactor) meter1_values['imported'] = self.round( m1_imported * m1_energy_scalefactor) # Apparent Energy # --------------- #40243 m1_exported_va = data.decode_32bit_uint() data.skip_bytes(12) # Skip phases #40251 m1_imported_va = data.decode_32bit_uint() data.skip_bytes(12) # Skip phases #40259 m1_energy_va_scalefactor = 10**data.decode_16bit_uint() # Total production entire lifetime meter1_values['exported_va'] = self.round( m1_exported_va * m1_energy_va_scalefactor) meter1_values['imported_va'] = self.round( m1_imported_va * m1_energy_va_scalefactor) # Reactive Energy # --------------- #40260 m1_imported_var_q1 = data.decode_32bit_uint() data.skip_bytes(12) # Skip phases #40268 m1_imported_var_q2 = data.decode_32bit_uint() data.skip_bytes(12) # Skip phases #40276 m1_exported_var_q3 = data.decode_32bit_uint() data.skip_bytes(12) # Skip phases #40284 m1_exported_var_q4 = data.decode_32bit_uint() data.skip_bytes(12) # Skip phases #40293 m1_energy_var_scalefactor = 10**data.decode_16bit_uint() # Total production entire lifetime meter1_values['imported_var_q1'] = self.round( m1_imported_var_q1 * m1_energy_var_scalefactor) meter1_values['imported_var_q2'] = self.round( m1_imported_var_q2 * m1_energy_var_scalefactor) meter1_values['exported_var_q3'] = self.round( m1_exported_var_q3 * m1_energy_var_scalefactor) meter1_values['exported_var_q4'] = self.round( m1_exported_var_q4 * m1_energy_var_scalefactor) # Events # --------------- #40097 m1_events = data.decode_32bit_uint() meter1_values['events'] = m1_events # M_EVENT_Power_Failure 0x00000004 Loss of power or phase # M_EVENT_Under_Voltage 0x00000008 Voltage below threshold (Phase Loss) # M_EVENT_Low_PF 0x00000010 Power Factor below threshold (can indicate miss-associated voltage and current inputs in three phase systems) # M_EVENT_Over_Current 0x00000020 Current Input over threshold (out of measurement range) # M_EVENT_Over_Voltage 0x00000040 Voltage Input over threshold (out of measurement range) # M_EVENT_Missing_Sensor 0x00000080 Sensor not connected # Skip if incrementing counters have gone to 0, means something isn't right, usually happens on inverter startup validValue = meter1_values[ 'exported'] > 0 and meter1_values['imported'] > 0 if validValue: self._state = meter1_values['ac_power_output'] self._device_state_attributes = meter1_values #tell HA there is new data self.async_schedule_update_ha_state() else: if self._client.last_error() > 0: _LOGGER.error(f'error {self._client.last_error()}') except Exception as e: _LOGGER.error(f'exception: {e}') print(traceback.format_exc()) await asyncio.sleep(self._scan_interval)
#!/usr/bin/env python from pymodbus.constants import Defaults from pymodbus.constants import Endian from pymodbus.client.sync import ModbusTcpClient as ModbusClient from pymodbus.payload import BinaryPayloadDecoder Defaults.Timeout = 25 Defaults.Retries = 5 client = ModbusClient('ipaddress.of.venus', port='502') result = client.read_input_registers(806, 2) # If you get this error: "unexpected keyword argument 'endian'" try replacing # `endian=Endian.Big` #with # `byteorder=Endian.Big` # on this line: decoder = BinaryPayloadDecoder.fromRegisters(result.registers, endian=Endian.Big) relay1 = decoder.decode_16bit_uint() relay2 = decoder.decode_16bit_uint() print("Relay1: %d, Relay2: %d" % (relay1, relay2))
def __read_u64(self, adr_dec): result = self.client.read_holding_registers(adr_dec, 4, unit=71) u64_value = BinaryPayloadDecoder.fromRegisters(result.registers, byteorder=Endian.Big, wordorder=Endian.Big) return round((u64_value.decode_64bit_uint() * 0.0001), 2)
def requestLoop(self): numberOfRegPerClamp = 74 # max number of holding registers is 125 as the total number of bytes incl. CRC is 256 according to the spec (via 03 command) meterreadings = {} meterreadingsV2 = {} print(time.time()) try: if (self.client.connect() is False): print('not connected') self.client = self.client.connect() print('trying to connecto to ' + str(self.pfcIp)) address = 0 #============================================================================== # Read current timestamp from PFC #============================================================================== timestampSys = round(time.time() * 1000) result2 = self.client.read_holding_registers(timestampPFCRegister, 4) decoder = BinaryPayloadDecoder.fromRegisters(result2.registers, endian=Endian.Little) timestampPFC = decoder.decode_64bit_int() #============================================================================== # Reads the values from modbus registers clamp by clamp # and buffers the results in meterreadings{} # It is not possible to read all registers in one request because of the limitation of the Modbus-Message size to 255kb # When the results of all clamps are buffered, they are published #============================================================================== for x in range(0, len(self.clamps)): result = self.client.read_holding_registers(address, numberOfRegPerClamp) decoder = BinaryPayloadDecoder.fromRegisters(result.registers, endian=Endian.Little) decoded = { 'I1': decoder.decode_32bit_float(), 'I2': decoder.decode_32bit_float(), 'I3': decoder.decode_32bit_float(), 'U1': decoder.decode_32bit_float(), 'U2': decoder.decode_32bit_float(), 'U3': decoder.decode_32bit_float(), 'P1': decoder.decode_32bit_float(), 'P2': decoder.decode_32bit_float(), 'P3': decoder.decode_32bit_float(), 'Q1': decoder.decode_32bit_float(), 'Q2': decoder.decode_32bit_float(), 'Q3': decoder.decode_32bit_float(), 'S1': decoder.decode_32bit_float(), 'S2': decoder.decode_32bit_float(), 'S3': decoder.decode_32bit_float(), 'CosPhi1': decoder.decode_32bit_float(), 'CosPhi2': decoder.decode_32bit_float(), 'CosPhi3': decoder.decode_32bit_float(), 'PF1': decoder.decode_32bit_float(), 'PF2': decoder.decode_32bit_float(), 'PF3': decoder.decode_32bit_float(), 'Qua1': decoder.decode_32bit_float(), 'Qua2': decoder.decode_32bit_float(), 'Qua3': decoder.decode_32bit_float(), 'AEI1': decoder.decode_32bit_float(), 'AED1': decoder.decode_32bit_float(), 'REI1': decoder.decode_32bit_float(), 'REC1': decoder.decode_32bit_float(), 'AEI2': decoder.decode_32bit_float(), 'AED2': decoder.decode_32bit_float(), 'REI2': decoder.decode_32bit_float(), 'REC2': decoder.decode_32bit_float(), 'AEI3': decoder.decode_32bit_float(), 'AED3': decoder.decode_32bit_float(), 'REI3': decoder.decode_32bit_float(), 'REC3': decoder.decode_32bit_float(), 'DataValid' : decoder.decode_32bit_float()} #============================================================================== # standardize both TimestampPFC and TimestampSYS precision to be millisecond #============================================================================== decoded['TimestampPFC'] = str(timestampPFC)[0:13] decoded['TimestampSYS'] = timestampSys #============================================================================== # PFC measures energy values in mWh --> convert to watt-seconds #============================================================================== decoded['AEI1'] = float(decoded['AEI1']) * 3.6 decoded['AED1'] = float(decoded['AED1']) * 3.6 decoded['REI1'] = float(decoded['REI1']) * 3.6 decoded['REC1'] = float(decoded['REC1']) * 3.6 decoded['AEI2'] = float(decoded['AEI2']) * 3.6 decoded['AED2'] = float(decoded['AED2']) * 3.6 decoded['REI2'] = float(decoded['REI2']) * 3.6 decoded['REC2'] = float(decoded['REC2']) * 3.6 decoded['AEI3'] = float(decoded['AEI3']) * 3.6 decoded['AED3'] = float(decoded['AED3']) * 3.6 decoded['REI3'] = float(decoded['REI3']) * 3.6 decoded['REC3'] = float(decoded['REC3']) * 3.6 meterreadingsV2[self.clampsV2[x]] = decoded meterreadings[self.clamps[x]] = decoded address += numberOfRegPerClamp self.publish(u'eshl.wago.v1.readout.wiz.494', json.dumps(meterreadings, sort_keys=True)) self.publish(u'eshl.wago.v2.readout.wiz.494', meterreadingsV2) #============================================================================== # If there is no connection to the pfc-modbus slave or no connection to the pfc at all # the blankDataSet is published #============================================================================== except ConnectionException as connErr: for x in range(0, len(self.clamps)): meterreadings[self.clamps[x]] = self.blankDataSetGen() self.publish(u'eshl.wago.v1.readout.wiz.494', json.dumps(meterreadings, sort_keys=True)) self.publish(u'eshl.wago.v2.readout.wiz.494', meterreadings) sys.__stdout__.write('ConnectionException in-wago-wiz-494' + '\n' + 'Timestamp: ' + str(timestampSys) + ', Errorcode --> ' + str(connErr)) sys.__stdout__.flush() except Exception as err: sys.__stdout__.write('Exception in-wago-wiz-494' + '\n' + 'Timestamp: ' + str(timestampSys) + ', Errorcode --> ' + str(err)) sys.__stdout__.flush()
def _defloat(registers): d = BinaryPayloadDecoder.fromRegisters(registers, byteorder='>') return [d.decode_32bit_float() for r in range(0, len(registers) // 2)]
def run_binary_payload_ex(): # ----------------------------------------------------------------------- # # We are going to use a simple client to send our requests # ----------------------------------------------------------------------- # client = ModbusClient('127.0.0.1', port=5020) client.connect() # ----------------------------------------------------------------------- # # If you need to build a complex message to send, you can use the payload # builder to simplify the packing logic. # # Here we demonstrate packing a random payload layout, unpacked it looks # like the following: # # - a 8 byte string 'abcdefgh' # - a 32 bit float 22.34 # - a 16 bit unsigned int 0x1234 # - another 16 bit unsigned int 0x5678 # - an 8 bit int 0x12 # - an 8 bit bitstring [0,1,0,1,1,0,1,0] # - an 32 bit uint 0x12345678 # - an 32 bit signed int -0x1234 # - an 64 bit signed int 0x12345678 # The packing can also be applied to the word (wordorder) and bytes in each # word (byteorder) # The wordorder is applicable only for 32 and 64 bit values # Lets say we need to write a value 0x12345678 to a 32 bit register # The following combinations could be used to write the register # +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ # # Word Order - Big Byte Order - Big # word1 =0x1234 word2 = 0x5678 # Word Order - Big Byte Order - Little # word1 =0x3412 word2 = 0x7856 # Word Order - Little Byte Order - Big # word1 = 0x5678 word2 = 0x1234 # Word Order - Little Byte Order - Little # word1 =0x7856 word2 = 0x3412 # +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ # # ----------------------------------------------------------------------- # builder = BinaryPayloadBuilder(byteorder=Endian.Big, wordorder=Endian.Little) builder.add_string('abcdefgh') builder.add_bits([0, 1, 0, 1, 1, 0, 1, 0]) builder.add_8bit_int(-0x12) builder.add_8bit_uint(0x12) builder.add_16bit_int(-0x5678) builder.add_16bit_uint(0x1234) builder.add_32bit_int(-0x1234) builder.add_32bit_uint(0x12345678) builder.add_16bit_float(12.34) builder.add_16bit_float(-12.34) builder.add_32bit_float(22.34) builder.add_32bit_float(-22.34) builder.add_64bit_int(-0xDEADBEEF) builder.add_64bit_uint(0x12345678DEADBEEF) builder.add_64bit_uint(0x12345678DEADBEEF) builder.add_64bit_float(123.45) builder.add_64bit_float(-123.45) payload = builder.to_registers() print("-" * 60) print("Writing Registers") print("-" * 60) print(payload) print("\n") payload = builder.build() address = 0 # Can write registers # registers = builder.to_registers() # client.write_registers(address, registers, unit=1) # Or can write encoded binary string client.write_registers(address, payload, skip_encode=True, unit=1) # ----------------------------------------------------------------------- # # If you need to decode a collection of registers in a weird layout, the # payload decoder can help you as well. # # Here we demonstrate decoding a random register layout, unpacked it looks # like the following: # # - a 8 byte string 'abcdefgh' # - a 32 bit float 22.34 # - a 16 bit unsigned int 0x1234 # - another 16 bit unsigned int which we will ignore # - an 8 bit int 0x12 # - an 8 bit bitstring [0,1,0,1,1,0,1,0] # ----------------------------------------------------------------------- # address = 0x0 count = len(payload) result = client.read_holding_registers(address, count, unit=1) print("-" * 60) print("Registers") print("-" * 60) print(result.registers) print("\n") decoder = BinaryPayloadDecoder.fromRegisters(result.registers, byteorder=Endian.Big, wordorder=Endian.Little) assert decoder._byteorder == builder._byteorder, \ "Make sure byteorder is consistent between BinaryPayloadBuilder and BinaryPayloadDecoder" assert decoder._wordorder == builder._wordorder, \ "Make sure wordorder is consistent between BinaryPayloadBuilder and BinaryPayloadDecoder" decoded = OrderedDict([ ('string', decoder.decode_string(8)), ('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(8)), ('64float', decoder.decode_64bit_float()), ('64float2', decoder.decode_64bit_float()), ]) print("-" * 60) print("Decoded Data") print("-" * 60) for name, value in iteritems(decoded): print("%s\t" % name, hex(value) if isinstance(value, int) else value) # ----------------------------------------------------------------------- # # close the client # ----------------------------------------------------------------------- # client.close()
def convert(self, config, data): self.__result["telemetry"] = [] self.__result["attributes"] = [] for config_data in data: if self.__result.get(config_data) is None: self.__result[config_data] = [] for tag in data[config_data]: log.debug(tag) data_sent = data[config_data][tag]["data_sent"] input_data = data[config_data][tag]["input_data"] log.debug("Called convert function from %s with args", self.__class__.__name__) log.debug(data_sent) log.debug(input_data) result = None if data_sent.get("functionCode") == 1 or data_sent.get("functionCode") == 2: result = input_data.bits log.debug(result) if "registerCount" in data_sent: result = result[:data_sent["registerCount"]] else: result = result[0] elif data_sent.get("functionCode") == 3 or data_sent.get("functionCode") == 4: result = input_data.registers byte_order = data_sent.get("byteOrder", "LITTLE") reg_count = data_sent.get("registerCount", 1) type_of_data = data_sent["type"] try: try: if byte_order == "LITTLE": decoder = BinaryPayloadDecoder.fromRegisters(result, byteorder=Endian.Little) elif byte_order == "BIG": decoder = BinaryPayloadDecoder.fromRegisters(result, byteorder=Endian.Big) except TypeError: if byte_order == "LITTLE": decoder = BinaryPayloadDecoder.fromRegisters(result, endian=Endian.Little) elif byte_order == "BIG": decoder = BinaryPayloadDecoder.fromRegisters(result, endian=Endian.Big) else: log.warning("byte order is not BIG or LITTLE") # continue except Exception as e: log.error(e) if type_of_data == "string": result = decoder.decode_string(2 * reg_count) elif type_of_data == "long": try: if reg_count == 1: # r = decoder.decode_8bit_int() result = decoder.decode_16bit_int() elif reg_count == 2: result = decoder.decode_32bit_int() elif reg_count == 4: result = decoder.decode_64bit_int() else: log.warning("unsupported register count for long data type in response for tag %s", data_sent["tag"]) except Exception as e: log.exception(e) elif type_of_data == "double": if reg_count == 2: result = decoder.decode_32bit_float() elif reg_count == 4: result = decoder.decode_64bit_float() else: log.warning("unsupported register count for double data type in response for tag %s", data_sent["tag"]) # continue elif type_of_data == "bit": if "bit" in data_sent: if type(result) == list: if len(result) > 1: log.warning("with bit parameter only one register is expected, got more then one in response for tag %s", data_sent["tag"]) continue result = result[0] position = 15 - data_sent["bit"] # reverse order # transform result to string representation of a bit sequence, add "0" to make it longer >16 result = "0000000000000000" + str(bin(result)[2:]) # get length of 16, then get bit, then cast it to int(0||1 from "0"||"1", then cast to boolean) result = bool(int((result[len(result) - 16:])[15 - position])) else: log.error("Bit address not found in config for modbus connector for tag: %s", data_sent["tag"]) else: log.warning("unknown data type, not string, long or double in response for tag %s", data_sent["tag"]) continue try: if result == 0: self.__result[config_data].append({tag: result}) elif int(result): self.__result[config_data].append({tag: result}) except ValueError: try: self.__result[config_data].append({tag: int(result, 16)}) except ValueError: self.__result[config_data].append({tag: result.decode('UFT-8')}) self.__result["telemetry"] = self.__result.pop("timeseries") log.debug(self.__result) return self.__result
def get_pv_data(config): host = config.get('inv_host') port = config.get('inv_port', 502) modbusid = config.get('inv_modbus', 3) manufacturer = config.get('inv_modbus', 'Default') registers = eval(config.get('registers')) client = ModbusClient(host=host, port=port) try: client.connect() except: print('Modbus Connection Error', 'could not connect to target. Check your settings, please.') return None data = {} ## empty data store for current values for myreg in registers: ## if the connection is somehow not possible (e.g. target not responding) # show a error message instead of excepting and stopping try: received = client.read_input_registers(address=int(myreg[0]), count=modbusdatatype[myreg[1]], unit=modbusid) except: thisdate = str(datetime.datetime.now()).partition('.')[0] thiserrormessage = thisdate + ': Connection not possible. Check settings or connection.' print(thiserrormessage) return None ## prevent further execution of this function name = myreg[3] message = BinaryPayloadDecoder.fromRegisters(received.registers, byteorder=Endian.Big, wordorder=Endian.Big) ## provide the correct result depending on the defined datatype if myreg[1] == 'S32': interpreted = message.decode_32bit_int() elif myreg[1] == 'U32': interpreted = message.decode_32bit_uint() elif myreg[1] == 'U64': interpreted = message.decode_64bit_uint() elif myreg[1] == 'STR32': interpreted = message.decode_string(32) elif myreg[1] == 'S16': interpreted = message.decode_16bit_int() elif myreg[1] == 'U16': interpreted = message.decode_16bit_uint() else: ## if no data type is defined do raw interpretation of the delivered data interpreted = message.decode_16bit_uint() ## check for "None" data before doing anything else if ((interpreted == MIN_SIGNED) or (interpreted == MAX_UNSIGNED)): value = None else: ## put the data with correct formatting into the data table if myreg[2] == 'FIX3': value = float(interpreted) / 1000 elif myreg[2] == 'FIX2': value = float(interpreted) / 100 elif myreg[2] == 'FIX1': value = float(interpreted) / 10 elif myreg[2] == 'UTF8': value = str(interpreted,'UTF-8').rstrip("\x00") elif myreg[2] == 'ENUM': e=pvenums.get(name,{}) value = e.get(interpreted,str(interpreted)) else: value = interpreted data[name] = value client.close() return data
def check_values(p_client,p_first_register, m_client, m_first_register,sleep_time): global BAR_INPUT, CUMULATIVE_VOLUME exp_item = OrderedDict() log.debug("check_values %s %s" % (p_client,m_client)) # m_start_address = m_first_register-1 # if zero_mode=False => inizia a leggere da Manifold a partire da m_first_register-1 per gli N successivi,escluso m_start_address #################################### CAVALLETTO m_start_address = m_first_register m_rr = m_client.read_holding_registers(m_start_address,35) # Machine ID m_ID = m_rr.registers[1-1] # IP ADDR. da 32,33,34 e 35 sIPAddr = "%d.%d.%d.%d" % tuple(m_rr.registers[32-1:36-1]) # pressione in mA in posizione 4 p_mA = m_rr.registers[4-1] p_bar = scale(p_p1,p_p2,p_mA) # portata in mA in posizione 6 q_mA = m_rr.registers[6-1] q_bar = scale(q_p1,q_p2,q_mA) CUMULATIVE_VOLUME += q_bar*(sleep_time/60.) p_eff, static_head, p_hdlf = peff(p_bar, q_bar) log.debug("\n#### Readings from %s (%s)####\n##Pressure \tP(mA)=%d \tP(bar)=%d \n##Flow-rate \tQ(mA)=%d \tQ(lit/min)=%d Peff = %f\n####" %(m_ID, sIPAddr,m_rr.registers[4-1], p_bar, m_rr.registers[6-1],q_bar, p_eff )) exp_item["TIME"] = datetime.datetime.utcnow().strftime("%Y%m%d %H:%M:%S.%f") exp_item["m_ID"] = m_ID exp_item["m_IP"] = sIPAddr exp_item["STAGE_LENGTH"] = STAGE_LENGTH exp_item["p_gauge"] = p_bar exp_item["static_head"] = static_head exp_item["p_hdlf"] = p_hdlf exp_item["p_eff"] = p_eff exp_item["R"] = R exp_item["q_bar"] = q_bar exp_item["q_cum"] = CUMULATIVE_VOLUME volume_m = CUMULATIVE_VOLUME/STAGE_LENGTH exp_item["volume_m"] = volume_m BAR_INPUT = q_bar #################################### INIETTORE #p_start_address = p_first_register-1 # if zero_mode=False => inizia a leggere da m_first_register-1 e prendi gli N successivi,escluso m_start_address p_start_address = p_first_register p_rr = p_client.read_holding_registers(p_start_address+500,100,unit=1) # danzi.tn@20160729 iniettore inizia da 0 noi prendiamo da 500 a 599 p_out = p_rr.registers[16] # pressione in uscita dall'iniettore q_out = p_rr.registers[20] # portata in uscita dall'iniettore p_max = p_rr.registers[60] # pressione massima iniettore q_max = p_rr.registers[62] # portata massima iniettore np_max = R + p_out - p_eff dP_pump = p_max - p_out dP_borehole = R - p_eff decoder = BinaryPayloadDecoder.fromRegisters(p_rr.registers[22:26],endian=Endian.Little) f_522 = decoder.decode_32bit_float() f_524 = decoder.decode_32bit_float() log.debug("\n#### Readings ####\n##p_bar=%f;p_eff=%f;p_out=%d;p_max=%d;np_max=%d;q=%f;V=%f\n####" %(p_bar, p_eff, p_out,p_max,np_max,q_bar,CUMULATIVE_VOLUME)) bR, bStop, next_mix_type, bIntermittent = check_mix(volume_m, MIX_TYPE, p_eff,R,BUPSTAGE, P_PREVIOUS ) #check_mix(volume_m,p_eff,R) exp_item["stop"] = bStop exp_item["ok R"] = bR exp_item["next mix_type"] = next_mix_type exp_item["p_out"] = p_out exp_item["q_out"] = q_out # salvo anche portata in uscita dall'iniettore exp_item["q_max"] = q_max # salvo anche portata massima dell'iniettore exp_item["p_max"] = p_max exp_item["p_max_new"] = np_max exp_item["dP_pump"] = dP_pump exp_item["dP_borehole"] = dP_borehole return exp_item
class ModbusSerialReader(BaseReader): """" A class to manage Mobus serial connections. Constants: KEYS (dict[string]): Defines the dictionary keys of a Modbus request. __TYPES (List[string]): A list of all data types that can be read. __REGISTER_COUNT_BY_TYPE (dict[int]): Defines the mapping of input type to registry size. __DECODER_BY_TYPE (dict[lambda]): Defines the mapping of decoding functions. Attributes: client (pymodbus.client.sync.ModbusSerialClient): Object of the ModbusSerialClient class. """ # Defining the dictionary keys of a Modbus request KEYS = { 'NAME': 'name', 'SLAVE_UNIT': 'slave_unit', 'ADDRESS': 'address', 'TYPE': 'type', } # Defining types codes __TYPES = ['UInt16', 'Int16', 'UInt32', 'Int64', 'UTF8', 'Float32'] # Defining the mapping of input type to registry size __REGISTER_COUNT_BY_TYPE = { 'UInt16': 1, 'Int16': 1, 'UInt32': 2, 'Int64': 4, 'UTF8': 8, 'Float32': 2, } # Defining the mapping of decoding functions __DECODER_BY_TYPE = { 'UInt16': lambda x: BinaryPayloadDecoder.fromRegisters(x, endian=Endian.Big).decode_16bit_uint(), 'Int16': lambda x: BinaryPayloadDecoder.fromRegisters(x, endian=Endian.Big).decode_16bit_int(), 'UInt32': lambda x: BinaryPayloadDecoder.fromRegisters(x, endian=Endian.Big).decode_32bit_uint(), 'Int64': lambda x: BinaryPayloadDecoder.fromRegisters(x, endian=Endian.Big).decode_64bit_int(), 'UTF8': lambda x: BinaryPayloadDecoder.fromRegisters(x, endian=Endian.Big).decode_string(8), 'Float32': lambda x: BinaryPayloadDecoder.fromRegisters(x, endian=Endian.Big).decode_32bit_float(), } client = None def __init__(self, port, parity, baudrate, timeout=0.05, sensor_name='sensor', sensor_location='location', dropbox_keys=None, database_keys=None, delimiter=',', output_file_ext='.csv', output_file_folder='Outputs'): """ Constructor method of the ModbusSerialReader class. The Modbus client is initialized. Parameters: Argument 1: port (string) The address of the port that is connected to the device. Argument 2: parity (string) The parity setting of the connection. Argument 3: baudrate (int) The rate of bits transfer per second. Argument 4: timeout (float, optional) The amount of time given for the client to read per request. Defaults to 0.05. Argument 5, 6, 7, 8, 9, 10, 11: See BaseReader """ super(ModbusSerialReader, self).__init__(sensor_name=sensor_name, sensor_location=sensor_location, dropbox_keys=dropbox_keys, database_keys=database_keys, delimiter=delimiter, output_file_ext=output_file_ext, output_file_folder=output_file_folder) self.initialize_connection(port, parity, baudrate, timeout) def initialize_connection(self, port, parity, baudrate, timeout): """ Defines and set the class attribute for the Modbus serial client with the settings given in the parameters. Parameters: Argument 1: port (string) The address of the port that is connected to the device. Argument 2: parity (string) The parity setting of the connection. Must either be one of "E", "O" or "N" only. Argument 3: baudrate (int) The rate of bits transfer per second. Must be in the possible values of 300-100000. Raises: ValueError: If parity code is invalid, or baudrate is invalid; < 300 or > 1000000. SystemError: If connection with the device fails. """ # Validation if parity not in 'EON' or len(parity) != 1: raise ValueError('Invalid parity code; enter either "E", "O" or "N"') if baudrate < 300 or baudrate > 100000: raise ValueError('Invalid baudrate; enter possible values of 300-100000') self.client = ModbusSerialClient( method='rtu', port=port, stopbits=1, bytesize=8, parity=parity, baudrate=baudrate, timeout=timeout ) if not self.test_connection(): raise SystemError('Connection with device failed; retry connection using initialize_connection()') def test_connection(self): """ Checks and reports the status of the serial Modbus connection. Returns: True: If the connection is valid; False: If the connection is not valid. Raises: SystemError: If the Modbus client is yet to be initialized. """ if self.client is None: raise SystemError('Modbus client not yet initialize; run initialize_connection() first') return self.client.connect() def read_input(self, input_filename, has_headers=True, delimiter=','): """ Reads a list of input settings from a file in the local directory. Parameters: Argument 1: input_filename (string) The name of the input file to be read. Argument 2: has_headers (bool, optional) True if input file has headers; false otherwise. Defaults to True. Argument 3: delimiter (string, optional) A separator sequence to denote end of data read. Defaults to "," (comma). Note: The required format of the input file is: [Name of data] [Slave unit], [Starting register address], [Data type] Returns: List of dictionary (List[dict]): List of requests represented in dictionary form. e.g. [ { 'name': 'apparent_power', 'slave_unit': '1', 'address': '3053', 'type': 'Float32' }, { 'name': 'real_power', 'slave_unit': '1', 'address': '3053', 'type': 'Float32' }, { 'name': 'total_power', 'slave_unit': '1', 'address': '3053', 'type': 'Float32' } ] Raises: SystemError: If the input fie does not exist. TypeError: If type code is invalid. ValueError: If address is invalid range; < 0, or > 39999 or slave_unit, address is not an integer. """ # Validation if not isfile(input_filename): raise SystemError('Input file "' + input_filename + '" does not exist') # Reads in the data with open(input_filename, 'rb') as input_file: # rb: read-only in binary header = [self.KEYS['NAME'], self.KEYS['SLAVE_UNIT'], self.KEYS['ADDRESS'], self.KEYS['TYPE']] if has_headers: output = [dict(zip(header, line.strip().split(delimiter))) for line in input_file][1:] # remove first row else: output = [dict(zip(header, line.strip().split(delimiter))) for line in input_file] # To convert types and data validation for request in output: request[self.KEYS['SLAVE_UNIT']] = int(request[self.KEYS['SLAVE_UNIT']]) # for slave unit request[self.KEYS['ADDRESS']] = int(request[self.KEYS['ADDRESS']]) # for address if request[self.KEYS['ADDRESS']] < 0 or request[self.KEYS['ADDRESS']] > 39999: raise ValueError('Address "' + str(request[self.KEYS['ADDRESS']]) + '" not within possible range: 0-39999') if request[self.KEYS['TYPE']] not in self.__TYPES: # for type raise TypeError('Type code "' + request[self.KEYS['TYPE']] + '" entered is invalid') return output def execute_request(self, request): """ Executes a reading request. Parameters: Argument 1: request (dict) The request represented by a dictionary as prepared by the input reader. Returns: Reading (type: any): The decoded data parsed in the data type of the format as given. Raises: KeyError: If any of the keys of the dictionary is invalid. SystemError: If the connection of the Modbus client is invalid. """ # Validation if not self.test_connection(): raise SystemError('Connection with device failed; retry connection using initialize_connection()') # Read raw values from holding registers response = self.client.read_holding_registers( address=request[self.KEYS['ADDRESS']], count=self.__REGISTER_COUNT_BY_TYPE[request[self.KEYS['TYPE']]], unit=request[self.KEYS['SLAVE_UNIT']] ) return self.decode(response, request[self.KEYS['TYPE']]) def decode(self, response, data_type): """ Decodes the response according to the data format given. Parameters: Argument 1: response (List[int]) The response of the read represented by a list of 16-bit int values in a list. Argument 2: data_type (string) The format of the data to be decoded. Returns: Reading (type: any): The decoded data parsed in the data type of the format as given. Raises: ValueEror: If the output is invalid. """ try: return self.__DECODER_BY_TYPE[data_type](response.registers) except: raise ValueError('Invalid output decoded')
def updating_pump_writer(a): ''' A worker process that runs every so often and updates live values of the context. It should be noted that there is a race condition for the update. :param arguments: The input arguments to the call ''' context = a[0] srv_id = a[1] handler = a[2] register = 3 slave_id = 0x00 values = context[slave_id].getValues(register, 0, count=600) # update P and Q with random values logInfo.debug("PUMP context values: " + str(values)) logInfo.debug("PUMP p_out=%d; q_out=%d" % (handler.p_out,handler.q_out)) decoder = BinaryPayloadDecoder.fromRegisters(values[502:503],endian=Endian.Little) bits_502 = decoder.decode_bits() bits_502 += decoder.decode_bits() decoder = BinaryPayloadDecoder.fromRegisters(values[552:553],endian=Endian.Little) bits_552 = decoder.decode_bits() bits_552 += decoder.decode_bits() decoder = BinaryPayloadDecoder.fromRegisters(values[506:507],endian=Endian.Little) bits_506 = decoder.decode_bits() bits_506 += decoder.decode_bits() cicli_min = 0 p_new = 0 q_val = 0 # if iniettore Started if handler.pumpStarted and not bits_502[7]: handler.pumpStarted = False if bits_502[7]: #cicli_min = cicli_rand.rvs() q_val = handler.q_out if q_val < 0: q_val = 0 cicli_min = int(q_val / liters_cycle ) p_new = handler.p_out logInfo.debug("PUMP p_new=%d" % p_new) q_m_ch = 60.0*q_val/1000.0 handler.cicli_min = cicli_min handler.q_m_ch = q_m_ch logInfo.debug("PUMP cicli=%d, q=%f, mc=%f" % (cicli_min, q_val,q_m_ch)) # conversione float - Endian.Little il primo è il meno significativo handler.p_pump_out = p_new handler.q_pump_out = cicli_min values[516] = p_new # %MW516 PRESSIONE ATTUALE values[520] = cicli_min handler.qmax = values[562] handler.pmax = values[560] builder = BinaryPayloadBuilder(endian=Endian.Little) builder.add_32bit_float(q_val) builder.add_32bit_float(q_m_ch) reg=builder.to_registers() logInfo.debug("PUMP 2 x 32bit_float = %s" % str(reg)) values[522:526]=reg logInfo.debug("PUMP On Pump Server %02d new values (516-525): %s" % (srv_id, str(values[516:526]))) # assign new values to context values[599] = 699 context[slave_id].setValues(register, 0, values)
async def write_to_influx(dbhost, dbport, mbmeters, period, dbname, legacysupport): global client global datapoint global reg_block global promInv global promMeter def trunc_float(floatval): return float('%.2f' % floatval) try: solar_client = InfluxDBClient(host=dbhost, port=dbport, db=dbname) await solar_client.create_database(db=dbname) except ClientConnectionError as e: logger.error(f'Error during connection to InfluxDb {dbhost}: {e}') return logger.info('Database opened and initialized') # Connect to the solaredge inverter client = ModbusClient(args.inverter_ip, port=args.inverter_port, unit_id=args.unitid, auto_open=True) # Read the common blocks on the Inverter while True: reg_block = {} reg_block = client.read_holding_registers(40004, 65) if reg_block: decoder = BinaryPayloadDecoder.fromRegisters(reg_block, byteorder=Endian.Big, wordorder=Endian.Big) InvManufacturer = decoder.decode_string(32).decode( 'UTF-8') #decoder.decode_32bit_float(), InvModel = decoder.decode_string(32).decode( 'UTF-8') #decoder.decode_32bit_int(), Invfoo = decoder.decode_string(16).decode('UTF-8') InvVersion = decoder.decode_string(16).decode( 'UTF-8') #decoder.decode_bits() InvSerialNumber = decoder.decode_string(32).decode('UTF-8') InvDeviceAddress = decoder.decode_16bit_uint() print('*' * 60) print('* Inverter Info') print('*' * 60) print(' Manufacturer: ' + InvManufacturer) print(' Model: ' + InvModel) print(' Version: ' + InvVersion) print(' Serial Number: ' + InvSerialNumber) print(' ModBus ID: ' + str(InvDeviceAddress)) break else: # Error during data receive if client.last_error() == 2: logger.error( f'Failed to connect to SolarEdge inverter {client.host()}!' ) elif client.last_error() == 3 or client.last_error() == 4: logger.error('Send or receive error!') elif client.last_error() == 5: logger.error('Timeout during send or receive operation!') await asyncio.sleep(period) # Read the common blocks on the meter/s (if present) connflag = False if mbmeters >= 1: while True: dictMeterLabel = [] for x in range(1, mbmeters + 1): reg_block = {} if x == 1: reg_block = client.read_holding_registers(40123, 65) if x == 2: reg_block = client.read_holding_registers(40297, 65) if x == 3: reg_block = client.read_holding_registers(40471, 65) if reg_block: decoder = BinaryPayloadDecoder.fromRegisters( reg_block, byteorder=Endian.Big, wordorder=Endian.Big) MManufacturer = decoder.decode_string(32).decode( 'UTF-8') #decoder.decode_32bit_float(), MModel = decoder.decode_string(32).decode( 'UTF-8') #decoder.decode_32bit_int(), MOption = decoder.decode_string(16).decode('UTF-8') MVersion = decoder.decode_string(16).decode( 'UTF-8') #decoder.decode_bits() MSerialNumber = decoder.decode_string(32).decode('UTF-8') MDeviceAddress = decoder.decode_16bit_uint() fooLabel = MManufacturer.split( '\x00')[0] + '(' + MSerialNumber.split('\x00')[0] + ')' dictMeterLabel.append(fooLabel) print('*' * 60) print('* Meter ' + str(x) + ' Info') print('*' * 60) print(' Manufacturer: ' + MManufacturer) print(' Model: ' + MModel) print(' Mode: ' + MOption) print(' Version: ' + MVersion) print(' Serial Number: ' + MSerialNumber) print(' ModBus ID: ' + str(MDeviceAddress)) if x == mbmeters: print('*' * 60) connflag = True else: # Error during data receive if client.last_error() == 2: logger.error( f'Failed to connect to SolarEdge inverter {client.host()}!' ) elif client.last_error() == 3 or client.last_error() == 4: logger.error('Send or receive error!') elif client.last_error() == 5: logger.error( 'Timeout during send or receive operation!') await asyncio.sleep(period) if connflag: break # Start the loop for collecting the metrics... while True: try: reg_block = {} dictInv = {} reg_block = client.read_holding_registers(40069, 50) if reg_block: # print(reg_block) # reg_block[0] = Sun Spec DID # reg_block[1] = Length of Model Block # reg_block[2] = AC Total current value # reg_block[3] = AC Phase A current value # reg_block[4] = AC Phase B current value # reg_block[5] = AC Phase C current value # reg_block[6] = AC current scale factor # reg_block[7] = AC Phase A to B voltage value # reg_block[8] = AC Phase B to C voltage value # reg_block[9] = AC Phase C to A voltage value # reg_block[10] = AC Phase A to N voltage value # reg_block[11] = AC Phase B to N voltage value # reg_block[12] = AC Phase C to N voltage value # reg_block[13] = AC voltage scale factor # reg_block[14] = AC Power value # reg_block[15] = AC Power scale factor # reg_block[16] = AC Frequency value # reg_block[17] = AC Frequency scale factor # reg_block[18] = AC Apparent Power # reg_block[19] = AC Apparent Power scale factor # reg_block[20] = AC Reactive Power # reg_block[21] = AC Reactive Power scale factor # reg_block[22] = AC Power Factor # reg_block[23] = AC Power Factor scale factor # reg_block[24] = AC Lifetime Energy (HI bits) # reg_block[25] = AC Lifetime Energy (LO bits) # reg_block[26] = AC Lifetime Energy scale factor # reg_block[27] = DC Current value # reg_block[28] = DC Current scale factor # reg_block[29] = DC Voltage value # reg_block[30] = DC Voltage scale factor # reg_block[31] = DC Power value # reg_block[32] = DC Power scale factor # reg_block[34] = Inverter temp # reg_block[37] = Inverter temp scale factor # reg_block[38] = Inverter Operating State # reg_block[39] = Inverter Status Code datapoint = { 'measurement': 'SolarEdge', 'tags': {}, 'fields': {} } logger.debug(f'inverter reg_block: {str(reg_block)}') datapoint['tags']['inverter'] = str(1) data = BinaryPayloadDecoder.fromRegisters(reg_block, byteorder=Endian.Big, wordorder=Endian.Big) # SunSpec DID # Register 40069 fooVal = trunc_float(data.decode_16bit_uint()) fooName = 'SunSpec_DID' if fooVal < 65535: dictInv[fooName] = fooVal else: dictInv[fooName] = 0.0 # SunSpec Length # Register 40070 fooVal = trunc_float(data.decode_16bit_uint()) fooName = 'SunSpec_Length' if fooVal < 65535: dictInv[fooName] = fooVal else: dictInv[fooName] = 0.0 # AC Current data.skip_bytes(8) # Register 40075 scalefactor = 10**data.decode_16bit_int() data.skip_bytes(-10) # Register 40071-40074 fooVal = trunc_float(data.decode_16bit_uint()) fooName = 'AC_Current' if fooVal < 65535: dictInv[fooName] = fooVal * scalefactor else: dictInv[fooName] = 0.0 fooVal = trunc_float(data.decode_16bit_uint()) fooName = 'AC_CurrentA' if fooVal < 65535: dictInv[fooName] = fooVal * scalefactor else: dictInv[fooName] = 0.0 fooVal = trunc_float(data.decode_16bit_uint()) fooName = 'AC_CurrentB' if fooVal < 65535: dictInv[fooName] = fooVal * scalefactor else: dictInv[fooName] = 0.0 fooVal = trunc_float(data.decode_16bit_uint()) fooName = 'AC_CurrentC' if fooVal < 65535: dictInv[fooName] = fooVal * scalefactor else: dictInv[fooName] = 0.0 # AC Voltage data.skip_bytes(14) # Register 40082 scalefactor = 10**data.decode_16bit_int() data.skip_bytes(-14) # Register 40077 fooVal = trunc_float(data.decode_16bit_uint()) fooName = 'AC_VoltageAB' if fooVal < 65535: dictInv[fooName] = fooVal * scalefactor else: dictInv[fooName] = 0.0 fooVal = trunc_float(data.decode_16bit_uint()) fooName = 'AC_VoltageBC' if fooVal < 65535: dictInv[fooName] = fooVal * scalefactor else: dictInv[fooName] = 0.0 fooVal = trunc_float(data.decode_16bit_uint()) fooName = 'AC_VoltageCA' if fooVal < 65535: dictInv[fooName] = fooVal * scalefactor else: dictInv[fooName] = 0.0 fooVal = trunc_float(data.decode_16bit_uint()) fooName = 'AC_VoltageAN' if fooVal < 65535: dictInv[fooName] = fooVal * scalefactor else: dictInv[fooName] = 0.0 fooVal = trunc_float(data.decode_16bit_uint()) fooName = 'AC_VoltageBN' if fooVal < 65535: dictInv[fooName] = fooVal * scalefactor else: dictInv[fooName] = 0.0 fooVal = trunc_float(data.decode_16bit_uint()) fooName = 'AC_VoltageCN' if fooVal < 65535: dictInv[fooName] = fooVal * scalefactor else: dictInv[fooName] = 0.0 # AC Power data.skip_bytes(4) # Register 40084 scalefactor = 10**data.decode_16bit_int() data.skip_bytes(-4) # Register 40083 fooVal = trunc_float(data.decode_16bit_int()) fooName = 'AC_Power' if fooVal < 65535: dictInv[fooName] = fooVal * scalefactor else: dictInv[fooName] = 0.0 # AC Frequency data.skip_bytes(4) # Register 40086 scalefactor = 10**data.decode_16bit_int() data.skip_bytes(-4) # Register 40085 fooVal = trunc_float(data.decode_16bit_uint()) fooName = 'AC_Frequency' if fooVal < 65535: dictInv[fooName] = fooVal * scalefactor else: dictInv[fooName] = 0.0 # AC Apparent Power data.skip_bytes(4) # Register 40088 scalefactor = 10**data.decode_16bit_int() data.skip_bytes(-4) # Register 40087 fooVal = trunc_float(data.decode_16bit_int()) fooName = 'AC_VA' if fooVal < 65535: dictInv[fooName] = fooVal * scalefactor else: dictInv[fooName] = 0.0 # AC Reactive Power data.skip_bytes(4) # Register 40090 scalefactor = 10**data.decode_16bit_int() data.skip_bytes(-4) # Register 40089 fooVal = trunc_float(data.decode_16bit_int()) fooName = 'AC_VAR' if fooVal < 65535: dictInv[fooName] = fooVal * scalefactor else: dictInv[fooName] = 0.0 # AC Power Factor data.skip_bytes(4) # Register 40092 scalefactor = 10**data.decode_16bit_int() data.skip_bytes(-4) # Register 40091 fooVal = trunc_float(data.decode_16bit_int()) fooName = 'AC_PF' if fooVal < 65535: dictInv[fooName] = fooVal * scalefactor else: dictInv[fooName] = 0.0 # AC Lifetime Energy Production data.skip_bytes(6) # Register 40095 scalefactor = 10**data.decode_16bit_uint() data.skip_bytes(-6) # Register 40093 fooVal = trunc_float(data.decode_32bit_uint()) fooName = 'AC_Energy_WH' if fooVal < 4294967295: dictInv[fooName] = fooVal * scalefactor else: dictInv[fooName] = 0.0 # DC Current data.skip_bytes(4) # Register 40097 scalefactor = 10**data.decode_16bit_int() data.skip_bytes(-4) # Register 40096 fooVal = trunc_float(data.decode_16bit_uint()) fooName = 'DC_Current' if fooVal < 65535: dictInv[fooName] = fooVal * scalefactor else: dictInv[fooName] = 0.0 # DC Voltage data.skip_bytes(4) # Register 40099 scalefactor = 10**data.decode_16bit_int() data.skip_bytes(-4) # Register 40098 fooVal = trunc_float(data.decode_16bit_uint()) fooName = 'DC_Voltage' if fooVal < 65535: dictInv[fooName] = fooVal * scalefactor else: dictInv[fooName] = 0.0 # DC Power data.skip_bytes(4) # Register 40101 scalefactor = 10**data.decode_16bit_int() data.skip_bytes(-4) # Register 40100 fooVal = trunc_float(data.decode_16bit_int()) fooName = 'DC_Power' if fooVal < 65535: dictInv[fooName] = fooVal * scalefactor else: dictInv[fooName] = 0.0 # Inverter Temp data.skip_bytes(10) # Register 40106 scalefactor = 10**data.decode_16bit_int() data.skip_bytes(-8) # Register 40103 fooVal = trunc_float(data.decode_16bit_int()) fooName = 'Temp_Sink' if fooVal < 65535: dictInv[fooName] = fooVal * scalefactor else: dictInv[fooName] = 0.0 # Inverter Operating State data.skip_bytes(6) # Register 40107 fooVal = trunc_float(data.decode_16bit_uint()) fooName = 'Status' if fooVal < 65535: dictInv[fooName] = fooVal else: dictInv[fooName] = 0.0 # Inverter Operating Status Code # Register 40108 fooVal = trunc_float(data.decode_16bit_uint()) fooName = 'Status_Vendor' if fooVal < 65535: dictInv[fooName] = fooVal else: dictInv[fooName] = 0.0 # Adding the ScaleFactor elements dictInv['AC_Current_SF'] = 0.0 dictInv['AC_Voltage_SF'] = 0.0 dictInv['AC_Power_SF'] = 0.0 dictInv['AC_Frequency_SF'] = 0.0 dictInv['AC_VA_SF'] = 0.0 dictInv['AC_VAR_SF'] = 0.0 dictInv['AC_PF_SF'] = 0.0 dictInv['AC_Energy_WH_SF'] = 0.0 dictInv['DC_Current_SF'] = 0.0 dictInv['DC_Voltage_SF'] = 0.0 dictInv['DC_Power_SF'] = 0.0 dictInv['Temp_SF'] = 0.0 logger.debug(f'Inverter') for j, k in dictInv.items(): logger.debug(f' {j}: {k}') publish_metrics(dictInv, 'inverter', '') logger.debug('Done publishing inverter metrics...') datapoint['time'] = str(datetime.datetime.utcnow().replace( tzinfo=datetime.timezone.utc).isoformat()) logger.debug(f'Writing to Influx: {str(datapoint)}') await solar_client.write(datapoint) else: # Error during data receive if client.last_error() == 2: logger.error( f'Failed to connect to SolarEdge inverter {client.host()}!' ) elif client.last_error() == 3 or client.last_error() == 4: logger.error('Send or receive error!') elif client.last_error() == 5: logger.error('Timeout during send or receive operation!') await asyncio.sleep(period) for x in range(1, mbmeters + 1): # Now loop through this for each meter that is attached. logger.debug(f'Meter={str(x)}') reg_block = {} dictM = {} # Start point is different for each meter if x == 1: reg_block = client.read_holding_registers(40188, 105) if x == 2: reg_block = client.read_holding_registers(40362, 105) if x == 3: reg_block = client.read_holding_registers(40537, 105) if reg_block: # print(reg_block) # reg_block[0] = AC Total current value # reg_block[1] = AC Phase A current value # reg_block[2] = AC Phase B current value # reg_block[3] = AC Phase C current value # reg_block[4] = AC current scale factor # reg_block[5] = AC Phase Line (average) to N voltage value # reg_block[6] = AC Phase A to N voltage value # reg_block[7] = AC Phase B to N voltage value # reg_block[8] = AC Phase C to N voltage value # reg_block[9] = AC Phase Line to Line voltage value # reg_block[10] = AC Phase A to B voltage value # reg_block[11] = AC Phase B to C voltage value # reg_block[12] = AC Phase C to A voltage value # reg_block[13] = AC voltage scale factor # reg_block[14] = AC Frequency value # reg_block[15] = AC Frequency scale factor # reg_block[16] = Total Real Power # reg_block[17] = Phase A Real Power # reg_block[18] = Phase B Real Power # reg_block[19] = Phase C Real Power # reg_block[20] = Real Power scale factor # reg_block[21] = Total Apparent Power # reg_block[22] = Phase A Apparent Power # reg_block[23] = Phase B Apparent Power # reg_block[24] = Phase C Apparent Power # reg_block[25] = Apparent Power scale factor # reg_block[26] = Total Reactive Power # reg_block[27] = Phase A Reactive Power # reg_block[28] = Phase B Reactive Power # reg_block[29] = Phase C Reactive Power # reg_block[30] = Reactive Power scale factor # reg_block[31] = Average Power Factor # reg_block[32] = Phase A Power Factor # reg_block[33] = Phase B Power Factor # reg_block[34] = Phase C Power Factor # reg_block[35] = Power Factor scale factor # reg_block[36] = Total Exported Real Energy # reg_block[38] = Phase A Exported Real Energy # reg_block[40] = Phase B Exported Real Energy # reg_block[42] = Phase C Exported Real Energy # reg_block[44] = Total Imported Real Energy # reg_block[46] = Phase A Imported Real Energy # reg_block[48] = Phase B Imported Real Energy # reg_block[50] = Phase C Imported Real Energy # reg_block[52] = Real Energy scale factor # reg_block[53] = Total Exported Real Energy # reg_block[55] = Phase A Exported Real Energy # reg_block[57] = Phase B Exported Real Energy # reg_block[59] = Phase C Exported Real Energy # reg_block[61] = Total Imported Real Energy # reg_block[63] = Phase A Imported Real Energy # reg_block[65] = Phase B Imported Real Energy # reg_block[67] = Phase C Imported Real Energy # reg_block[69] = Real Energy scale factor logger.debug(f'meter reg_block: {str(reg_block)}') # Set the Label to use for the Meter Metrics for Prometheus metriclabel = dictMeterLabel[x - 1] # Clear data from inverter, otherwise we publish that again! datapoint = { 'measurement': 'SolarEdge', 'tags': { 'meter': dictMeterLabel[x - 1] }, 'fields': {} } data = BinaryPayloadDecoder.fromRegisters( reg_block, byteorder=Endian.Big, wordorder=Endian.Big) # SunSpec DID # Register 40188 fooVal = trunc_float(data.decode_16bit_uint()) fooName = 'M_SunSpec_DID' if fooVal < 65535: dictM[fooName] = fooVal else: dictM[fooName] = 0.0 # SunSpec Length # Register 40070 fooVal = trunc_float(data.decode_16bit_uint()) fooName = 'M_SunSpec_Length' if fooVal < 65535: dictM[fooName] = fooVal else: dictM[fooName] = 0.0 # AC Current data.skip_bytes(8) # Register 40194 scalefactor = 10**data.decode_16bit_int() data.skip_bytes(-10) # Register 40190-40193 fooVal = trunc_float(data.decode_16bit_int()) fooName = 'M_AC_Current' if fooVal < 32768: dictM[fooName] = fooVal * scalefactor else: dictM[fooName] = 0.0 fooVal = trunc_float(data.decode_16bit_int()) fooName = 'M_AC_CurrentA' if fooVal < 32768: dictM[fooName] = fooVal * scalefactor else: dictM[fooName] = 0.0 fooVal = trunc_float(data.decode_16bit_int()) fooName = 'M_AC_CurrentB' if fooVal < 32768: dictM[fooName] = fooVal * scalefactor else: dictM[fooName] = 0.0 fooVal = trunc_float(data.decode_16bit_int()) fooName = 'M_AC_CurrentC' if fooVal < 32768: dictM[fooName] = fooVal * scalefactor else: dictM[fooName] = 0.0 # AC Voltage data.skip_bytes(18) # Register 40203 scalefactor = 10**data.decode_16bit_int() data.skip_bytes(-18) # Register 40195-40202 fooVal = trunc_float(data.decode_16bit_int()) fooName = 'M_AC_VoltageLN' if fooVal < 32768: dictM[fooName] = fooVal * scalefactor else: dictM[fooName] = 0.0 fooVal = trunc_float(data.decode_16bit_int()) fooName = 'M_AC_VoltageAN' if fooVal < 32768: dictM[fooName] = fooVal * scalefactor else: dictM[fooName] = 0.0 fooVal = trunc_float(data.decode_16bit_int()) fooName = 'M_AC_VoltageBN' if fooVal < 32768: dictM[fooName] = fooVal * scalefactor else: dictM[fooName] = 0.0 fooVal = trunc_float(data.decode_16bit_int()) fooName = 'M_AC_VoltageCN' if fooVal < 32768: dictM[fooName] = fooVal * scalefactor else: dictM[fooName] = 0.0 fooVal = trunc_float(data.decode_16bit_int()) fooName = 'M_AC_VoltageLL' if fooVal < 32768: dictM[fooName] = fooVal * scalefactor else: dictM[fooName] = 0.0 fooVal = trunc_float(data.decode_16bit_int()) fooName = 'M_AC_VoltageAB' if fooVal < 32768: dictM[fooName] = fooVal * scalefactor else: dictM[fooName] = 0.0 fooVal = trunc_float(data.decode_16bit_int()) fooName = 'M_AC_VoltageBC' if fooVal < 32768: dictM[fooName] = fooVal * scalefactor else: dictM[fooName] = 0.0 fooVal = trunc_float(data.decode_16bit_int()) fooName = 'M_AC_VoltageCA' if fooVal < 32768: dictM[fooName] = fooVal * scalefactor else: dictM[fooName] = 0.0 # AC Frequency data.skip_bytes(4) # Register 40205 scalefactor = 10**data.decode_16bit_int() data.skip_bytes(-4) # Register 40204 fooVal = trunc_float(data.decode_16bit_int()) fooName = 'M_AC_Frequency' if fooVal < 32768: dictM[fooName] = fooVal * scalefactor else: dictM[fooName] = 0.0 # AC Real Power data.skip_bytes(10) # Register 40210 scalefactor = 10**data.decode_16bit_int() data.skip_bytes(-10) # Register 40206-40209 fooVal = trunc_float(data.decode_16bit_int()) fooName = 'M_AC_Power' if fooVal < 32768: dictM[fooName] = fooVal * scalefactor else: dictM[fooName] = 0.0 fooVal = trunc_float(data.decode_16bit_int()) fooName = 'M_AC_Power_A' if fooVal < 32768: dictM[fooName] = fooVal * scalefactor else: dictM[fooName] = 0.0 fooVal = trunc_float(data.decode_16bit_int()) fooName = 'M_AC_Power_B' if fooVal < 32768: dictM[fooName] = fooVal * scalefactor else: dictM[fooName] = 0.0 fooVal = trunc_float(data.decode_16bit_int()) fooName = 'M_AC_Power_C' if fooVal < 32768: dictM[fooName] = fooVal * scalefactor else: dictM[fooName] = 0.0 # AC Apparent Power data.skip_bytes(10) # Register 40215 scalefactor = 10**data.decode_16bit_int() data.skip_bytes(-10) # Register 40211-40214 fooVal = trunc_float(data.decode_16bit_int()) fooName = 'M_AC_VA' if fooVal < 32768: dictM[fooName] = fooVal * scalefactor else: dictM[fooName] = 0.0 fooVal = trunc_float(data.decode_16bit_int()) fooName = 'M_AC_VA_A' if fooVal < 32768: dictM[fooName] = fooVal * scalefactor else: dictM[fooName] = 0.0 fooVal = trunc_float(data.decode_16bit_int()) fooName = 'M_AC_VA_B' if fooVal < 32768: dictM[fooName] = fooVal * scalefactor else: dictM[fooName] = 0.0 fooVal = trunc_float(data.decode_16bit_int()) fooName = 'M_AC_VA_C' if fooVal < 32768: dictM[fooName] = fooVal * scalefactor else: dictM[fooName] = 0.0 # AC Reactive Power data.skip_bytes(10) # Register 40220 scalefactor = 10**data.decode_16bit_int() data.skip_bytes(-10) # Register 40216-40219 fooVal = trunc_float(data.decode_16bit_int()) fooName = 'M_AC_VAR' if fooVal < 32768: dictM[fooName] = fooVal * scalefactor else: dictM[fooName] = 0.0 fooVal = trunc_float(data.decode_16bit_int()) fooName = 'M_AC_VAR_A' if fooVal < 32768: dictM[fooName] = fooVal * scalefactor else: dictM[fooName] = 0.0 fooVal = trunc_float(data.decode_16bit_int()) fooName = 'M_AC_VAR_B' if fooVal < 32768: dictM[fooName] = fooVal * scalefactor else: dictM[fooName] = 0.0 fooVal = trunc_float(data.decode_16bit_int()) fooName = 'M_AC_VAR_C' if fooVal < 32768: dictM[fooName] = fooVal * scalefactor else: dictM[fooName] = 0.0 # AC Power Factor data.skip_bytes(10) # Register 40225 scalefactor = 10**data.decode_16bit_int() data.skip_bytes(-10) # Register 40221-40224 fooVal = trunc_float(data.decode_16bit_int()) fooName = 'M_AC_PF' if fooVal < 32768: dictM[fooName] = fooVal * scalefactor else: dictM[fooName] = 0.0 fooVal = trunc_float(data.decode_16bit_int()) fooName = 'M_AC_PF_A' if fooVal < 32768: dictM[fooName] = fooVal * scalefactor else: dictM[fooName] = 0.0 fooVal = trunc_float(data.decode_16bit_int()) fooName = 'M_AC_PF_B' if fooVal < 32768: dictM[fooName] = fooVal * scalefactor else: dictM[fooName] = 0.0 fooVal = trunc_float(data.decode_16bit_int()) fooName = 'M_AC_PF_C' if fooVal < 32768: dictM[fooName] = fooVal * scalefactor else: dictM[fooName] = 0.0 # Accumulated AC Real Energy data.skip_bytes(34) # Register 40242 scalefactor = 10**data.decode_16bit_int() data.skip_bytes(-34) # Register 40226-40240 fooVal = trunc_float(data.decode_32bit_uint()) fooName = 'M_Exported' if fooVal < 4294967295: dictM[fooName] = fooVal * scalefactor else: dictM[fooName] = 0.0 fooVal = trunc_float(data.decode_32bit_uint()) fooName = 'M_Exported_A' if fooVal < 4294967295: dictM[fooName] = fooVal * scalefactor else: dictM[fooName] = 0.0 fooVal = trunc_float(data.decode_32bit_uint()) fooName = 'M_Exported_B' if fooVal < 4294967295: dictM[fooName] = fooVal * scalefactor else: dictM[fooName] = 0.0 fooVal = trunc_float(data.decode_32bit_uint()) fooName = 'M_Exported_C' if fooVal < 4294967295: dictM[fooName] = fooVal * scalefactor else: dictM[fooName] = 0.0 fooVal = trunc_float(data.decode_32bit_uint()) fooName = 'M_Imported' if fooVal < 4294967295: dictM[fooName] = fooVal * scalefactor else: dictM[fooName] = 0.0 fooVal = trunc_float(data.decode_32bit_uint()) fooName = 'M_Imported_A' if fooVal < 4294967295: dictM[fooName] = fooVal * scalefactor else: dictM[fooName] = 0.0 fooVal = trunc_float(data.decode_32bit_uint()) fooName = 'M_Imported_B' if fooVal < 4294967295: dictM[fooName] = fooVal * scalefactor else: dictM[fooName] = 0.0 fooVal = trunc_float(data.decode_32bit_uint()) fooName = 'M_Imported_C' if fooVal < 4294967295: dictM[fooName] = fooVal * scalefactor else: dictM[fooName] = 0.0 # Accumulated AC Apparent Energy #logger.debug(f'Apparent Energy SF: {str(np.int16(reg_block[69]))}') #scalefactor = np.float_power(10,np.int16(reg_block[69])) #datapoint['fields']['M_Exported_VA'] = trunc_float(((reg_block[53] << 16) + reg_block[54]) * scalefactor) #datapoint['fields']['M_Exported_VA_A'] = trunc_float(((reg_block[55] << 16) + reg_block[56]) * scalefactor) #datapoint['fields']['M_Exported_VA_B'] = trunc_float(((reg_block[57] << 16) + reg_block[58]) * scalefactor) #datapoint['fields']['M_Exported_VA_C'] = trunc_float(((reg_block[59] << 16) + reg_block[60]) * scalefactor) #datapoint['fields']['M_Imported_VA'] = trunc_float(((reg_block[61] << 16) + reg_block[62]) * scalefactor) #datapoint['fields']['M_Imported_VA_A'] = trunc_float(((reg_block[63] << 16) + reg_block[64]) * scalefactor) #datapoint['fields']['M_Imported_VA_B'] = trunc_float(((reg_block[65] << 16) + reg_block[66]) * scalefactor) #datapoint['fields']['M_Imported_VA_C'] = trunc_float(((reg_block[67] << 16) + reg_block[68]) * scalefactor) # Add the ScaleFactor elements dictM['M_AC_Current_SF'] = 0.0 dictM['M_AC_Voltage_SF'] = 0.0 dictM['M_AC_Frequency_SF'] = 0.0 dictM['M_AC_Power_SF'] = 0.0 dictM['M_AC_VA_SF'] = 0.0 dictM['M_AC_VAR_SF'] = 0.0 dictM['M_AC_PF_SF'] = 0.0 dictM['M_Energy_W_SF'] = 0.0 publish_metrics(dictM, 'meter', metriclabel, x, legacysupport) datapoint['time'] = str(datetime.datetime.utcnow().replace( tzinfo=datetime.timezone.utc).isoformat()) logger.debug(f'Meter: {metriclabel}') for j, k in dictM.items(): logger.debug(f' {j}: {k}') logger.debug(f'Writing to Influx: {str(datapoint)}') await solar_client.write(datapoint) else: # Error during data receive if client.last_error() == 2: logger.error( f'Failed to connect to SolarEdge inverter {client.host()}!' ) elif client.last_error() == 3 or client.last_error() == 4: logger.error('Send or receive error!') elif client.last_error() == 5: logger.error( 'Timeout during send or receive operation!') await asyncio.sleep(period) except InfluxDBWriteError as e: logger.error(f'Failed to write to InfluxDb: {e}') except IOError as e: logger.error(f'I/O exception during operation: {e}') except Exception as e: logger.error(f'Unhandled exception: {e}') await asyncio.sleep(period)
def onHeartbeat(self): #Domoticz.Log("onHeartbeat called") # Wich serial port settings to use? if (Parameters["Mode3"] == "S1B7PN"): StopBits, ByteSize, Parity = 1, 7, "N" if (Parameters["Mode3"] == "S1B7PE"): StopBits, ByteSize, Parity = 1, 7, "E" if (Parameters["Mode3"] == "S1B7PO"): StopBits, ByteSize, Parity = 1, 7, "O" if (Parameters["Mode3"] == "S1B8PN"): StopBits, ByteSize, Parity = 1, 8, "N" if (Parameters["Mode3"] == "S1B8PE"): StopBits, ByteSize, Parity = 1, 8, "E" if (Parameters["Mode3"] == "S1B8PO"): StopBits, ByteSize, Parity = 1, 8, "O" if (Parameters["Mode3"] == "S2B7PN"): StopBits, ByteSize, Parity = 2, 7, "N" if (Parameters["Mode3"] == "S2B7PE"): StopBits, ByteSize, Parity = 2, 7, "E" if (Parameters["Mode3"] == "S2B7PO"): StopBits, ByteSize, Parity = 2, 7, "O" if (Parameters["Mode3"] == "S2B8PN"): StopBits, ByteSize, Parity = 2, 8, "N" if (Parameters["Mode3"] == "S2B8PE"): StopBits, ByteSize, Parity = 2, 8, "E" if (Parameters["Mode3"] == "S2B8PO"): StopBits, ByteSize, Parity = 2, 8, "O" # How many registers to read (depending on data type)? registercount = 1 # Default if (Parameters["Mode6"] == "noco"): registercount = 1 if (Parameters["Mode6"] == "int8"): registercount = 1 if (Parameters["Mode6"] == "int16"): registercount = 1 if (Parameters["Mode6"] == "int32"): registercount = 2 if (Parameters["Mode6"] == "int64"): registercount = 4 if (Parameters["Mode6"] == "uint8"): registercount = 1 if (Parameters["Mode6"] == "uint16"): registercount = 1 if (Parameters["Mode6"] == "uint32"): registercount = 2 if (Parameters["Mode6"] == "uint64"): registercount = 4 if (Parameters["Mode6"] == "float32"): registercount = 2 if (Parameters["Mode6"] == "float64"): registercount = 4 if (Parameters["Mode6"] == "string2"): registercount = 2 if (Parameters["Mode6"] == "string4"): registercount = 4 if (Parameters["Mode6"] == "string6"): registercount = 6 if (Parameters["Mode6"] == "string8"): registercount = 8 ################################### # pymodbus: RTU / ASCII ################################### if (Parameters["Mode1"] == "rtu" or Parameters["Mode1"] == "ascii"): Domoticz.Debug("MODBUS DEBUG USB SERIAL HW - Port=" + Parameters["SerialPort"] + ", BaudRate=" + Parameters["Mode2"] + ", StopBits=" + str(StopBits) + ", ByteSize=" + str(ByteSize) + " Parity=" + Parity) Domoticz.Debug("MODBUS DEBUG USB SERIAL CMD - Method=" + Parameters["Mode1"] + ", Address=" + Parameters["Address"] + ", Register=" + Parameters["Password"] + ", Function=" + Parameters["Username"] + ", Data type=" + Parameters["Mode6"]) try: client = ModbusSerialClient(method=Parameters["Mode1"], port=Parameters["SerialPort"], stopbits=StopBits, bytesize=ByteSize, parity=Parity, baudrate=int(Parameters["Mode2"]), timeout=1, retries=2) except: Domoticz.Log("Error opening Serial interface on " + Parameters["SerialPort"]) Devices[1].Update(0, "0") # Update device in Domoticz ################################### # pymodbus: RTU over TCP ################################### if (Parameters["Mode1"] == "rtutcp"): Domoticz.Debug("MODBUS DEBUG TCP CMD - Method=" + Parameters["Mode1"] + ", Address=" + Parameters["Address"] + ", Port=" + Parameters["Port"] + ", Register=" + Parameters["Password"] + ", Data type=" + Parameters["Mode6"]) try: client = ModbusTcpClient(host=Parameters["Address"], port=int(Parameters["Port"]), timeout=5) except: Domoticz.Log("Error opening TCP interface on address: " + Parameters["Address"]) Devices[1].Update(0, "0") # Update device in Domoticz ################################### # pymodbusTCP: TCP/IP ################################### if (Parameters["Mode1"] == "tcpip"): Domoticz.Debug("MODBUS DEBUG TCP CMD - Method=" + Parameters["Mode1"] + ", Address=" + Parameters["Address"] + ", Port=" + Parameters["Port"] + ", Register=" + Parameters["Password"] + ", Data type=" + Parameters["Mode6"]) try: client = ModbusClient(host=Parameters["Address"], port=int(Parameters["Port"]), auto_open=True, auto_close=True, timeout=5) except: Domoticz.Log("Error opening TCP/IP interface on address: " + Parameters["Address"]) Devices[1].Update(0, "0") # Update device in Domoticz ################################### # pymodbus section ################################### if (Parameters["Mode1"] == "rtu" or Parameters["Mode1"] == "ascii" or Parameters["Mode1"] == "rtutcp"): try: # Which function to execute? RTU/ASCII/RTU over TCP if (Parameters["Username"] == "1"): data = client.read_coils(int(Parameters["Password"]), registercount, unit=int(Parameters["Address"])) if (Parameters["Username"] == "2"): data = client.read_discrete_inputs( int(Parameters["Password"]), registercount, unit=int(Parameters["Address"])) if (Parameters["Username"] == "3"): data = client.read_holding_registers( int(Parameters["Password"]), registercount, unit=int(Parameters["Address"])) if (Parameters["Username"] == "4"): data = client.read_input_registers( int(Parameters["Password"]), registercount, unit=int(Parameters["Address"])) Domoticz.Debug("MODBUS DEBUG RESPONSE: " + str(data)) except: Domoticz.Log( "Modbus error communicating! (RTU/ASCII/RTU over TCP), check your settings!" ) Devices[1].Update(0, "0") # Update device to OFF in Domoticz try: # How to decode the input? decoder = BinaryPayloadDecoder.fromRegisters( data.registers, byteorder=Endian.Big, wordorder=Endian.Big) if (Parameters["Mode6"] == "noco"): value = data.registers[0] if (Parameters["Mode6"] == "int8"): value = decoder.decode_8bit_int() if (Parameters["Mode6"] == "int16"): value = decoder.decode_16bit_int() if (Parameters["Mode6"] == "int32"): value = decoder.decode_32bit_int() if (Parameters["Mode6"] == "int64"): value = decoder.decode_64bit_int() if (Parameters["Mode6"] == "uint8"): value = decoder.decode_8bit_uint() if (Parameters["Mode6"] == "uint16"): value = decoder.decode_16bit_uint() if (Parameters["Mode6"] == "uint32"): value = decoder.decode_32bit_uint() if (Parameters["Mode6"] == "uint64"): value = decoder.decode_64bit_uint() if (Parameters["Mode6"] == "float32"): value = decoder.decode_32bit_float() if (Parameters["Mode6"] == "float64"): value = decoder.decode_64bit_float() if (Parameters["Mode6"] == "string2"): value = decoder.decode_string(2) if (Parameters["Mode6"] == "string4"): value = decoder.decode_string(4) if (Parameters["Mode6"] == "string6"): value = decoder.decode_string(6) if (Parameters["Mode6"] == "string8"): value = decoder.decode_string(8) Domoticz.Debug("MODBUS DEBUG VALUE: " + str(value)) # Divide the value (decimal)? if (Parameters["Mode5"] == "div0"): value = str(value) if (Parameters["Mode5"] == "div10"): value = str(round(value / 10, 1)) if (Parameters["Mode5"] == "div100"): value = str(round(value / 100, 2)) if (Parameters["Mode5"] == "div1000"): value = str(round(value / 1000, 3)) Devices[1].Update(0, value) # Update value in Domoticz except: Domoticz.Log( "Modbus error decoding or recieved no data (RTU/ASCII/RTU over TCP)!, check your settings!" ) Devices[1].Update(0, "0") # Update value in Domoticz ################################### # pymodbusTCP section ################################### if (Parameters["Mode1"] == "tcpip"): try: # Which function to execute? TCP/IP if (Parameters["Username"] == "1"): data = client.read_coils(int(Parameters["Password"]), registercount) if (Parameters["Username"] == "2"): data = client.read_discrete_inputs( int(Parameters["Password"]), registercount) if (Parameters["Username"] == "3"): data = client.read_holding_registers( int(Parameters["Password"]), registercount) if (Parameters["Username"] == "4"): data = client.read_input_registers( int(Parameters["Password"]), registercount) Domoticz.Debug("MODBUS DEBUG RESPONSE: " + str(data[0])) except: Domoticz.Log( "Modbus error communicating! (TCP/IP), check your settings!" ) Devices[1].Update(0, "0") # Update device to OFF in Domoticz try: # How to decode the input? decoder = BinaryPayloadDecoder.fromRegisters( data, byteorder=Endian.Big, wordorder=Endian.Big) if (Parameters["Mode6"] == "noco"): value = data[0] if (Parameters["Mode6"] == "int8"): value = decoder.decode_8bit_int() if (Parameters["Mode6"] == "int16"): value = decoder.decode_16bit_int() if (Parameters["Mode6"] == "int32"): value = decoder.decode_32bit_int() if (Parameters["Mode6"] == "int64"): value = decoder.decode_64bit_int() if (Parameters["Mode6"] == "uint8"): value = decoder.decode_8bit_uint() if (Parameters["Mode6"] == "uint16"): value = decoder.decode_16bit_uint() if (Parameters["Mode6"] == "uint32"): value = decoder.decode_32bit_uint() if (Parameters["Mode6"] == "uint64"): value = decoder.decode_64bit_uint() if (Parameters["Mode6"] == "float32"): value = decoder.decode_32bit_float() if (Parameters["Mode6"] == "float64"): value = decoder.decode_64bit_float() if (Parameters["Mode6"] == "string2"): value = decoder.decode_string(2) if (Parameters["Mode6"] == "string4"): value = decoder.decode_string(4) if (Parameters["Mode6"] == "string6"): value = decoder.decode_string(6) if (Parameters["Mode6"] == "string8"): value = decoder.decode_string(8) Domoticz.Debug("MODBUS DEBUG VALUE: " + str(value)) # Divide the value (decimal)? if (Parameters["Mode5"] == "div0"): value = str(value) if (Parameters["Mode5"] == "div10"): value = str(round(value / 10, 1)) if (Parameters["Mode5"] == "div100"): value = str(round(value / 100, 2)) if (Parameters["Mode5"] == "div1000"): value = str(round(value / 1000, 3)) Devices[1].Update(0, value) # Update value in Domoticz except: Domoticz.Log( "Modbus error decoding or recieved no data (TCP/IP)!, check your settings!" ) Devices[1].Update(0, "0") # Update value in Domoticz
def get_pv_data(host, port, modbusid, registers): client = ModbusClient(host=host, port=port) # connects even within if clause if client.connect() == False: print('Modbus Connection Error: Could not connect to', host) return None data = {} ## empty data store for current values for myreg in registers: ## if the connection is somehow not possible (e.g. target not responding) # show a error message instead of excepting and stopping try: addr = int(myreg[0]) dt = myreg[1] received = client.read_input_registers(address=addr, count=modbusdatatype[dt], unit=int(modbusid)) except Exception as e: thisdate = str(datetime.datetime.now()).partition('.')[0] thiserrormessage = thisdate + 'Modbus: Connection not possible. Check settings or connection.' print(thiserrormessage) print(traceback.format_exc()) return None ## prevent further execution of this function name = myreg[3] message = BinaryPayloadDecoder.fromRegisters(received.registers, byteorder=Endian.Big, wordorder=Endian.Big) ## provide the correct result depending on the defined datatype if myreg[1] == 'S32': interpreted = message.decode_32bit_int() elif myreg[1] == 'U32': interpreted = message.decode_32bit_uint() elif myreg[1] == 'U64': interpreted = message.decode_64bit_uint() elif myreg[1] == 'STR32': interpreted = message.decode_string(32) elif myreg[1] == 'S16': interpreted = message.decode_16bit_int() elif myreg[1] == 'U16': interpreted = message.decode_16bit_uint() else: ## if no data type is defined do raw interpretation of the delivered data interpreted = message.decode_16bit_uint() ## check for "None" data before doing anything else if ((interpreted == MIN_SIGNED) or (interpreted == MAX_UNSIGNED)): value = None else: ## put the data with correct formatting into the data table if myreg[2] == 'FIX3': value = float(interpreted) / 1000 elif myreg[2] == 'FIX2': value = float(interpreted) / 100 elif myreg[2] == 'FIX1': value = float(interpreted) / 10 elif myreg[2] == 'UTF8': value = str(interpreted, 'UTF-8', errors='ignore').rstrip("\x00") elif myreg[2] == 'ENUM': e = pvenums.get(name, {}) value = e.get(interpreted, str(interpreted)) else: value = interpreted data[name] = value client.close() return data
def onStart(self): self.uTEa = 1 self.uTEb = 6 self.uTU1 = 3 self.uTU2 = 4 self.uPowerCur = 8 self.uControlMode = 9 #self.uPowerReq = 10 self.uModeCur = 7 #self.uModeReq = 11 self.uIN1 = 12 self.uIN2 = 13 self.uD1 = 14 self.uD2 = 15 self.uD3 = 16 self.uD4 = 17 self.uZVT = 18 self.uBypass = 19 self.uAlarmFilter = 20 self.uHeatingSeason = 21 self.uNightlyCooling = 22 # CMDs: # init: pygettext3.8 -d base -o locales/base.pot plugin.py # init: locales/cs/LC_MESSAGES# msgfmt -o base.mo base # update: xgettext -d base --from-code utf-8 -s -j -o locales/base.pot plugin.py # update: msgmerge --update locales/cs/LC_MESSAGES/base.po locales/base.pot # update: locales/cs/LC_MESSAGES# msgfmt -o base.mo base translate = gettext.translation('base', localedir='plugins/atrea/locales', fallback=True, languages=['cs']) translate.install() self._ = translate.gettext if (Parameters["SerialPort"] == "1"): Domoticz.Debugging(1) DumpConfigToLog() Domoticz.Debug("Domoticz language: " + Settings["Language"]) # Domoticz language: cs Domoticz.Debug("***** NOTIFICATION: Debug is enabled!") else: Domoticz.Debugging(0) Domoticz.Debug("onStart called") Domoticz.Heartbeat(int(10)) # Device pollrate (heartbeat) : 10s self.TCP_IP = Parameters["Address"] self.TCP_PORT = 502 self.labelIN1 = str(Parameters["Mode1"]) self.labelIN2 = str(Parameters["Mode2"]) self.labelD1 = str(Parameters["Mode3"]) self.labelD2 = str(Parameters["Mode4"]) self.labelD3 = str(Parameters["Mode5"]) self.labelD4 = str(Parameters["Mode6"]) self.atreaMinPower = 60 # Get Atrea unit info from Modbus.. 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: # 1 = 180 EC, 2 = 190 ECV, 3 = 370 EC, 4 = 390ECV, 5 = 510 EC, 6 = 520 ECV self.atreaType = BinaryPayloadDecoder.fromRegisters( client.read_holding_registers(509, 1), byteorder=Endian.Big, wordorder=Endian.Big).decode_16bit_int() if (self.atreaType == 1): self.atreaTypeStr = "180 EC" self.atreaNominalPower = 180 elif (self.atreaType == 2): self.atreaTypeStr = "190 ECV" self.atreaNominalPower = 180 elif (self.atreaType == 3): self.atreaTypeStr = "370 EC" self.atreaNominalPower = 370 elif (self.atreaType == 4): self.atreaTypeStr = "390 ECV" self.atreaNominalPower = 370 elif (self.atreaType == 5): self.atreaTypeStr = "510 EC" self.atreaNominalPower = 510 elif (self.atreaType == 6): self.atreaTypeStr = "520 ECV" self.atreaNominalPower = 510 Domoticz.Debug("Atrea type: " + self.atreaTypeStr) self.atreaLimitedPower = BinaryPayloadDecoder.fromRegisters( client.read_holding_registers(516, 1), byteorder=Endian.Big, wordorder=Endian.Big).decode_16bit_int() self.atreaMaxPower = self.atreaLimitedPower / 100 * self.atreaNominalPower Domoticz.Debug("Atrea limited power at: " + str(self.atreaLimitedPower) + "% = " + str(self.atreaMaxPower) + "m3") self.atreaIN1type = BinaryPayloadDecoder.fromRegisters( client.read_holding_registers(705, 1), byteorder=Endian.Big, wordorder=Endian.Big).decode_16bit_int() self.atreaIN2type = BinaryPayloadDecoder.fromRegisters( client.read_holding_registers(704, 1), byteorder=Endian.Big, wordorder=Endian.Big).decode_16bit_int() self.atreaZVT = BinaryPayloadDecoder.fromRegisters( client.read_holding_registers(514, 1), byteorder=Endian.Big, wordorder=Endian.Big).decode_16bit_int() except: Domoticz.Error("Modbus TCP/IP communication error. Check it out!") if self.uTEa not in Devices: Domoticz.Device(Unit=self.uTEa, DeviceID="TEa", Name=self._("Outside temperature"), TypeName="Temperature", Used=1).Create() if self.uTEb not in Devices: Domoticz.Device(Unit=self.uTEb, DeviceID="TEb", Name=self._("Exhaust air before recuperation"), TypeName="Temperature", Used=1).Create() if self.uTU1 not in Devices: Domoticz.Device(Unit=self.uTU1, DeviceID="TU1", Name=self._("Fresh air after recuperation"), TypeName="Temperature", Used=1).Create() if self.uTU2 not in Devices: Domoticz.Device(Unit=self.uTU2, DeviceID="TU2", Name=self._("Exhaust air after recuperation"), TypeName="Temperature", Used=1).Create() if self.labelD1 != "" and self.labelD1 != "D1" and self.uD1 not in Devices: Domoticz.Device(Unit=self.uD1, DeviceID="D1", Name=self.labelD1, TypeName="Contact", Used=1).Create() if self.labelD2 != "" and self.labelD2 != "D2" and self.uD2 not in Devices: Domoticz.Device(Unit=self.uD2, DeviceID="D2", Name=self.labelD2, TypeName="Contact", Used=1).Create() if self.labelD3 != "" and self.labelD3 != "D3" and self.uD3 not in Devices: Domoticz.Device(Unit=self.uD3, DeviceID="D3", Name=self.labelD3, TypeName="Contact", Used=1).Create() if self.labelD4 != "" and self.labelD4 != "D4" and self.uD4 not in Devices: Domoticz.Device(Unit=self.uD4, DeviceID="D4", Name=self.labelD4, TypeName="Contact", Used=1).Create() if self.atreaZVT > 0 and self.uZVT not in Devices: Domoticz.Device(Unit=self.uZVT, DeviceID="ZVT", Name=self._("Ground heat exchanger"), TypeName="Contact", Used=1).Create() if self.uBypass not in Devices: Domoticz.Device(Unit=self.uBypass, DeviceID="BYPASS", Name=self._("Bypass flap"), TypeName="Contact", Used=1).Create() if self.uAlarmFilter not in Devices: Domoticz.Device(Unit=self.uAlarmFilter, DeviceID="AlarmFilter", Name=self._("Filter change"), Type=243, Subtype=22, Used=1).Create() if self.uHeatingSeason not in Devices: Domoticz.Device(Unit=self.uHeatingSeason, DeviceID="HeatingSeason", Name=self._("Heating season"), Type=244, Subtype=73, Switchtype=0, Image=10, Used=1).Create() if self.uNightlyCooling not in Devices: Domoticz.Device(Unit=self.uNightlyCooling, DeviceID="NightlyCooling", Name=self._("Nightly cooling"), Type=244, Subtype=73, Switchtype=0, Image=16, Used=1).Create() if self.labelIN1 != "" and self.labelIN1 != "IN1" and self.uIN1 not in Devices: # H00705 Režim vstupu IN1: 0 = Kontakt, 1 = Analog (0-10V) if self.atreaIN1type == 0: TypeName = "Contact" elif self.atreaIN1type == 1: # analog if re.search('vlhko', self.labelIN1, re.IGNORECASE) or re.search( 'humid', self.labelIN1, re.IGNORECASE): TypeName = "Humidity" elif re.search('teplot', self.labelIN1, re.IGNORECASE) or re.search( 'temp', self.labelIN1, re.IGNORECASE): TypeName = "Temperature" else: TypeName = "Percentage" Domoticz.Device(Unit=self.uIN1, DeviceID="IN1", Name=self.labelIN1, TypeName=TypeName, Used=1).Create() if self.labelIN2 != "" and self.labelIN2 != "IN2" and self.uIN2 not in Devices: # H00704 Režim vstupu IN2: 0 = Kontakt, 1 = Analog (0-10V), 2 = Teplota if self.atreaIN2type == 0: TypeName = "Contact" elif self.atreaIN2type == 2: TypeName = "Temperature" elif self.atreaIN2type == 1: # analog if re.search('vlhko', self.labelIN2, re.IGNORECASE) or re.search( 'humid', self.labelIN2, re.IGNORECASE): TypeName = "Humidity" elif re.search('teplot', self.labelIN2, re.IGNORECASE) or re.search( 'temp', self.labelIN2, re.IGNORECASE): TypeName = "Temperature" else: TypeName = "Percentage" Domoticz.Device(Unit=self.uIN2, DeviceID="IN2", Name=self.labelIN2, TypeName=TypeName, Used=1).Create() o1 = 60 + (self.atreaMaxPower - 60) / 8 o2 = o1 + (self.atreaMaxPower - 60) / 8 o3 = o2 + (self.atreaMaxPower - 60) / 8 o4 = o3 + (self.atreaMaxPower - 60) / 8 o5 = o4 + (self.atreaMaxPower - 60) / 4 self.oPower = { "LevelNames": "0|60|" + str(int(o1)) + "|" + str(int(o2)) + "|" + str(int(o3)) + "|" + str(int(o4)) + "|" + str(int(o5)) + "|" + str(int(self.atreaMaxPower)) } self.oControlMode = { "LevelNames": "|" + self._("Manual") + "|" + self._("Automatic") + "|" + self._("Temporary"), "LevelOffHidden": "true", "SelectorStyle": "1" } self.oModeReq = { "LevelNames": self._("Off") + "|" + self._("Periodic ventilation") + "|" + self._("Ventilation"), "SelectorStyle": "1" } self.oModeCur = { "LevelNames": self._("Off") + "|" + self._("Periodic ventilation") + "|" + self._("Ventilation") + "|" + self.labelIN1 + "|" + self.labelIN2 + "|" + self.labelD1 + "|" + self.labelD2 + "|" + self.labelD3 + "|" + self.labelD4 + "|" + self._("Rise") + "|" + self._("Rundown") + "|" + self._("Defrosting the recuperator"), "SelectorStyle": "1" } if self.uControlMode not in Devices: Domoticz.Device(Unit=self.uControlMode, DeviceID="ControlMode", Name=self._("Ventilation control mode"), Options=self.oControlMode, Type=244, Subtype=62, Switchtype=18, Image=9, Used=1).Create() if self.uPowerCur not in Devices: Domoticz.Device(Unit=self.uPowerCur, DeviceID="PowerCur", Name=self._("Current ventilation power"), Options=self.oPower, Type=244, Subtype=62, Switchtype=18, Image=7, Used=1).Create() #if self.uPowerReq not in Devices: Domoticz.Device(Unit=self.uPowerReq, DeviceID="PowerReq", Name=self._("Requested ventilation power"), Options=self.oPower, Type=244, Subtype=62, Switchtype=18, Image=7, Used=1).Create() if self.uModeCur not in Devices: Domoticz.Device(Unit=self.uModeCur, DeviceID="ModeCur", Name=self._("Current ventilation mode"), Options=self.oModeCur, Image=7, Type=244, Subtype=62, Switchtype=18, Used=1).Create() #if self.uModeReq not in Devices: Domoticz.Device(Unit=self.uModeReq, DeviceID="ModeReq", Name=self._("Requested ventilation mode"), Options=self.oModeReq, Image=7, Type=244, Subtype=62, Switchtype=18, Used=1).Create() return
def _refresh(self): start_time = time.time() try: # myCounter = 1 for pluggit_key in self._myTempReadDict: # logger.debug("Pluggit: ---------------------------------> Wir sind in der Refresh Schleife") values = self._modbusRegisterDic[pluggit_key] # logger.debug("Pluggit: Refresh Schleife: Inhalt von values ist {0}".format(values)) # 2015-01-07 23:53:08,296 DEBUG Pluggit Pluggit: Refresh Schleife: Inhalt von values ist 168 -- __init__.py:_refresh:158 item = self._myTempReadDict[pluggit_key] # logger.debug("Pluggit: Refresh Schleife: Inhalt von item ist {0}".format(item)) # 2015-01-07 23:53:08,316 DEBUG Pluggit Pluggit: Refresh Schleife: Inhalt von item ist pluggit.unitMode -- __init__.py:_refresh:160 #=======================================================# # read values from pluggit via modbus client registers #=======================================================# # logger.debug("Pluggit: ------------------------------------------> Wir sind vor dem Auslesen der Werte") registerValue = None registerValue = self._Pluggit.read_holding_registers(values, read_qty = 1).getRegister(0) # logger.debug("Pluggit: Read parameter '{0}' with register '{1}': Value is '{2}'".format(pluggit_key, values, registerValue)) # week program: possible values 0-10 if values == self._modbusRegisterDic['prmNumOfWeekProgram']: registerValue += 1 item(registerValue, 'Pluggit') # 2015-01-07 23:53:08,435 DEBUG Pluggit Item pluggit.unitMode = 8 via Pluggit None None -- item.py:__update:363 # logger.debug("Pluggit: Week Program Number: {0}".format(registerValue)) # 2015-01-07 23:53:08,422 DEBUG Pluggit Pluggit: Active Unit Mode: Week program -- __init__.py:_refresh:177 # active unit mode if values == self._modbusRegisterDic['prmRamIdxUnitMode'] and registerValue == 8: # logger.debug("Pluggit: Active Unit Mode: Week program") item('Woche', 'Pluggit') if values == self._modbusRegisterDic['prmRamIdxUnitMode'] and registerValue == 4: # logger.debug("Pluggit: Active Unit Mode: Manual") item('Manuell', 'Pluggit') # fan speed if values == self._modbusRegisterDic['prmRomIdxSpeedLevel']: # logger.debug("Pluggit: Fan Speed: {0}".format(registerValue)) item(registerValue, 'Pluggit') # remaining filter lifetime if values == self._modbusRegisterDic['prmFilterRemainingTime']: # logger.debug("Pluggit: Remaining filter lifetime: {0}".format(registerValue)) item(registerValue, 'Pluggit') # bypass state if values == self._modbusRegisterDic['prmRamIdxBypassActualState'] and registerValue == 255: # logger.debug("Pluggit: Bypass state: opened") item('geöffnet', 'Pluggit') if values == self._modbusRegisterDic['prmRamIdxBypassActualState'] and registerValue == 0: # logger.debug("Pluggit: Bypass state: closed") item('geschlossen', 'Pluggit') # Temperatures # Frischluft außen if values == self._modbusRegisterDic['prmRamIdxT1']: t1 = self._Pluggit.read_holding_registers(values, 2, unit=22) decodert1 = BinaryPayloadDecoder.fromRegisters(t1.registers, endian=Endian.Big) t1 = decodert1.decode_32bit_float() t1 = round(t1, 2) # logger.debug("Pluggit: Frischluft außen: {0:4.1f}".format(t1)) # logger.debug("Pluggit: Frischluft außen: {0}".format(t1)) item(t1, 'Pluggit') # Zuluft innen if values == self._modbusRegisterDic['prmRamIdxT2']: t2 = self._Pluggit.read_holding_registers(values, 2, unit=22) decodert2 = BinaryPayloadDecoder.fromRegisters(t2.registers, endian=Endian.Big) t2 = decodert2.decode_32bit_float() t2 = round(t2, 2) # logger.debug("Pluggit: Zuluft innen: {0:4.1f}".format(t2)) # logger.debug("Pluggit: Zuluft innen: {0}".format(t2)) item(t2, 'Pluggit') # Abluft innen if values == self._modbusRegisterDic['prmRamIdxT3']: t3 = self._Pluggit.read_holding_registers(values, 2, unit=22) decodert3 = BinaryPayloadDecoder.fromRegisters(t3.registers, endian=Endian.Big) t3 = decodert3.decode_32bit_float() t3 = round(t3, 2) # logger.debug("Pluggit: Abluft innen: {0:4.1f}".format(t3)) # logger.debug("Pluggit: Abluft innen: {0}".format(t3)) item(t3, 'Pluggit') # Fortluft außen if values == self._modbusRegisterDic['prmRamIdxT4']: t4 = self._Pluggit.read_holding_registers(values, 2, unit=22) decodert4 = BinaryPayloadDecoder.fromRegisters(t4.registers, endian=Endian.Big) t4 = decodert4.decode_32bit_float() t4 = round(t4, 2) # logger.debug("Pluggit: Fortluft außen: {0:4.1f}".format(t4)) # logger.debug("Pluggit: Fortluft außen: {0}".format(t4)) item(t4, 'Pluggit') # logger.debug("Pluggit: ------------------------------------------> Ende der Schleife vor sleep, Durchlauf Nr. {0}".format(myCounter)) time.sleep(0.1) # myCounter += 1 except Exception as e: logger.error("Pluggit: something went wrong in the refresh function: {0}".format(e)) return end_time = time.time() cycletime = end_time - start_time logger.debug("Pluggit: cycle took {0} seconds".format(cycletime))
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
def getDataDecoder(registers): return BinaryPayloadDecoder.fromRegisters( registers, byteorder=Endian.Big, wordorder=Endian.Little)
def run_binary_payload_ex(): # ----------------------------------------------------------------------- # # We are going to use a simple client to send our requests # ----------------------------------------------------------------------- # client = ModbusClient('127.0.0.1', port=5440) client.connect() # ----------------------------------------------------------------------- # # If you need to build a complex message to send, you can use the payload # builder to simplify the packing logic. # # Here we demonstrate packing a random payload layout, unpacked it looks # like the following: # # - a 8 byte string 'abcdefgh' # - a 32 bit float 22.34 # - a 16 bit unsigned int 0x1234 # - another 16 bit unsigned int 0x5678 # - an 8 bit int 0x12 # - an 8 bit bitstring [0,1,0,1,1,0,1,0] # - an 32 bit uint 0x12345678 # - an 32 bit signed int -0x1234 # - an 64 bit signed int 0x12345678 # The packing can also be applied to the word (wordorder) and bytes in each # word (byteorder) # The wordorder is applicable only for 32 and 64 bit values # Lets say we need to write a value 0x12345678 to a 32 bit register # The following combinations could be used to write the register # +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ # # Word Order - Big Byte Order - Big # word1 =0x1234 word2 = 0x5678 # Word Order - Big Byte Order - Little # word1 =0x3412 word2 = 0x7856 # Word Order - Little Byte Order - Big # word1 = 0x5678 word2 = 0x1234 # Word Order - Little Byte Order - Little # word1 =0x7856 word2 = 0x3412 # +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ # # ----------------------------------------------------------------------- # builder = BinaryPayloadBuilder(byteorder=Endian.Little, wordorder=Endian.Big) builder.add_string('abcdefgh') builder.add_32bit_float(22.34) builder.add_16bit_uint(0x1234) builder.add_16bit_uint(0x5678) builder.add_8bit_int(0x12) builder.add_bits([0, 1, 0, 1, 1, 0, 1, 0]) builder.add_32bit_uint(0x12345678) builder.add_32bit_int(-0x1234) builder.add_64bit_int(0x1234567890ABCDEF) payload = builder.build() address = 0 client.write_registers(address, payload, skip_encode=True, unit=1) # ----------------------------------------------------------------------- # # If you need to decode a collection of registers in a weird layout, the # payload decoder can help you as well. # # Here we demonstrate decoding a random register layout, unpacked it looks # like the following: # # - a 8 byte string 'abcdefgh' # - a 32 bit float 22.34 # - a 16 bit unsigned int 0x1234 # - another 16 bit unsigned int which we will ignore # - an 8 bit int 0x12 # - an 8 bit bitstring [0,1,0,1,1,0,1,0] # ----------------------------------------------------------------------- # address = 0x00 count = len(payload) result = client.read_holding_registers(address, count, unit=1) print("-" * 60) print("Registers") print("-" * 60) print(result.registers) print("\n") decoder = BinaryPayloadDecoder.fromRegisters(result.registers, byteorder=Endian.Little, wordorder=Endian.Big) decoded = { 'string': decoder.decode_string(8), 'float': decoder.decode_32bit_float(), '16uint': decoder.decode_16bit_uint(), 'ignored': decoder.skip_bytes(2), '8int': decoder.decode_8bit_int(), 'bits': decoder.decode_bits(), "32uints": decoder.decode_32bit_uint(), "32ints": decoder.decode_32bit_int(), "64ints": decoder.decode_64bit_int(), } print("-" * 60) print("Decoded Data") print("-" * 60) for name, value in iteritems(decoded): print("%s\t" % name, hex(value) if isinstance(value, int) else value) # ----------------------------------------------------------------------- # # close the client # ----------------------------------------------------------------------- # client.close()
now = datetime.datetime.now() write_data("Starting data logger "+ now.strftime("%Y-%m-%d %H:%M")+"\n\n") # diris_modbus = ModbusClient(diris_host) diris_modbus = ModbusClient(method='rtu', port=diris_port, timeout=1, baudrate=9600, parity='E') diris_modbus.connect() while True: rk = diris_modbus.read_holding_registers(50536,2,unit=diris_slave_id) active_p = BinaryPayloadDecoder.fromRegisters(rk.registers,endian=Endian.Big).decode_32bit_uint() active_p = float(active_p)/100 rk = diris_modbus.read_holding_registers(50538,2,unit=diris_slave_id) reactive_p = BinaryPayloadDecoder.fromRegisters(rk.registers,endian=Endian.Big).decode_32bit_uint() reactive_p = float(reactive_p)/100 rk = diris_modbus.read_holding_registers(50540,2,unit=diris_slave_id) apparent_p = BinaryPayloadDecoder.fromRegisters(rk.registers,endian=Endian.Big).decode_32bit_uint() apparent_p = float(apparent_p)/100 # decoder = BinaryPayloadDecoder.fromRegisters(rk.registers,endian=Endian.Big) # print decoder.decode_32bit_uint()
def update(self,client,Domoticz): value = "0" #Domoticz.Log("--> Device:"+self.device.Name+" Address="+str(self.UnitIdForIp)+", Register="+str(self.address)+", Function="+self.function+", Data type="+self.dataType+" Digits:"+str(self.digits)+" Method="+self.method) ################################### # pymodbus section ################################### if (self.method == "rtu" or self.method == "ascii" or self.method == "rtutcp"): Domoticz.Log("Start rtu read") try: # Which function to execute? RTU/ASCII/RTU over TCP or COM if (self.function == "1"): data = client.read_coils(self.address, self.registercount, unit=self.UnitIdForIpUnitIdForIp) elif (self.function == "2"): data = client.read_discrete_inputs(self.address, self.registercount, unit=self.UnitIdForIp) elif (self.function == "3"): data = client.read_holding_registers(self.address, self.registercount, unit=self.UnitIdForIp) elif (self.function == "4"): data = client.read_input_registers(self.address, self.registercount, unit=self.UnitIdForIp) else: Domoticz.Debug("No function selected: " + str(self.function)) return Domoticz.Log("MODBUS DEBUG RESPONSE: " + str(data.registers)) except Exception as e: Domoticz.Log("Modbus error communicating! (RTU/ASCII/RTU over TCP or COM), check your settings!"+repr(e)) data = 0 #self.device.Update(0, "0") # Update device to OFF in Domoticz ################################### # pymodbusTCP section ################################### else: #if (self.method == "tcpip"): #Domoticz.Log("Start tcp read") try: # Which function to execute? TCP/IP if (function == "1"): data = client.read_coils(self.address, self.registercount) if (function == "2"): data = client.read_discrete_inputs(self.address, self.registercount) if (function == "3"): data = client.read_holding_registers(self.address, self.registercount) if (function == "4"): data = client.read_input_registers(self.address, self.registercount) Domoticz.Log("MODBUS DEBUG RESPONSE: " + str(data)) except Exception as e: Domoticz.Log("Modbus error communicating! (TCP/IP), check your settings!"+repr(e)) data = 0 #self.device.Update(0, "0") # Update device to OFF in Domoticz #Domoticz.Log("Modbus Na read" ) if data: try: # How to decode the input? try: decoder = BinaryPayloadDecoder.fromRegisters(data, byteorder=self.byteorder, wordorder=self.wordorder) except: decoder = BinaryPayloadDecoder.fromRegisters(data.registers, byteorder=self.byteorder, wordorder=self.wordorder) if (self.dataType == "noco"): value = data if (self.dataType == "int8LSB"): ignored = decoder.skip_bytes(1) value = decoder.decode_8bit_int() if (self.dataType == "int8MSB"): value = decoder.decode_8bit_int() if (self.dataType == "int16"): value = decoder.decode_16bit_int() if (self.dataType == "int16s"): value = decoder.decode_16bit_int() if (self.dataType == "int32"): value = decoder.decode_32bit_int() if (self.dataType == "int64"): value = decoder.decode_64bit_int() if (self.dataType == "uint8LSB"): ignored = decoder.skip_bytes(1) value = decoder.decode_8bit_uint() if (self.dataType == "uint8MSB"): value = decoder.decode_8bit_uint() if (self.dataType == "uint16"): value = decoder.decode_16bit_uint() if (self.dataType == "uint32"): value = decoder.decode_32bit_uint() if (self.dataType == "uint64"): value = decoder.decode_64bit_uint() if (self.dataType == "float32"): value = decoder.decode_32bit_float() if (self.dataType == "float64"): value = decoder.decode_64bit_float() if (self.dataType == "string2"): value = decoder.decode_string(2) if (self.dataType == "string4"): value = decoder.decode_string(4) if (self.dataType == "string6"): value = decoder.decode_string(6) if (self.dataType == "string8"): value = decoder.decode_string(8) if (self.dataType == "bcd8"): value=decoder.decoder.decode_8bit_int() value=value & 0xf + value & (0xf0&0xf0>>4*10) if (self.dataType == "bcd16"): value=decoder.decoder.decode_8bit_int() value=value & 0xf + (value & 0xf0>>4*10) + (value & 0xf00>>8*100) + (value & 0xf000>>12*1000) if multiplydevice: value=value*domoticz.device(multiplydevice) # Divide the value (decimal)? value = str(round(value / self.devide, self.digits)) Domoticz.Log("MODBUS DEBUG VALUE: " + str(value)+" Old value:"+self.device.sValue+" Old value:"+str(self.device.nValue)) #Domoticz.Log("LastUpdate:"+self.device.LastUpdate) age=(datetime.now()-datetime.strptime(self.device.LastUpdate, '%Y-%m-%d %H:%M:%S')).seconds #Domoticz.Log("LastUpdate seconds ago:"+str(age)) if (self.device.sValue != value ) or age>300: self.device.Update(0, value) # Update value in Domoticz #Domoticz.Debug("Done update: " + str(value)) except Exception as e: Domoticz.Log("Modbus error decoding or received no data (TCP/IP)!, check your settings!"+repr(e)) #self.device.Update(0, "0") # Update value in Domoticz else: Domoticz.Log("No data")
def decode_register(self, register, type): #omitting string for now since it requires a specified length if type == '8int': rr = self.read_register_raw(register, 1) decoder = BinaryPayloadDecoder.fromRegisters( rr.registers, byteorder=self.BYTE_ORDER, wordorder=self.WORD_ORDER) output = decoder.decode_8bit_int() elif type == '8uint': rr = self.read_register_raw(register, 1) decoder = BinaryPayloadDecoder.fromRegisters( rr.registers, byteorder=self.BYTE_ORDER, wordorder=self.WORD_ORDER) output = decoder.decode_8bit_uint() elif type == '16int': rr = self.read_register_raw(register, 1) decoder = BinaryPayloadDecoder.fromRegisters( rr.registers, byteorder=self.BYTE_ORDER, wordorder=self.WORD_ORDER) output = decoder.decode_16bit_int() elif type == '16uint': rr = self.read_register_raw(register, 1) decoder = BinaryPayloadDecoder.fromRegisters( rr.registers, byteorder=self.BYTE_ORDER, wordorder=self.WORD_ORDER) output = decoder.decode_16bit_uint() elif type == '32int': rr = self.read_register_raw(register, 2) decoder = BinaryPayloadDecoder.fromRegisters( rr.registers, byteorder=self.BYTE_ORDER, wordorder=self.WORD_ORDER) output = decoder.decode_32bit_int() elif type == '32uint': rr = self.read_register_raw(register, 2) decoder = BinaryPayloadDecoder.fromRegisters( rr.registers, byteorder=self.BYTE_ORDER, wordorder=self.WORD_ORDER) output = decoder.decode_32bit_uint() elif type == '32float': rr = self.read_register_raw(register, 2) decoder = BinaryPayloadDecoder.fromRegisters( rr.registers, byteorder=self.BYTE_ORDER, wordorder=self.WORD_ORDER) output = decoder.decode_32bit_float() elif type == '64int': rr = self.read_register_raw(register, 4) decoder = BinaryPayloadDecoder.fromRegisters( rr.registers, byteorder=self.BYTE_ORDER, wordorder=self.WORD_ORDER) output = decoder.decode_64bit_int() elif type == '64uint': rr = self.read_register_raw(register, 4) decoder = BinaryPayloadDecoder.fromRegisters( rr.registers, byteorder=self.BYTE_ORDER, wordorder=self.WORD_ORDER) output = decoder.decode_64bit_uint() elif type == 'ignore': rr = self.read_register_raw(register, 1) decoder = BinaryPayloadDecoder.fromRegisters( rr.registers, byteorder=self.BYTE_ORDER, wordorder=self.WORD_ORDER) output = decoder.skip_bytes(8) elif type == '64float': rr = self.read_register_raw(register, 4) decoder = BinaryPayloadDecoder.fromRegisters( rr.registers, byteorder=self.BYTE_ORDER, wordorder=self.WORD_ORDER) output = decoder.decode_64bit_float() return output
def convert(self, config, data): self.__result["telemetry"] = [] self.__result["attributes"] = [] for config_data in data: for tag in data[config_data]: try: configuration = data[config_data][tag]["data_sent"] response = data[config_data][tag]["input_data"] if configuration.get("byteOrder"): byte_order = configuration["byteOrder"] elif config.get("byteOrder"): byte_order = config["byteOrder"] else: byte_order = "LITTLE" if configuration.get("wordOrder"): word_order = configuration["wordOrder"] elif config.get("wordOrder"): word_order = config.get("wordOrder", "BIG") else: word_order = "BIG" endian_order = Endian.Little if byte_order.upper( ) == "LITTLE" else Endian.Big word_endian_order = Endian.Little if word_order.upper( ) == "LITTLE" else Endian.Big decoded_data = None if not isinstance(response, ModbusIOException) and not isinstance( response, ExceptionResponse): if configuration["functionCode"] in [1, 2]: result = response.bits result = result if byte_order.upper( ) == 'LITTLE' else result[::-1] log.debug(result) if configuration["type"].lower() == "bits": decoded_data = result[:configuration.get( "objectsCount", configuration. get("registersCount", configuration.get("registerCount", 1) ))] if len(decoded_data) == 1 and isinstance( decoded_data, list): decoded_data = decoded_data[0] else: decoded_data = result[0] elif configuration["functionCode"] in [3, 4]: decoder = None registers = response.registers log.debug("Tag: %s Config: %s registers: %s", tag, str(configuration), str(registers)) try: decoder = BinaryPayloadDecoder.fromRegisters( registers, byteorder=endian_order, wordorder=word_endian_order) except TypeError: # pylint: disable=E1123 decoder = BinaryPayloadDecoder.fromRegisters( registers, endian=endian_order, wordorder=word_endian_order) assert decoder is not None decoded_data = self.__decode_from_registers( decoder, configuration) if configuration.get("divider"): decoded_data = float(decoded_data) / float( configuration["divider"]) if configuration.get("multiplier"): decoded_data = decoded_data * configuration[ "multiplier"] else: log.exception(response) decoded_data = None if config_data == "rpc": return decoded_data log.debug("datatype: %s \t key: %s \t value: %s", self.__datatypes[config_data], tag, str(decoded_data)) self.__result[self.__datatypes[config_data]].append( {tag: decoded_data}) except Exception as e: log.exception(e) log.debug(self.__result) return self.__result
def updating_writer(a): ''' A worker process that runs every so often and updates live values of the context. It should be noted that there is a race condition for the update. :param arguments: The input arguments to the call ''' global g_Time global s_Time global g_increment if g_Time >= 60*20: g_Time = 0 log.debug("g_Time reset") print "g_Time reset" g_Time += 1 log.debug("updating the context at {0}".format(g_Time)) context = a[0] srv_id = a[1] register = 3 slave_id = 0x00 # gets current values if context[slave_id].zero_mode: START_ADDRESS = FIRST_REGISTER # if zero_mode=True else: START_ADDRESS = FIRST_REGISTER-1 # if zero_mode=False. inizia a leggere da 40000 e prendi gli N successivi,escluso il 40000 values = context[slave_id].getValues(register, START_ADDRESS, count=NUM_REGISTERS) # update P and Q with random values log.debug("pump context values: " + str(values)) decoder = BinaryPayloadDecoder.fromRegisters(values[502:503],endian=Endian.Little) bits_502 = decoder.decode_bits() bits_502 += decoder.decode_bits() decoder = BinaryPayloadDecoder.fromRegisters(values[552:553],endian=Endian.Little) bits_552 = decoder.decode_bits() bits_552 += decoder.decode_bits() decoder = BinaryPayloadDecoder.fromRegisters(values[506:507],endian=Endian.Little) bits_506 = decoder.decode_bits() bits_506 += decoder.decode_bits() if g_Time >= s_Time > 0: print "start iniettore dopo {0} secondi".format(s_Time) log.debug("start iniettore dopo {0} secondi".format(s_Time)) s_Time = 0 bits_502[7] = 1 # START INIETTORE bits_builder = BinaryPayloadBuilder(endian=Endian.Little) bits_builder.add_bits(bits_502) bits_reg=bits_builder.to_registers() values[502:503]=[bits_reg[0]] cicli_min = 0 p_new = 0 # if iniettore Started if bits_502[7]: s_Time = 0 #cicli_min = cicli_rand.rvs() cicli_min = int( out_val_q(g_Time,50.) ) p_new = int(out_val_p(g_Time,values[560])) + delta_rand.rvs() + 1 if p_new < 1: cicli_min = 70. else: cicli_min = 70./p_new if g_Time % 13 == 0: g_increment += 1 p_new = p_new + g_increment ########################################## ### Verifica limite massimo P ############################# if p_new >= values[560]: log.debug("PMax exceeded: %d (516) > %d (560)" % (p_new,values[560]) ) p_new = values[560] + delta_rand.rvs() + 1 ########################################## ### Verifica limite massimo Q ############################# if cicli_min >= values[562]: log.debug("QMax exceeded: %d (520) > %d (562)" % (cicli_min,values[562]) ) cicli_min = values[562] else: if values[560] == 0: print "560 è zero" values[560] = 1 if p_new/values[560] >= 0.5: cicli_min = max(1,int((values[560])/max(1,p_new))) else: cicli_min = 3*values[560]/max(1,p_new) else: cicli_min = 0 p_new = 0 log.debug("p_new=%d" % p_new) q_val = cicli_min*liters_cycle q_m_ch = 60.0*q_val/1000.0 log.debug("cicli=%d, q=%f, mc=%f" % (cicli_min, q_val,q_m_ch)) # conversione float - Endian.Little il primo è il meno significativo if p_new < 0: p_new = 0 if cicli_min < 0: cicli_min = 0 values[516] = p_new # %MW516 PRESSIONE ATTUALE values[520] = cicli_min builder = BinaryPayloadBuilder(endian=Endian.Little) builder.add_32bit_float(q_val) builder.add_32bit_float(q_m_ch) reg=builder.to_registers() log.debug("2 x 32bit_float = %s" % str(reg)) values[522:526]=reg log.debug("On Pump Server %02d new values (516-525): %s" % (srv_id, str(values[516:526]))) # assign new values to context values[599] = 699 context[slave_id].setValues(register, START_ADDRESS, values)
def _decode_response( resp: ReadCoilsResponse | ReadDiscreteInputsResponse | ReadHoldingRegistersResponse | ReadInputRegistersResponse, obj: ModbusObj, ) -> bool | int | float: """Decodes value from registers and scale them. # TODO make 4 decoders for all combinations in bo, wo and use them? Args: resp: Read request response. obj: Object instance Returns: Decoded from register\bits and scaled value. """ # TODO: Add decode with different byteorder in bytes VisioDecoder class if isinstance(resp, ReadBitsResponseBase): data = resp.bits return 1 if data[0] else 0 # TODO: add support several bits? if isinstance(resp, ReadRegistersResponseBase): data = resp.registers scaled: float | int if obj.data_type == ModbusDataType.BOOL: if obj.bit and obj.data_length == 1: value = format(data[0], "0>16b") value = list(reversed(value))[obj.bit] scaled = decoded = int(value) elif obj.data_length == 1: scaled = decoded = 1 if data[0] else 0 else: scaled = decoded = any(data) else: decoder = BinaryPayloadDecoder.fromRegisters( registers=data, byteorder=obj.byte_order, wordorder=obj.word_order ) decode_funcs = { ModbusDataType.BITS: decoder.decode_bits, # DataType.BOOL: None, # DataType.STR: decoder.decode_string, 8: { ModbusDataType.INT: decoder.decode_8bit_int, ModbusDataType.UINT: decoder.decode_8bit_uint, }, 16: { ModbusDataType.INT: decoder.decode_16bit_int, ModbusDataType.UINT: decoder.decode_16bit_uint, ModbusDataType.FLOAT: decoder.decode_16bit_float, # DataType.BOOL: None, }, 32: { ModbusDataType.INT: decoder.decode_32bit_int, ModbusDataType.UINT: decoder.decode_32bit_uint, ModbusDataType.FLOAT: decoder.decode_32bit_float, }, 64: { ModbusDataType.INT: decoder.decode_64bit_int, ModbusDataType.UINT: decoder.decode_64bit_uint, ModbusDataType.FLOAT: decoder.decode_64bit_float, }, } assert decode_funcs[obj.data_length][obj.data_type] is not None decoded = decode_funcs[obj.data_length][obj.data_type]() scaled = decoded * obj.scale + obj.offset # Scaling _LOG.debug( "Decoded", extra={ "object": obj, "value_raw": data, "value_decoded": decoded, "value_scaled": scaled, }, ) return scaled raise NotImplementedError
def _convert_to_float(self, registers): """ Converts unsigned int from Modbus device to a floating point number """ decoder = BinaryPayloadDecoder.fromRegisters(registers, byteorder=Endian.Big, wordorder=Endian.Big) return round(decoder.decode_32bit_float(), 2)
def main(): # IP = input("Slave IP: ") # ID = input("Slave ID: ") IP = "192.168.0.13" ID = 2 client = ModbusCLient(IP, port=502) if client.connect() == False: debug.error("Unable to connect to PLC") return while True: payload = client.read_holding_registers(0, 1, unit=ID) # print(payload.bits) decoder = BinaryPayloadDecoder.fromRegisters(payload.registers, byteorder=Endian.Little, wordorder=Endian.Big) port_0 = decoder.decode_bits() payload = client.read_holding_registers(1, 1, unit=ID) decoder = BinaryPayloadDecoder.fromRegisters(payload.registers, byteorder=Endian.Big, wordorder=Endian.Little) port_1 = decoder.decode_bits() debug.info("Input 0: {}".format(port_0)) q_0 = port_0[0] debug.debug(q_0) debug.info("Input 1: {}".format(port_1)) # decoded = OrderedDict([ # ('Port_0', decoder.decode_bits()), # ('Port_1', decoder.decode_bits()), # # # ('Analog_1', decoder.decode_32bit_int()), # # # ('Analog_2', decoder.decode_16bit_int()), # ]) # # for name,value in iteritems(decoded): # debug.info("{}: {}\t".format(name,value)) # print("vida: {}".format(decoder.decode_bits())) # print(payload.registers) # print("Bits: {}".format(decoder.decode_bits(1))) payload = client.read_holding_registers(2, 2, unit=ID) debug.debug("Payload: {} {}".format(payload.getRegister(0), payload.getRegister(1))) # decoder = BinaryPayloadDecoder.fromRegisters(payload.registers, # byteorder = Endian.Little, # wordorder = Endian.Little) # decoded = OrderedDict([ # # ('Port_0', decoder.decode_bits()), # # ('Port_1', decoder.decode_bits()), # ('Analog_1', decoder.decode_16bit_int()), # ('Analog_2', decoder.decode_16bit_int()), # ]) # # sleep(10) builder = BinaryPayloadBuilder(byteorder=Endian.Big, wordorder=Endian.Little) builder.add_bits([1, 1, 0, 0, 0, 0, 1, 0]) payload = builder.to_registers() debug.debug(payload) client.write_registers(4, payload, unit=ID)
def decodeData(self,data): returnData = [0]*(len(data)/2) decoder = BinaryPayloadDecoder.fromRegisters(data, endian=Endian.Little) for i in range(0,len(data)/2): returnData[i] = round(decoder.decode_32bit_float(),2) return returnData
import binascii from pymodbus.constants import Endian from pymodbus.payload import BinaryPayloadDecoder ipaddress = str(sys.argv[1]) from pymodbus.client.sync import ModbusTcpClient client = ModbusTcpClient(ipaddress, port=502) connection = client.connect() # Studer Battery Power request = client.read_input_registers(6, 2, unit=60) if request.isError(): # handle error, log? print('Modbus Error:', request) else: result = request.registers decoder = BinaryPayloadDecoder.fromRegisters(result, byteorder=Endian.Big) bw = decoder.decode_32bit_float() # type: float f = open('/var/www/html/openWB/ramdisk/speicherleistung', 'w') f.write(str(bw)) f.close() # Studer SOC request = client.read_input_registers(4, 2, unit=60) if request.isError(): # handle error, log? print('Modbus Error:', request) else: result = request.registers decoder = BinaryPayloadDecoder.fromRegisters(result, byteorder=Endian.Big) bs = decoder.decode_32bit_float() # type: float f = open('/var/www/html/openWB/ramdisk/speichersoc', 'w')
def read(self): """ Read registers from client""" if pymodbus_found: time.sleep(float(self._settings["interval"])) f = [] c = Cargo.new_cargo(rawdata="") # valid datacodes list and number of registers associated # in modbus protocol, one register is 16 bits or 2 bytes valid_datacodes = ({ 'h': 1, 'H': 1, 'i': 2, 'l': 2, 'I': 2, 'L': 2, 'f': 2, 'q': 4, 'Q': 4, 'd': 4 }) if not self._modcon: self._con.close() self._log.info("Not connected, retrying connect %s", self.init_settings) self._con = self._open_modTCP( self.init_settings["modbus_IP"], self.init_settings["modbus_port"]) if self._modcon: # fetch nodeid if 'nodeId' in self._settings: node = str(self._settings["nodeId"]) else: self._log.error("please provide a nodeId") return # stores registers if 'register' in self._settings: registers = self._settings["register"] else: self._log.error( "please provide a register number or a list of registers" ) return # fetch unitids if present UnitIds = self._settings.get("nUnit", None) # stores names # fetch datacode or datacodes if node in ehc.nodelist and 'rx' in ehc.nodelist[node]: rNames = ehc.nodelist[node]['rx']['names'] if 'datacode' in ehc.nodelist[node]['rx']: datacode = ehc.nodelist[node]['rx']['datacode'] datacodes = None elif 'datacodes' in ehc.nodelist[node]['rx']: datacodes = ehc.nodelist[node]['rx']['datacodes'] else: self._log.error( "please provide a datacode or a list of datacodes") return # check if number of registers and number of names are the same if len(rNames) != len(registers): self._log.error( "You have to define an equal number of registers and of names" ) return # check if number of names and number of datacodes are the same if datacodes is not None: if len(datacodes) != len(rNames): self._log.error( "You are using datacodes. You have to define an equal number of datacodes and of names" ) return # calculate expected size in bytes and search for invalid datacode(s) expectedSize = 0 if datacodes is not None: for code in datacodes: if code not in valid_datacodes: self._log.debug("-" * 46) self._log.debug("invalid datacode") self._log.debug("-" * 46) return else: expectedSize += valid_datacodes[code] * 2 else: if datacode not in valid_datacodes: self._log.debug("-" * 46) self._log.debug("invalid datacode") self._log.debug("-" * 46) return else: expectedSize = len( rNames) * valid_datacodes[datacode] * 2 self._log.debug("expected bytes number after encoding: %s", expectedSize) # at this stage, we don't have any invalid datacode(s) # so we can loop and read registers for idx, rName in enumerate(rNames): register = int(registers[idx], 0) if UnitIds is not None: unitId = int(UnitIds[idx]) else: unitId = 1 if datacodes is not None: datacode = datacodes[idx] self._log.debug("datacode %s", datacode) qty = valid_datacodes[datacode] self._log.debug( "reading register #: %s, qty #: %s, unit #: %s", register, qty, unitId) try: self.rVal = self._con.read_input_registers( address=register, count=qty, unit=unitId) # assert self.rVal.function_code < 0x80 except Exception as e: self._log.error( "Connection failed on read of register: %s : %s", register, e) self._modcon = False #missing datas will lead to an incorrect encoding #we have to drop the payload return else: #self._log.debug("register value: %s type= %s", self.rVal.registers, type(self.rVal.registers)) #f = f + self.rVal.registers decoder = BinaryPayloadDecoder.fromRegisters( self.rVal.registers, byteorder=Endian.Big, wordorder=Endian.Big) if datacode == 'h': rValD = decoder.decode_16bit_int() elif datacode == 'H': rValD = decoder.decode_16bit_uint() elif datacode == 'i': rValD = decoder.decode_32bit_int() elif datacode == 'l': rValD = decoder.decode_32bit_int() elif datacode == 'I': rValD = decoder.decode_32bit_uint() elif datacode == 'L': rValD = decoder.decode_32bit_uint() elif datacode == 'f': rValD = decoder.decode_32bit_float() * 10 elif datacode == 'q': rValD = decoder.decode_64bit_int() elif datacode == 'Q': rValD = decoder.decode_64bit_uint() elif datacode == 'd': rValD = decoder.decode_64bit_float() * 10 t = ehc.encode(datacode, rValD) f = f + list(t) self._log.debug("Encoded value: %s", t) self._log.debug("value: %s", rValD) #test if payload length is OK if len(f) == expectedSize: self._log.debug("payload size OK (%d)", len(f)) self._log.debug("reporting data: %s", f) c.nodeid = node c.realdata = f self._log.debug("Return from read data: %s", c.realdata) return c else: self._log.error("incorrect payload size: %d expecting %d", len(f), expectedSize)
CU_Pf,CU_Pr,CU_Pt,CU_V, BH_Pf,BH_Pr,BH_Pt, HV_Bias, HV_Off_Setpoint,HV_On_Setpoint, SRF_Pf,SRF_Pr,SRF_Pt, Pulse_Freq,Pulse_Duty,Pulse_Delay,IR_Temp,Current_Emitted] #Building the list of tags to grab DBA_Mag_Tags = Hnums + Vnums + Dpnums + Solnums + CU_Tags + BH_Tags + HV_Tags + SRF_Tags + Pulse_Tags client = ModbusTcpClient('10.6.0.2') p = 0 N = 1 temp_list = [] for j in range(len(DBA_Mag_Names)): result = client.read_holding_registers(int(DBA_Mag_Tags[j]),2,unit=1) number = BinaryPayloadDecoder.fromRegisters(result.registers, byteorder=Endian.Big, wordorder=Endian.Big) temp_list.append(round(number.decode_32bit_float(),3)) time.sleep(.020) client.close() WFH_1,WFH_2,WFH_3,WFH_4,WFH_5,WFH_6,WFH_7,WFH_8,WFH_9,WFH_10,WFH_11,WFH_12,WFH_13,WFH_14,WFH_15,WFH_16,WFH_17,WFH_18,WFH_19,WFH_20,WFH_21,WFV_1,WFV_2,WFV_3,WFV_4,WFV_5,WFV_6,WFV_7,WFV_8,WFV_9,WFV_10,WFV_11,WFV_12,WFV_13,WFV_14,WFV_15,WFV_16,WFV_17,WFV_18,WFV_19,WFV_20,WFV_21,DP_1,DP_2,DP_3,DP_4,DP_5,DP_6,DP_7,DP_8,Sol_1,Sol_2,Sol_3,Sol_4,Sol_5,Sol_6,Sol_7,Sol_8,Sol_9,CU_Pf,CU_Pr,CU_Pt,CU_V,BH_Pf,BH_Pr,BH_Pt,HV_Bias,HV_Off_Setpoint,HV_On_Setpoint,SRF_Pf,SRF_Pr,SRF_Pt,Pulse_Freq,Pulse_Duty,Pulse_Delay,IR_Temp,Current_Emitted = temp_list #Putting the dataframe into an excel file. ########################################## #into the spreadsheet format dftoexcel = pd.DataFrame([[WFV_1,WFH_1,Sol_1,DP_1,'','Cu Gun (1)',CU_Pf,CU_Pr,CU_Pt,CU_V,'','','','','','','','','',''], [WFV_2,WFH_2,Sol_2,DP_2,'','BH (2)',BH_Pf,BH_Pr,BH_Pt,BH_V,'','','','','','','','','',''], [WFV_3,WFH_3,Sol_3,DP_3,'','Bias (0)','--','--','--',HV_Bias,'','','','','','','','','',''], [WFV_4,WFH_4,Sol_4,DP_4,'','SRF',SRF_Pf,SRF_Pr,SRF_Pt,'','','','','','','','','','',''],
# implementations differentiate holding/input discrete/coils and as such # you will not be able to write to these, therefore the starting values # are not known to these tests. Furthermore, some use the same memory # blocks for the two sets, so a change to one is a change to the other. # Keep both of these cases in mind when testing as the following will # _only_ pass with the supplied async modbus server (script supplied). #---------------------------------------------------------------------------# #rr = client.read_holding_registers(1,1) # #rr = client.read_holding_registers(address=0xF900, count=1) #decoder = BcdPayloadDecoder.fromRegisters(rr.registers) #Time_Interval = decoder.decode_int(2) #Time_Interval = rs485.read_string(0xF900, functioncode=3, numberOfRegisters=2)[0] # rr = client.read_input_registers(address=0x0000, count=2) decoder = BinaryPayloadDecoder.fromRegisters(rr.registers, endian=Endian.Big) Volts = decoder.decode_32bit_float() # rr = client.read_input_registers(address=0x0006, count=2) decoder = BinaryPayloadDecoder.fromRegisters(rr.registers, endian=Endian.Big) Current = decoder.decode_32bit_float() # rr = client.read_input_registers(address=0x000C, count=2) decoder = BinaryPayloadDecoder.fromRegisters(rr.registers, endian=Endian.Big) Active_Power = decoder.decode_32bit_float() # rr = client.read_input_registers(address=0x0012, count=2) decoder = BinaryPayloadDecoder.fromRegisters(rr.registers, endian=Endian.Big) Apparent_Power = decoder.decode_32bit_float() # rr = client.read_input_registers(address=0x0018, count=2)
def run(self): global OUTPUT ret = dict() modbus_server = cfg.get("sensors", "server") modbus_port = int(cfg.get("sensors", "port")) self.client = ModbusClient(modbus_server, port=modbus_port) temp_reg = int(cfg.get("sensors", "temperature_reg"),16) humid_reg = int(cfg.get("sensors", "humidity_reg"), 16) pressure_reg = int(cfg.get("sensors", "pressure_reg"), 16) geo_lat_reg = int(cfg.get("sensors", "geo_latitude_reg"), 16) geo_long_reg = int(cfg.get("sensors", "geo_longitude_reg"), 16) key_op_reg = int(cfg.get("sensors", "key_operation_reg"), 16) poll_freq = int(cfg.get("sensors", "poll_frequency")) while True: if self.stop_event.is_set(): break try: try: # Read all data in a single go # recv = self.client.read_holding_registers(temp_reg,5) # Read Temperature recv = self.client.read_holding_registers(temp_reg,1) ret['Temperature'] = recv.registers[0] # Read Humidity recv = self.client.read_holding_registers(humid_reg,1) ret['Humidity'] = recv.registers[0] # Read Pressure recv = self.client.read_holding_registers(pressure_reg,1) ret['Pressure'] = recv.registers[0] # Read Geo location latitude recv = self.client.read_holding_registers(geo_lat_reg,2) decoder = BinaryPayloadDecoder.fromRegisters(recv.registers, endian=Endian.Big) ret['Latitude'] = decoder.decode_32bit_float() # Read Geo location longitude recv = self.client.read_holding_registers(geo_long_reg,2) decoder = BinaryPayloadDecoder.fromRegisters(recv.registers, endian=Endian.Big) ret['Longitude'] = decoder.decode_32bit_float() # Read keyboard operation recv = self.client.read_holding_registers(key_op_reg,6) decoder = BinaryPayloadDecoder.fromRegisters(recv.registers, endian=Endian.Big) reg_str = decoder.decode_string(6) str_tokens = reg_str.split("\x00") ret['Key'] = str_tokens[0] except ModbusException: logger.error("Failed to retrieve data from modbus server!") OUTPUT = ret dweet(ret) send_to_cloud(ret) logger.debug("###################################") time.sleep(poll_freq) except Exception as ex: logger.exception("Exception.. but let us be resilient..") time.sleep(poll_freq)
# If you need to decode a collection of registers in a weird layout, the # payload decoder can help you as well. # # Here we demonstrate decoding a random register layout, unpacked it looks # like the following: # # - a 8 byte string 'abcdefgh' # - a 32 bit float 22.34 # - a 16 bit unsigned int 0x1234 # - an 8 bit int 0x12 # - an 8 bit bitstring [0,1,0,1,1,0,1,0] # ---------------------------------------------------------------------------# address = 0x01 count = 8 result = client.read_input_registers(address, count) decoder = BinaryPayloadDecoder.fromRegisters(result.registers, endian=Endian.Little) decoded = { "string": decoder.decode_string(8), "float": decoder.decode_32bit_float(), "16uint": decoder.decode_16bit_uint(), "8int": decoder.decode_8bit_int(), "bits": decoder.decode_bits(), } print "-" * 60 print "Decoded Data" print "-" * 60 for name, value in decoded.iteritems(): print ("%s\t" % name), value # ---------------------------------------------------------------------------#
def convert_format(self, block_start, block_end, data, sections, start, query_length): #def convert_format(self, data, sections, start, query_length): values = dict() ptr = 0 if sections[0][7] == 'BB': decoder = BinaryPayloadDecoder.fromRegisters(data, byteorder=Endian.Big, wordorder=Endian.Big) elif sections[0][7] == 'BL': decoder = BinaryPayloadDecoder.fromRegisters(data, byteorder=Endian.Big, wordorder=Endian.Little) for s in sections: if block_start + ptr >= start and block_start + ptr < start + query_length: #print("DECODE:", ptr, s[0], start , query_length, s) if s[0] == "uint16": if s[2] == 1: val = decoder.decode_16bit_uint() * int(s[2]) + int(s[3]) else: val = decoder.decode_16bit_uint() * float(s[2]) + float(s[3]) elif s[0] in ["sint16", "int16"]: if s[2] == 1: val = decoder.decode_16bit_int() * int(s[2]) + int(s[3]) else: val = decoder.decode_16bit_int() * float(s[2]) + float(s[3]) elif s[0] == "uint32": val = decoder.decode_32bit_uint() * float(s[2]) + float(s[3]) elif s[0] in ["sint32", "int32"]: val = decoder.decode_32bit_int() * float(s[2]) + float(s[3]) elif s[0] == "float32": val = decoder.decode_32bit_float() * float(s[2]) + float(s[3]) elif s[0] == "bit16": d_1 = decoder.decode_bits() d_2 = decoder.decode_bits() val = d_1 + d_2 #print(d_1, d_2, val) elif s[0][:3] == "str": length = int(s[1]) val1 = decoder.decode_string(length) #print("String Received", s, val1) val = val1.decode() else: print("Unknown type", s) #print(ptr, s, val ) ## ***** ROHIT change it to format field_name = self.params["field_name"].format(source=s[4], block=s[5], field=s[6] ) values[field_name] = val if s[0] in [ "bit16", "uint16", "sint16", "int16"]: ptr+=1 elif s[0] in ["uint32", "sint32", "int32", "float32"] : ptr +=2 elif s[0][:3] == "str": length = int(s[1]) ptr += length/2 else: print("Unknown type", s) #print(ptr, s, val ) #print("Formated Values", values) return values