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)
Esempio n. 2
0
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()
Esempio n. 3
0
    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
Esempio n. 4
0
    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
Esempio n. 5
0
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))