class EUROTHERM_MODULE(): """ Class to manipulate a single Eurotherm Module """ def __init__(self, path, number, digits): """ Instrument class initalizes a connection through the desired port.""" # Path = COM / dev/tty*** Port self.device = Instrument(path, number) self.device.debug = False # Baudrate for Module is 9600 self.device.serial.baudrate = 9600 # Number Of Decimals used by the module. Temperature has 1 decimal while flowrate has 0. self.digits = digits # Select's the setpoint found in the config file as the active setpoint. self.set_active_setpoint(configuration['sp_select_default']) # Writing def set_active_setpoint(self, value): """ Set's the active setpoint to the value specified. """ if 0 <= value and value <= 1: self.device.write_register(configuration['sp_select_default'], value, self.digits) def set_setpoint(self, value): """ Set's the value in the active setpoint to the value given """ if self.read_setpoint_low_limit( ) <= value and value <= self.read_setpoint_high_limit(): self.device.write_register(configuration['sp_select_address'], value, self.digits) # Reading def read_value(self): """ Read value from the first register. This is the current value found on the Eurotherm Module. """ return self.device.read_register(1, self.digits) def read_setpoint_select(self): """ Reads the active setpoint from the 15th register. """ return self.device.read_register(15, self.digits) def read_setpoint_value(self): """ Reads the active setpoint's value from the register specified in the config.py. """ return self.device.read_register(configuration['sp_value_address'], self.digits) def read_setpoint_high_limit(self): """ Reads the active setpoint's high limit value from the register specified in the config.py. """ return self.device.read_register(configuration['sp_high_limit'], self.digits) def read_setpoint_low_limit(self): """ Reads the active setpoint's low limit value from the register specified in the config.py. """ return self.device.read_register(configuration['sp_low_limit'], self.digits)
def main(): # Serial port parameters port = "COM2" slaveNumber = 1 BAUDRATE = 9600 STOPBITS = 1 PARITY = "E" BYTESIZE = 8 # Register parameters. # registers - list of registers which will be logged. #START_REGISTER = 1100 #NUMBER_OF_REGISTERS = 10 registers = {'engine_right':1100, 'engine_left':1101, 'light_level':1102, \ 'camera_id':1103, 'noop':1104, 'motor_direction_left':1105, \ 'motor_direction_right':1106} # open log file name = "logs/log_"+time.strftime("%d_%b_%Y_%H_%M_%S") + ".txt" logFile = open(name, 'a') # Connect to robot motka = Instrument(port, slaveNumber) motka.serial.baudrate = BAUDRATE motka.serial.stopbits = STOPBITS motka.serial.parity = PARITY motka.serial.bytesize = BYTESIZE #Read data from device and write it to log file print "Logger started. If you want to stop press Cntrl+C " try: while 1: data = [] for value in registers.values(): data.append( motka.read_register(value) ) writeData(logFile, data) except KeyboardInterrupt: print "Logger was stopped by keyboard interrupt" except: logFile.close() motka.close() raise logFile.close() motka.close()
def read_register(self, *args, **kwargs): """Read register from instrument (with retries) The argument definition is the same as for the minimalmodbus method, see the full documentation for `read_register <https://minimalmodbus.readthedocs.io/en/master/apiminimalmodbus .html#minimalmodbus.Instrument.read_register>`_ for details. """ for retry in range(0, self.retries + 1): try: return Instrument.read_register(self, *args, **kwargs) except ValueError as exception: if retry < self.retries: LOGGER.warning("Communication error in read_register, retrying %s " "out of %s times", retry + 1, self.retries) continue else: raise exception
def read_register(self, *args, **kwargs): """Read register from instrument (with retries) The argument definition is the same as for the minimalmodbus method, see the full documentation for `read_register <https://minimalmodbus.readthedocs.io/en/master/apiminimalmodbus .html#minimalmodbus.Instrument.read_register>`_ for details. """ for retry in range(0, self.retries + 1): try: return Instrument.read_register(self, *args, **kwargs) except ValueError as exception: if retry < self.retries: LOGGER.warning( "Communication error in read_register, retrying %s " "out of %s times", retry + 1, self.retries) continue else: raise exception
class EnergyModbusReader: energy_device_id = None instrument = None appSocketIO = None modbusConfig = None def __init__(self, energy_device_id, appSocketIO=None): self.logger = logging.getLogger( 'nl.oppleo.services.EnergyModbusReader') self.energy_device_id = energy_device_id self.appSocketIO = appSocketIO self.oppleoConfig: OppleoConfig = OppleoConfig() self.logger.debug('Production environment, calling initInstrument()') self.initInstrument() self.threadLock = threading.Lock() def initInstrument(self): global SDM630v2, SDM120 energy_device_data = EnergyDeviceModel.get() self.logger.debug( 'found device: %s %s %d' % (energy_device_data.energy_device_id, energy_device_data.port_name, energy_device_data.slave_address)) try: self.instrument = Instrument( port=energy_device_data.port_name, slaveaddress=energy_device_data.slave_address) except Exception as e: self.logger.error("initInstrument() failed: {}".format(str(e))) raise # Get this from the database self.instrument.serial.baudrate = energy_device_data.baudrate self.instrument.serial.bytesize = energy_device_data.bytesize self.instrument.serial.parity = energy_device_data.parity self.instrument.serial.stopbits = energy_device_data.stopbits self.instrument.serial.timeout = energy_device_data.serial_timeout self.instrument.debug = energy_device_data.debug self.instrument.mode = energy_device_data.mode self.instrument.close_port_after_each_call = energy_device_data.close_port_after_each_call self.modbusConfig = SDM630v2 for i in range(len(modbusConfigOptions)): if energy_device_data.modbus_config == modbusConfigOptions[i][ MB.NAME]: self.modbusConfig = modbusConfigOptions[i] self.logger.debug("Modbus config {} selected (default: {}).".format( self.modbusConfig[MB.NAME], SDM630v2[MB.NAME])) self.readSerialNumber(energy_device_data.port_name, energy_device_data.slave_address) def readSerialNumber(self, port_name=None, slave_address=None): self.logger.debug('readSerialNumber()') if self.modbusConfig[MB.SN][MB.ENABLED]: if self.modbusConfig[MB.SN][MB.TYPE] == MB.TYPE_REGISTER: serial_Hi = self.instrument.read_register( \ self.modbusConfig[MB.SN][MB.HI][MB.REGISTER_ADDRESS], \ self.modbusConfig[MB.SN][MB.HI][MB.NUMBER_OF_DECIMALS], \ self.modbusConfig[MB.SN][MB.HI][MB.FUNCTION_CODE], \ self.modbusConfig[MB.SN][MB.HI][MB.SIGNED] \ ) serial_Lo = self.instrument.read_register( \ self.modbusConfig[MB.SN][MB.LO][MB.REGISTER_ADDRESS], \ self.modbusConfig[MB.SN][MB.LO][MB.NUMBER_OF_DECIMALS], \ self.modbusConfig[MB.SN][MB.LO][MB.FUNCTION_CODE], \ self.modbusConfig[MB.SN][MB.LO][MB.SIGNED] \ ) self.logger.debug( 'readSerialNumber() serial_Hi:{} serial_Lo:{}'.format( serial_Hi, serial_Lo)) self.oppleoConfig.kWhMeterSerial = str((serial_Hi * 65536) + serial_Lo) self.logger.info( 'kWh meter serial number: {} (port:{}, address:{})'.format( self.oppleoConfig.kWhMeterSerial, port_name, slave_address)) else: self.logger.warn( 'modbusConfig serialNumber type {} not supported!'.format( self.modbusConfig[MB.SN][MB.TYPE])) self.oppleoConfig.kWhMeterSerial = 99999999 else: self.oppleoConfig.kWhMeterSerial = 99999999 return self.oppleoConfig.kWhMeterSerial def getTotalKWHHValue(self): self.logger.debug('Production environment, getting real data') return self.getProdTotalKWHHValue() def getMeasurementValue(self): self.logger.debug('Production environment, getting real data') return self.getProdMeasurementValue() def getProdTotalKWHHValue(self): if self.modbusConfig[MB.TOTAL_ENERGY][MB.TYPE] == MB.TYPE_FLOAT: return round( self.try_read_float( value_desc='kwh', registeraddress=self.modbusConfig[MB.TOTAL_ENERGY][ MB.REGISTER_ADDRESS], functioncode=self.modbusConfig[MB.TOTAL_ENERGY][ MB.FUNCTION_CODE], number_of_registers=self.modbusConfig[MB.TOTAL_ENERGY][ MB.NUMBER_OF_REGISTERS], byteorder=self.modbusConfig[MB.TOTAL_ENERGY][ MB.BYTE_ORDER]), 1) else: self.logger.warn( 'modbusConfig total_kWh type {} not supported!'.format( self.modbusConfig[MB.TOTAL_ENERGY][MB.TYPE])) return 0 def try_read_float_from_config(self, name, el): # self.logger.debug("try_read_float_from_config name:{} el:{}".format(name, str(el))) if not el[MB.ENABLED]: self.logger.debug("Modbus element {} not enabled".format(name)) return 0 if el[MB.TYPE] != MB.TYPE_FLOAT: self.logger.warn( "Type {} for modbus element {} not supported (must be {})". format(el[MB.TYPE], name, MB.TYPE_FLOAT)) return 0 return round( self.try_read_float(value_desc=name, registeraddress=el[MB.REGISTER_ADDRESS], functioncode=el[MB.FUNCTION_CODE], number_of_registers=el[MB.NUMBER_OF_REGISTERS], byteorder=el[MB.BYTE_ORDER]), 1) def getProdMeasurementValue(self): L1_P = self.try_read_float_from_config( 'l1_p', self.modbusConfig[MB.L1][MB.POWER]) L1_V = self.try_read_float_from_config( 'l1_v', self.modbusConfig[MB.L1][MB.VOLT]) L1_A = self.try_read_float_from_config( 'l1_a', self.modbusConfig[MB.L1][MB.AMP]) L1_kWh = self.try_read_float_from_config( 'l1_kWh', self.modbusConfig[MB.L1][MB.ENERGY]) L2_P = self.try_read_float_from_config( 'l2_p', self.modbusConfig[MB.L2][MB.POWER]) L2_V = self.try_read_float_from_config( 'l2_v', self.modbusConfig[MB.L2][MB.VOLT]) L2_A = self.try_read_float_from_config( 'l2_a', self.modbusConfig[MB.L2][MB.AMP]) L2_kWh = self.try_read_float_from_config( 'l2_kWh', self.modbusConfig[MB.L2][MB.ENERGY]) L3_P = self.try_read_float_from_config( 'l3_p', self.modbusConfig[MB.L3][MB.POWER]) L3_V = self.try_read_float_from_config( 'l3_v', self.modbusConfig[MB.L3][MB.VOLT]) L3_A = self.try_read_float_from_config( 'l3_a', self.modbusConfig[MB.L3][MB.AMP]) L3_kWh = self.try_read_float_from_config( 'l3_kWh', self.modbusConfig[MB.L3][MB.ENERGY]) kWh = self.try_read_float_from_config( 'kWh', self.modbusConfig[MB.TOTAL_ENERGY]) Hz = self.try_read_float_from_config('hz', self.modbusConfig[MB.FREQ]) return { "energy_device_id": self.energy_device_id, "kwh_l1": L1_kWh, "kwh_l2": L2_kWh, "kwh_l3": L3_kWh, "a_l1": L1_A, "a_l2": L2_A, "a_l3": L3_A, "v_l1": L1_V, "v_l2": L2_V, "v_l3": L3_V, "p_l1": L1_P, "p_l2": L2_P, "p_l3": L3_P, "kw_total": kWh, "hz": Hz } """ minimalmodbus.read_float raises: TypeError, ValueError if functioncode is unknown, number_of_registers is out of bounds ModbusException NoResponseError("No communication with the instrument (no answer)") serial.SerialException (inherited from IOError) """ def try_read_float(self, value_desc, registeraddress, functioncode, number_of_registers, byteorder, default=0): value = default maxRetries = 3 tries = 1 # Used by MeasureElectricityUsageThread and ChargerHandlerThread while True: try: with self.threadLock: value = self.instrument.read_float(registeraddress, functioncode, number_of_registers, byteorder) # Yield if we can, allow other time constraint threads to run if self.appSocketIO is not None: self.appSocketIO.sleep(0.01) return value except (ModbusException, NoResponseError, SerialException) as e: # Recoverable IO errors, try again self.logger.debug( "Could not read value {} due to potential recoverable exception {}" .format(value_desc, e)) except (TypeError, ValueError, Exception) as e: # Catch all, won't recover self.logger.warning( "Failed to read {} from modbus due to exception {}. Using {}" .format(value_desc, e, value)) return value if tries >= maxRetries: # No more retries, fail now self.logger.warning( "Failed to read {} from modbus after trying {} times. Using {}" .format(value_desc, tries, value)) return value tries += 1 # Wait before retry if self.appSocketIO is not None: self.appSocketIO.sleep(0.05) self.logger.debug( "Trying again ({}) to read modbus for {}...".format( (tries), value_desc))