Example #1
0
    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
Example #2
0
    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
Example #3
0
    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
Example #4
0
    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