def _process_tx(self, cargo): """Prepare data for outgoing transmission. cargo is passed through this chain of processing to scale and then break the real values down into byte values, Uses the datacode data if available. DO NOT OVER-WRITE THE "REAL" VALUE DATA WITH ENCODED DATA !!! there may be other threads that need to use cargo.realdata to encode data for other targets. New "encoded" data is stored as a list of {interfacer:encoded-data} dicts. Returns cargo. """ txc = cargo scaled = [] encoded = [] # Normal operation is dest from txc.nodeid if txc.target: dest = str(txc.target) # self._log.info("dest from txc.target: "+dest) else: dest = str(txc.nodeid) # self._log.info("dest from txc.nodeid: "+dest) # self._log.info("Target: "+dest) # self._log.info("Realdata: "+json.dumps(txc.realdata)) # check if node is listed and has individual scales for each value if dest in ehc.nodelist and 'tx' in ehc.nodelist[ dest] and 'scales' in ehc.nodelist[dest]['tx']: scales = ehc.nodelist[dest]['tx']['scales'] # Discard the frame & return 'False' if it doesn't match the number of scales if len(txc.realdata) != len(scales): self._log.warning( str(txc.uri) + " Scales " + str(scales) + " for RX data : " + str(txc.realdata) + " not suitable ") return False else: # Determine the expected number of values to be decoded # Set decoder to "Per value" scaling using scale 'False' as flag scale = False else: # if node is listed, but has only a single default scale for all values if dest in ehc.nodelist and 'tx' in ehc.nodelist[ dest] and 'scale' in ehc.nodelist[dest]['tx']: scale = ehc.nodelist[dest]['tx']['scale'] else: # when node not listed or has no scale(s) use the interfacers default if specified if 'scale' in self._settings: scale = self._settings['scale'] else: scale = "1" if scale == "1": scaled = txc.realdata else: for i in range(0, len(txc.realdata), 1): x = scale if not scale: x = scales[i] if x == "1": val = txc.realdata[i] else: val = float(txc.realdata[i]) / float(x) if val % 1 == 0: val = int(val) scaled.append(val) # self._log.info("Scaled: "+json.dumps(scaled)) # check if node is listed and has individual datacodes for each value if (dest in ehc.nodelist and 'tx' in ehc.nodelist[dest] and 'datacodes' in ehc.nodelist[dest]['tx']): # fetch the string of datacodes datacodes = ehc.nodelist[dest]['tx']['datacodes'] # fetch a string of data sizes based on the string of datacodes datasizes = [] for code in datacodes: datasizes.append(ehc.check_datacode(str(code))) # Discard the frame & return 'False' if it doesn't match the summed datasizes if len(scaled) != len(datasizes): self._log.warning( str(txc.uri) + " TX datacodes: " + str(datacodes) + " are not valid for values " + str(scaled)) return False else: # Determine the expected number of values to be decoded count = len(scaled) # Set decoder to "Per value" decoding using datacode 'False' as flag datacode = False else: # if node is listed, but has only a single default datacode for all values if dest in ehc.nodelist and 'tx' in ehc.nodelist[ dest] and 'datacode' in ehc.nodelist[dest]['tx']: datacode = ehc.nodelist[dest]['tx']['datacode'] else: # when node not listed or has no datacode(s) use the interfacers default if specified if 'datacode' in self._settings: datacode = self._settings['datacode'] else: datacode = "h" # Ensure only int 0 is passed not str 0 if datacode == '0': datacode = 0 # when no (default)datacode(s) specified, pass string values back as numerical values if not datacode: encoded.append(dest) for val in scaled: if float(val) % 1 != 0: val = float(val) else: val = int(float(val)) encoded.append(val) # Discard frame if total size is not an exact multiple of the specified datacode size. # elif len(data) * ehc.check_datacode(datacode) != 0: # self._log.warning(str(uri) + " TX data length: " + str(len(data)) + # " is not valid for datacode " + str(datacode)) # return False else: # Determine the number of values in the frame of the specified code & size count = len(scaled) #/ ehc.check_datacode(datacode) if not encoded: encoded.append(dest) for i in range(0, count, 1): # Use single datacode unless datacode = False then use datacodes dc = str(datacode) if not datacode: dc = str(datacodes[i]) for b in ehc.encode(dc, int(scaled[i])): encoded.append(b) # self._log.info("Encoded: "+json.dumps(encoded)) txc.encoded.update({self.getName(): encoded}) return txc
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" + str(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 if "nUnit" in self._settings: UnitIds = self._settings["nUnit"] else: UnitIds = 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: " + str(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]) if UnitIds is not None: unitId = int(UnitIds[idx]) else: unitId = 1 if datacodes is not None: datacode = datacodes[idx] self._log.debug("datacode " + datacode) qty = valid_datacodes[datacode] self._log.debug("reading register # :" + str(register) + ", qty #: " + str(qty) + ", unit #: " + str(unitId)) try: self.rVal = self._con.read_holding_registers( register - 1, qty, unit=unitId) assert (self.rVal.function_code < 0x80) except Exception as e: self._log.error( "Connection failed on read of register: " + str(register) + " : " + str(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:" + str(self.rVal.registers)+" type= " + str(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: " + str(t)) self._log.debug("value: " + str(rValD)) #test if payload length is OK if len(f) == expectedSize: self._log.debug("payload size OK (" + str(len(f)) + ")") self._log.debug("reporting data: " + str(f)) c.nodeid = node c.realdata = f self._log.debug("Return from read data: " + str(c.realdata)) return c else: self._log.error("incorrect payload size :" + str(len(f)) + " expecting " + str(expectedSize)) return
def read(self): """ Read registers from client""" time.sleep(float(self._settings["interval"])) f = [] c = Cargo.new_cargo(rawdata="") if not self._modcon: self._con.close() self._log.info("Not connected, retrying connect" + str(self.init_settings)) self._con = self._open_modTCP(self.init_settings["modbus_IP"], self.init_settings["modbus_port"]) if self._modcon: #self._log.info(" names " + str(self._settings["rName"])) rNameList = self._settings["rName"] #self._log.info("rNames type: " + str(type(rNameList))) registerList = self._settings["register"] nRegList = self._settings["nReg"] rTypeList = self._settings["rType"] if "nUnit" in self._settings: nUnitList = self._settings["nUnit"] else: nUnitList = None for idx, rName in enumerate(rNameList): register = int(registerList[idx]) qty = int(nRegList[idx]) rType = rTypeList[idx] if nUnitList is not None: unitId = int(nUnitList[idx]) else: unitId = 1 self._log.debug("register # :" + str(register) + ", qty #: " + str(qty) + ", unit #: " + str(unitId)) try: self.rVal = self._con.read_holding_registers(register - 1, qty, unit=unitId) assert (self.rVal.function_code < 0x80) except Exception as e: self._log.error("Connection failed on read of register: " + str(register) + " : " + str(e)) self._modcon = False else: #self._log.debug("register value:" + str(self.rVal.registers)+" type= " + str(type(self.rVal.registers))) #f = f + self.rVal.registers decoder = BinaryPayloadDecoder.fromRegisters( self.rVal.registers, endian=Endian.Big) self._log.debug("register type: " + str(rType)) if rType == "uint16": rValD = decoder.decode_16bit_uint() t = emonhub_coder.encode('H', rValD) f = f + list(t) elif rType == "uint32": rValD = decoder.decode_32bit_uint() t = emonhub_coder.encode('I', rValD) f = f + list(t) elif rType == "uint64": rValD = decoder.decode_64bit_uint() t = emonhub_coder.encode('Q', rValD) f = f + list(t) elif rType == "int16": rValD = decoder.decode_16bit_int() t = emonhub_coder.encode('h', rValD) f = f + list(t) elif rType == "string": rValD = decoder.decode_string(qty * 2) t = rValD elif rType == "float32": rValD = decoder.decode_32bit_float() * 10 t = emonhub_coder.encode('f', rValD) f = f + list(t) else: self._log.error("Register type not found: " + str(rType) + " Register:" + str(register)) self._log.debug("Encoded value: " + str(t)) self._log.debug("reporting data: " + str(f)) if int(self._settings['nodeId']): c.nodeid = int(self._settings['nodeId']) c.realdata = f else: c.nodeid = int(12) c.realdata = f self._log.debug("Return from read data: " + str(c.realdata)) return c
def _process_tx(self, cargo): """Prepare data for outgoing transmission. cargo is passed through this chain of processing to scale and then break the real values down into byte values, Uses the datacode data if available. DO NOT OVER-WRITE THE "REAL" VALUE DATA WITH ENCODED DATA !!! there may be other threads that need to use cargo.realdata to encode data for other targets. New "encoded" data is stored as a list of {interfacer:encoded-data} dicts. Returns cargo. """ txc = cargo scaled = [] encoded = [] # Normal operation is dest from txc.nodeid if txc.target: dest = str(txc.target) # self._log.info("dest from txc.target: "+dest) else: dest = str(txc.nodeid) # self._log.info("dest from txc.nodeid: "+dest) # self._log.info("Target: "+dest) # self._log.info("Realdata: "+json.dumps(txc.realdata)) # check if node is listed and has individual scales for each value if dest in ehc.nodelist and 'tx' in ehc.nodelist[dest] and 'scales' in ehc.nodelist[dest]['tx']: scales = ehc.nodelist[dest]['tx']['scales'] # Discard the frame & return 'False' if it doesn't match the number of scales if len(txc.realdata) != len(scales): self._log.warning(str(txc.uri) + " Scales " + str(scales) + " for RX data : " + str(txc.realdata) + " not suitable " ) return False else: # Determine the expected number of values to be decoded # Set decoder to "Per value" scaling using scale 'False' as flag scale = False else: # if node is listed, but has only a single default scale for all values if dest in ehc.nodelist and 'tx' in ehc.nodelist[dest] and 'scale' in ehc.nodelist[dest]['tx']: scale = ehc.nodelist[dest]['tx']['scale'] else: # when node not listed or has no scale(s) use the interfacers default if specified if 'scale' in self._settings: scale = self._settings['scale'] else: scale = "1" if scale == "1": scaled = txc.realdata else: for i in range(0, len(txc.realdata), 1): x = scale if not scale: x = scales[i] if x == "1": val = txc.realdata[i] else: val = float(txc.realdata[i]) / float(x) if val % 1 == 0: val = int(val) scaled.append(val) # self._log.info("Scaled: "+json.dumps(scaled)) # check if node is listed and has individual datacodes for each value if (dest in ehc.nodelist and 'tx' in ehc.nodelist[dest] and 'datacodes' in ehc.nodelist[dest]['tx']): # fetch the string of datacodes datacodes = ehc.nodelist[dest]['tx']['datacodes'] # fetch a string of data sizes based on the string of datacodes datasizes = [] for code in datacodes: datasizes.append(ehc.check_datacode(str(code))) # Discard the frame & return 'False' if it doesn't match the summed datasizes if len(scaled) != len(datasizes): self._log.warning(str(txc.uri) + " TX datacodes: " + str(datacodes) + " are not valid for values " + str(scaled)) return False else: # Determine the expected number of values to be decoded count = len(scaled) # Set decoder to "Per value" decoding using datacode 'False' as flag datacode = False else: # if node is listed, but has only a single default datacode for all values if dest in ehc.nodelist and 'tx' in ehc.nodelist[dest] and 'datacode' in ehc.nodelist[dest]['tx']: datacode = ehc.nodelist[dest]['tx']['datacode'] else: # when node not listed or has no datacode(s) use the interfacers default if specified if 'datacode' in self._settings: datacode = self._settings['datacode'] else: datacode = "h" # Ensure only int 0 is passed not str 0 if datacode == '0': datacode = 0 # when no (default)datacode(s) specified, pass string values back as numerical values if not datacode: encoded.append(dest) for val in scaled: if float(val) % 1 != 0: val = float(val) else: val = int(float(val)) encoded.append(val) # Discard frame if total size is not an exact multiple of the specified datacode size. # elif len(data) * ehc.check_datacode(datacode) != 0: # self._log.warning(str(uri) + " TX data length: " + str(len(data)) + # " is not valid for datacode " + str(datacode)) # return False else: # Determine the number of values in the frame of the specified code & size count = len(scaled) #/ ehc.check_datacode(datacode) if not encoded: encoded.append(dest) for i in range(0, count, 1): # Use single datacode unless datacode = False then use datacodes dc = str(datacode) if not datacode: dc = str(datacodes[i]) for b in ehc.encode(dc,int(scaled[i])): encoded.append(b) # self._log.info("Encoded: "+json.dumps(encoded)) txc.encoded.update({self.getName():encoded}) return txc