class TestClientServer(unittest.TestCase):
    def setUp(self):
        # modbus server
        self.server = ModbusServer(port=5020, no_block=True)
        self.server.start()
        # modbus client
        self.client = ModbusClient(port=5020)
        self.client.open()

    def tearDown(self):
        self.client.close()

    def test_read_and_write(self):
        # word space
        self.assertEqual(self.client.read_holding_registers(0), [0],
                         'Default value is 0 when server start')
        self.assertEqual(self.client.read_input_registers(0), [0],
                         'Default value is 0 when server start')
        # single read/write
        self.assertEqual(self.client.write_single_register(0, 0xffff), True)
        self.assertEqual(self.client.read_input_registers(0), [0xffff])
        # multi-write at max size
        words_l = [randint(0, 0xffff)] * 0x7b
        self.assertEqual(self.client.write_multiple_registers(0, words_l),
                         True)
        self.assertEqual(self.client.read_holding_registers(0, len(words_l)),
                         words_l)
        self.assertEqual(self.client.read_input_registers(0, len(words_l)),
                         words_l)
        # write over sized
        words_l = [randint(0, 0xffff)] * 0x7c
        self.assertEqual(self.client.write_multiple_registers(0, words_l),
                         None)
        # bit space
        self.assertEqual(self.client.read_coils(0), [False],
                         'Default value is False when server start')
        self.assertEqual(self.client.read_discrete_inputs(0), [False],
                         'Default value is False when server start')
        # single read/write
        self.assertEqual(self.client.write_single_coil(0, True), True)
        self.assertEqual(self.client.read_coils(0), [True])
        self.assertEqual(self.client.read_discrete_inputs(0), [True])
        # multi-write at min size
        bits_l = [getrandbits(1)] * 0x1
        self.assertEqual(self.client.write_multiple_coils(0, bits_l), True)
        self.assertEqual(self.client.read_coils(0, len(bits_l)), bits_l)
        self.assertEqual(self.client.read_discrete_inputs(0, len(bits_l)),
                         bits_l)
        # multi-write at max size
        bits_l = [getrandbits(1)] * 0x7b0
        self.assertEqual(self.client.write_multiple_coils(0, bits_l), True)
        self.assertEqual(self.client.read_coils(0, len(bits_l)), bits_l)
        self.assertEqual(self.client.read_discrete_inputs(0, len(bits_l)),
                         bits_l)
        # multi-write over sized
        bits_l = [getrandbits(1)] * 0x7b1
        self.assertEqual(self.client.write_multiple_coils(0, bits_l), None)
Exemplo n.º 2
0
class TestClientServer(unittest.TestCase):

    def setUp(self):
        # modbus server
        self.server = ModbusServer(port=5020, no_block=True)
        self.server.start()
        # modbus client
        self.client = ModbusClient(port=5020)
        self.client.open()

    def tearDown(self):
        self.client.close()

    def test_read_and_write(self):
        # word space
        self.assertEqual(self.client.read_holding_registers(0), [0], 'Default value is 0 when server start')
        self.assertEqual(self.client.read_input_registers(0), [0], 'Default value is 0 when server start')
        # single read/write
        self.assertEqual(self.client.write_single_register(0, 0xffff), True)
        self.assertEqual(self.client.read_input_registers(0), [0xffff])
        # multi-write at max size
        words_l = [randint(0, 0xffff)] * 0x7b
        self.assertEqual(self.client.write_multiple_registers(0, words_l), True)
        self.assertEqual(self.client.read_holding_registers(0, len(words_l)), words_l)
        self.assertEqual(self.client.read_input_registers(0, len(words_l)), words_l)
        # write over sized
        words_l = [randint(0, 0xffff)] * 0x7c
        self.assertEqual(self.client.write_multiple_registers(0, words_l), None)
        # bit space
        self.assertEqual(self.client.read_coils(0), [False], 'Default value is False when server start')
        self.assertEqual(self.client.read_discrete_inputs(0), [False], 'Default value is False when server start')
        # single read/write
        self.assertEqual(self.client.write_single_coil(0, True), True)
        self.assertEqual(self.client.read_coils(0), [True])
        self.assertEqual(self.client.read_discrete_inputs(0), [True])
        # multi-write at min size
        bits_l = [getrandbits(1)] * 0x1
        self.assertEqual(self.client.write_multiple_coils(0, bits_l), True)
        self.assertEqual(self.client.read_coils(0, len(bits_l)), bits_l)
        self.assertEqual(self.client.read_discrete_inputs(0, len(bits_l)), bits_l)
        # multi-write at max size
        bits_l = [getrandbits(1)] * 0x7b0
        self.assertEqual(self.client.write_multiple_coils(0, bits_l), True)
        self.assertEqual(self.client.read_coils(0, len(bits_l)), bits_l)
        self.assertEqual(self.client.read_discrete_inputs(0, len(bits_l)), bits_l)
        # multi-write over sized
        bits_l = [getrandbits(1)] * 0x7b1
        self.assertEqual(self.client.write_multiple_coils(0, bits_l), None)
Exemplo n.º 3
0
class ET7000:
    ranges = {
        0x00: {
            'min': -0.015,
            'max': 0.015,
            'min_code': 0x8000,
            'max_code': 0x7fff,
            'units': 'V'
        },
        0x01: {
            'min': -0.05,
            'max': 0.05,
            'min_code': 0x8000,
            'max_code': 0x7fff,
            'units': 'V'
        },
        0x02: {
            'min': -0.1,
            'max': 0.1,
            'min_code': 0x8000,
            'max_code': 0x7fff,
            'units': 'V'
        },
        0x03: {
            'min': -0.5,
            'max': 0.5,
            'min_code': 0x8000,
            'max_code': 0x7fff,
            'units': 'V'
        },
        0x04: {
            'min': -1.,
            'max': 1.,
            'min_code': 0x8000,
            'max_code': 0x7fff,
            'units': 'V'
        },
        0x05: {
            'min': -2.5,
            'max': 2.5,
            'min_code': 0x8000,
            'max_code': 0x7fff,
            'units': 'V'
        },
        0x06: {
            'min': -20.0e-3,
            'max': 20.0e-3,
            'min_code': 0x8000,
            'max_code': 0x7fff,
            'units': 'A'
        },
        0x07: {
            'units': 'A',
            'min': 4.0e-3,
            'min_code': 0x0000,
            'max_code': 0xffff,
            'max': 20.0e-3
        },
        0x08: {
            'units': 'V',
            'min': -10.,
            'max': 10.,
            'min_code': 0x8000,
            'max_code': 0x7fff,
        },
        0x09: {
            'units': 'V',
            'min_code': 0x8000,
            'max_code': 0x7fff,
            'min': -5.,
            'max': 5.
        },
        0x0A: {
            'units': 'V',
            'min_code': 0x8000,
            'max_code': 0x7fff,
            'min': -1.,
            'max': 1.
        },
        0x0B: {
            'units': 'V',
            'min_code': 0x8000,
            'max_code': 0x7fff,
            'min': -.5,
            'max': .5
        },
        0x0C: {
            'units': 'V',
            'min_code': 0x8000,
            'max_code': 0x7fff,
            'min': -.15,
            'max': .15
        },
        0x0D: {
            'min': -20.0e-3,
            'max': 20.0e-3,
            'min_code': 0x8000,
            'max_code': 0x7fff,
            'units': 'A'
        },
        0x0E: {
            'units': 'degC',
            'min_code': 0xdca2,
            'max_code': 0x7fff,
            'min': -210.0,
            'max': 760.0
        },
        0x0F: {
            'units': 'degC',
            'min_code': 0xe6d0,
            'max_code': 0x7fff,
            'min': -270.0,
            'max': 1372.0
        },
        0x10: {
            'units': 'degC',
            'min_code': 0xa99a,
            'max_code': 0x7fff,
            'min': -270.0,
            'max': 400.0
        },
        0x11: {
            'units': 'degC',
            'min_code': 0xdd71,
            'max_code': 0x7fff,
            'min': -270.0,
            'max': 1000.0
        },
        0x12: {
            'units': 'degC',
            'min_code': 0x0000,
            'max_code': 0x7fff,
            'min': 0.0,
            'max': 1768.0
        },
        0x13: {
            'units': 'degC',
            'min_code': 0x0000,
            'max_code': 0x7fff,
            'min': 0.0,
            'max': 1768.0
        },
        0x14: {
            'units': 'degC',
            'min_code': 0x0000,
            'max_code': 0x7fff,
            'min': 0.0,
            'max': 1820.0
        },
        0x15: {
            'units': 'degC',
            'min_code': 0xe56b,
            'max_code': 0x7fff,
            'min': -270.0,
            'max': 1300.0
        },
        0x16: {
            'units': 'degC',
            'min_code': 0x0000,
            'max_code': 0x7fff,
            'min': 0.0,
            'max': 2320.0
        },
        0x17: {
            'units': 'degC',
            'min_code': 0xe000,
            'max_code': 0x7fff,
            'min': -200.0,
            'max': 800.0
        },
        0x18: {
            'units': 'degC',
            'min_code': 0x8000,
            'max_code': 0x4000,
            'min': -200.0,
            'max': 100.0
        },
        0x19: {
            'units': 'degC',
            'min_code': 0xe38e,
            'max_code': 0xffff,
            'min': -200.0,
            'max': 900.0
        },
        0x1A: {
            'min': 0.0,
            'max': 20.0e-3,
            'min_code': 0x0000,
            'max_code': 0xffff,
            'units': 'A'
        },
        0x20: {
            'units': 'degC',
            'min_code': 0x8000,
            'max_code': 0x7fff,
            'min': -100.0,
            'max': 100.0
        },
        0x21: {
            'units': 'degC',
            'min_code': 0x0000,
            'max_code': 0x7fff,
            'min': 0.0,
            'max': 100.0
        },
        0x22: {
            'units': 'degC',
            'min_code': 0x0000,
            'max_code': 0x7fff,
            'min': 0.0,
            'max': 200.0
        },
        0x23: {
            'units': 'degC',
            'min_code': 0x0000,
            'max_code': 0x7fff,
            'min': 0.0,
            'max': 600.0
        },
        0x24: {
            'units': 'degC',
            'min_code': 0x8000,
            'max_code': 0x7fff,
            'min': -100.0,
            'max': 100.0
        },
        0x25: {
            'units': 'degC',
            'min_code': 0x0000,
            'max_code': 0x7fff,
            'min': 0.0,
            'max': 100.0
        },
        0x26: {
            'units': 'degC',
            'min_code': 0x0000,
            'max_code': 0x7fff,
            'min': 0.0,
            'max': 200.0
        },
        0x27: {
            'units': 'degC',
            'min_code': 0x0000,
            'max_code': 0x7fff,
            'min': 0.0,
            'max': 600.0
        },
        0x28: {
            'units': 'degC',
            'min_code': 0x999a,
            'max_code': 0x7fff,
            'min': -80.0,
            'max': 100.0
        },
        0x29: {
            'units': 'degC',
            'min_code': 0x0000,
            'max_code': 0x7fff,
            'min': 0.0,
            'max': 100.0
        },
        0x2A: {
            'units': 'degC',
            'min_code': 0xd556,
            'max_code': 0x7fff,
            'min': -200.0,
            'max': 600.0
        },
        0x2B: {
            'units': 'degC',
            'min_code': 0xeeef,
            'max_code': 0x7fff,
            'min': -20.0,
            'max': 150.0
        },
        0x2C: {
            'units': 'degC',
            'min_code': 0x0000,
            'max_code': 0x7fff,
            'min': 0.0,
            'max': 200.0
        },
        0x2D: {
            'units': 'degC',
            'min_code': 0xeeef,
            'max_code': 0x7fff,
            'min': -20.0,
            'max': 150.0
        },
        0x2E: {
            'units': 'degC',
            'min_code': 0x8000,
            'max_code': 0x7fff,
            'min': -200.0,
            'max': 200.0
        },
        0x2F: {
            'units': 'degC',
            'min_code': 0x8000,
            'max_code': 0x7fff,
            'min': -200.0,
            'max': 200.0
        },
        0x80: {
            'units': 'degC',
            'min_code': 0xd556,
            'max_code': 0x7fff,
            'min': -200.0,
            'max': 600.0
        },
        0x81: {
            'units': 'degC',
            'min_code': 0xd556,
            'max_code': 0x7fff,
            'min': -200.0,
            'max': 600.0
        },
        0x82: {
            'units': 'degC',
            'min_code': 0xd556,
            'max_code': 0x7fff,
            'min': -50.0,
            'max': 150.0
        },
        0x83: {
            'min_code': 0xd556,
            'max_code': 0x7fff,
            'units': 'degC',
            'min': -60.0,
            'max': 180.0
        },
        0x30: {
            'min_code': 0x0000,
            'max_code': 0xffff,
            'min': 0.0,
            'max': 20.0e-3,
            'units': 'A'
        },
        0x31: {
            'min_code': 0x0000,
            'max_code': 0xffff,
            'min': 4.0e-3,
            'max': 20.0e-3,
            'units': 'A'
        },
        0x32: {
            'min_code': 0x0000,
            'max_code': 0x7fff,
            'min': 0.0,
            'max': 10.0,
            'units': 'V'
        },
        0x33: {
            'min': -10.0,
            'max': 10.0,
            'min_code': 0x8000,
            'max_code': 0x7fff,
            'units': 'V'
        },
        0x34: {
            'min': 0.0,
            'max': 5.0,
            'min_code': 0x0000,
            'max_code': 0x7fff,
            'units': 'V'
        },
        0x35: {
            'min': -5.0,
            'max': 5.0,
            'min_code': 0x8000,
            'max_code': 0x7fff,
            'units': 'V'
        },
        0xff: {
            'min': 0,
            'max': 0xffff,
            'min_code': 0x0000,
            'max_code': 0xffff,
            'units': '?'
        }
    }
    devices = {0x7015: {}, 0x7016: {}, 0x7018: {}, 0x7060: {}, 0x7026: {}}

    @staticmethod
    def range(r):
        if r in ET7000.ranges:
            return (ET7000.ranges[r])
        return ET7000.ranges[0xff]

    # default conversion from quanta to real units
    @staticmethod
    def convert(b, amin, amax):
        b = float(b)
        # обрабатывается 2 случая - минимум нулевой или больше 0
        if amin >= 0 and amax > 0:
            return amin + (amax - amin) * b / 0xffff
        # и минимум  и максимум разного знака
        if amin < 0 and amax > 0:
            range = max(-amin, amax)
            if b <= 0x7fff:
                return range * b / 0x7fff
            else:
                return range * (0x8000 - b) / 0x8000
        # в других случаях ошибка
        return float('nan')

    @staticmethod
    def ai_convert_function(r):
        v_min = 0
        v_max = 0xffff
        c_min = 0
        c_max = 0xffff
        try:
            v_min = ET7000.ranges[r]['min']
            v_max = ET7000.ranges[r]['max']
            c_min = ET7000.ranges[r]['min_code']
            c_max = ET7000.ranges[r]['max_code']
        except:
            pass
        if c_min < c_max:
            k = (v_max - v_min) / (c_max - c_min)
            b = v_min - k * c_min
            return lambda x: k * x + b
        k_max = v_max / c_max
        k_min = v_min / (0x10000 - c_min)
        #return lambda x: (x < 0x8000) * k_max * x + (x >= 0x8000) *  k_min * (0x10000 - x)
        return lambda x: k_max * x if x < 0x8000 else k_min * (0x10000 - x)

    @staticmethod
    def ao_convert_function(r):
        v_min = 0
        v_max = 0xffff
        c_min = 0
        c_max = 0xffff
        try:
            v_min = ET7000.ranges[r]['min']
            v_max = ET7000.ranges[r]['max']
            c_min = ET7000.ranges[r]['min_code']
            c_max = ET7000.ranges[r]['max_code']
        except:
            pass
        #print(hex(r), v_min, v_max, c_min, c_max)
        if c_min < c_max:
            k = (c_max - c_min) / (v_max - v_min)
            b = c_min - k * v_min
            return lambda x: int(k * x + b)
        k_max = c_max / v_max
        k_min = (0xffff - c_min) / v_min
        #return lambda x: int((x >= 0) * k_max * x + (x < 0) * (0xffff - k_min * x))
        return lambda x: int(k_max * x) if (x >= 0) else int(0xffff - k_min * x
                                                             )

    @staticmethod
    def convert_to_raw(v, amin, amax):
        v = float(v)
        # обрабатывается 2 случая - минимум нулевой или больше 0
        if amin >= 0 and amax > 0:
            return int((v - amin) / (amax - amin) * 0xffff)
        # и минимум  и максимум разного знака
        if amin < 0 and amax > 0:
            if v >= 0.0:
                return int(v * 0x7fff / amax)
            else:
                return int(0x8000 - v / amax * 0x7fff)
        # в других случаях ошибка
        return 0

    def __init__(self, host, port=502, timeout=0.15, logger=None):
        self.host = host
        self.port = port
        # logger confid
        if logger is None:
            logger = logging.getLogger(__name__)
        self.logger = logger
        # default device type
        self._name = 0
        self.type = '0000'
        # default ai
        self.AI_n = 0
        self.AI_masks = []
        self.AI_ranges = []
        self.AI_min = []
        self.AI_max = []
        self.AI_units = []
        self.AI_raw = []
        self.AI_values = []
        # default ao
        self.AO_n = 0
        self.AO_masks = []
        self.AO_ranges = []
        self.AO_min = []
        self.AO_max = []
        self.AO_units = []
        self.AO_raw = []
        self.AO_values = []
        self.AO_write_raw = []
        self.AO_write_values = []
        self.AO_write_result = False
        # default di
        self.DI_n = 0
        self.DI_values = []
        # default do
        self.DO_n = 0
        self.DO_values = []
        # modbus client
        self._client = ModbusClient(host,
                                    port,
                                    auto_open=True,
                                    auto_close=True,
                                    timeout=timeout)
        status = self._client.open()
        if not status:
            self.logger.error('ET7000 device at %s is offline' % host)
            return
        # read module name
        self._name = self.read_module_name()
        self.type = hex(self._name).replace('0x', '')
        if self._name not in ET7000.devices:
            self.logger.warning(
                'ET7000 device type %s probably not supported' %
                hex(self._name))
        # ai
        self.AI_n = self.read_AI_n()
        self.AI_masks = [False] * self.AI_n
        self.AI_ranges = [0xff] * self.AI_n
        self.AI_raw = [0] * self.AI_n
        self.AI_values = [float('nan')] * self.AI_n
        self.AI_units = [''] * self.AI_n
        self.read_AI_masks()
        self.read_AI_ranges()
        self.AI_convert = [lambda x: x] * self.AI_n
        for n in range(self.AI_n):
            r = self.AI_ranges[n]
            self.AI_units[n] = ET7000.ranges[r]['units']
            self.AI_convert[n] = ET7000.ai_convert_function(r)
        # ao
        self.AO_n = self.read_AO_n()
        self.AO_masks = [True] * self.AO_n
        self.read_AO_masks()
        self.AO_ranges = [0xff] * self.AO_n
        self.AO_raw = [0] * self.AO_n
        self.AO_values = [float('nan')] * self.AO_n
        self.AO_write_values = [float('nan')] * self.AO_n
        self.AO_units = [''] * self.AO_n
        self.AO_write = [0] * self.AO_n
        self.AO_write_raw = [0] * self.AO_n
        self.read_AO_ranges()
        self.AO_convert = [lambda x: x] * self.AI_n
        self.AO_convert_write = [lambda x: 0] * self.AI_n
        for n in range(self.AO_n):
            r = self.AO_ranges[n]
            self.AO_units[n] = ET7000.ranges[r]['units']
            self.AO_convert[n] = ET7000.ai_convert_function(
                r)  # !!! ai_convert for reading
            self.AO_convert_write[n] = ET7000.ao_convert_function(
                r)  # !!! ao_convert for writing
        # di
        self.DI_n = self.read_DI_n()
        self.DI_values = [False] * self.DI_n
        # do
        self.DO_n = self.read_DO_n()
        self.DO_values = [False] * self.DO_n
        self.DO_write = [False] * self.DO_n

    def read_module_name(self):
        regs = self._client.read_holding_registers(559, 1)
        if regs and regs[0] != 0:
            return regs[0]
        regs = self._client.read_holding_registers(260, 1)
        if regs:
            return regs[0]
        return 0

    # AI functions
    def read_AI_n(self):
        regs = self._client.read_input_registers(320, 1)
        if regs and regs[0] != 0:
            return regs[0]
        regs = self._client.read_input_registers(120, 1)
        if regs:
            return regs[0]
        return 0

    def read_AI_masks(self):
        coils = self._client.read_coils(595, self.AI_n)
        if coils and len(coils) == self.AI_n:
            self.AI_masks = coils
        return coils

    def read_AI_ranges(self):
        regs = self._client.read_holding_registers(427, self.AI_n)
        if regs and len(regs) == self.AI_n:
            self.AI_ranges = regs
        return regs

    def read_AI_raw(self, channel=None):
        if channel is None:
            n = self.AI_n
            channel = 0
        else:
            n = 1
        regs = self._client.read_input_registers(0 + channel, n)
        if regs and len(regs) == n:
            self.AI_raw[channel:channel + n] = regs
        if n == 1:
            return regs[0]
        return regs

    def convert_AI(self):
        for k in range(self.AI_n):
            if self.AI_masks[k]:
                self.AI_values[k] = self.AI_convert[k](self.AI_raw[k])
            else:
                self.AI_values[k] = float('nan')
        return self.AI_values

    def read_AI(self, channel=None):
        if channel is None:
            self.read_AI_raw()
            self.convert_AI()
            return self.AI_values
        return self.read_AI_channel(channel)

    def read_AI_channel(self, k: int):
        v = float('nan')
        if self.AI_masks[k]:
            regs = self._client.read_input_registers(0 + k, 1)
            if regs:
                self.AI_raw[k] = regs[0]
                v = self.AI_convert[k](regs[0])
        self.AI_values[k] = v
        return v

    # AO functions
    def read_AO_n(self):
        regs = self._client.read_input_registers(330, 1)
        if regs and regs[0] != 0:
            return regs[0]
        regs = self._client.read_input_registers(130, 1)
        if regs:
            return regs[0]
        return 0

    def read_AO_masks(self):
        return self.AO_masks

    def read_AO_ranges(self):
        regs = self._client.read_holding_registers(459, self.AO_n)
        if regs and len(regs) == self.AO_n:
            self.AO_ranges = regs
        return regs

    def read_AO_raw(self, channel=None):
        if channel is None:
            n = self.AO_n
            channel = 0
        else:
            n = 1
        regs = self._client.read_holding_registers(0 + channel, n)
        if regs and len(regs) == n:
            self.AO_raw[channel:channel + n] = regs
        if n == 1:
            return regs[0]
        return regs

    def write_AO_raw(self, regs):
        result = self._client.write_multiple_registers(0, regs)
        self.AO_write_result = result
        if len(regs) == self.AO_n:
            self.AO_write_raw = regs
        return result

    def convert_AO(self):
        raw = self.AO_raw
        for k in range(self.AO_n):
            self.AO_values[k] = self.AO_convert[k](raw[k])
        return self.AO_values

    def convert_to_raw_AO(self, values=None):
        if values is None:
            values = self.AO_write_values
        answer = []
        for k in range(len(values)):
            answer.append(self.AO_convert_write[k](values[k]))
        return answer

    def read_AO(self):
        self.AO_raw = self.read_AO_raw()
        self.convert_AO()
        return self.AO_values

    def read_AO_channel(self, k: int):
        v = float('nan')
        if self.AO_masks[k]:
            regs = self._client.read_holding_registers(0 + k, 1)
            if regs:
                v = self.AO_convert[k](regs[0])
                self.AO_values[k] = v
        return v

    def write_AO(self, values):
        self.AO_write_values = values
        regs = ET7000.convert_to_raw_AO(values)
        result = self.write_AO_raw(regs)
        return result

    def write_AO_channel(self, k: int, value):
        raw = self.AO_convert_write[k](value)
        result = self._client.write_single_register(0 + k, raw)
        self.AO_write_result = result
        if result:
            self.AO_write_values[k] = value
            self.AO_write_raw[k] = raw
            pass
        return result

    # DI functions
    def read_DI_n(self):
        regs = self._client.read_input_registers(300, 1)
        if regs and regs[0] != 0:
            return regs[0]
        regs = self._client.read_input_registers(100, 1)
        if regs:
            return regs[0]
        return 0

    def read_DI(self):
        regs = self._client.read_discrete_inputs(0, self.DI_n)
        if regs:
            self.DI_values = regs
        return self.DI_values

    def read_DI_channel(self, k: int):
        reg = self._client.read_discrete_inputs(0 + k, 1)
        if reg:
            self.DI_values[k] = reg[0]
            return reg[0]
        return None

    # DO functions
    def read_DO_n(self):
        self.DO_time = time.time()
        regs = self._client.read_input_registers(310, 1)
        if regs and regs[0] != 0:
            return regs[0]
        regs = self._client.read_input_registers(110, 1)
        if regs:
            return regs[0]
        return 0

    def read_DO(self):
        regs = self._client.read_coils(0, self.DI_n)
        if regs:
            self.DI_values = regs
        return self.DI_values

    def read_DO_channel(self, k: int):
        reg = self._client.read_coils(0 + k, 1)
        if reg:
            self.DO_values[k] = reg[0]
            return reg[0]
        return None

    def write_DO(self, values):
        self.DO_write = values
        self.DO_write_result = self._client.write_multiple_coils(0, values)
        return self.DO_write_result

    def write_DO_channel(self, k: int, value: bool):
        result = self._client.write_single_coil(0 + k, value)
        self.DO_write_result = result
        if result:
            self.DO_write[k] = value
        return result
Exemplo n.º 4
0
class TestClientServer(unittest.TestCase):
    port = 5020

    def setUp(self):
        # modbus server
        self.server = ModbusServer(port=TestClientServer.port, no_block=True)
        self.server.start()
        # modbus client
        self.client = ModbusClient(port=TestClientServer.port)
        self.client.open()
        # to prevent address taken errors
        TestClientServer.port += 1

    def tearDown(self):
        self.client.close()

    @repeat
    def test_word_init(self):
        # word space
        self.assertEqual(self.client.read_holding_registers(0), [0],
                         'Default value is 0 when server start')
        self.assertEqual(self.client.read_input_registers(0), [0],
                         'Default value is 0 when server start')

    @repeat
    def test_word_single(self):
        # single read/write
        self.assertEqual(self.client.write_single_register(0, 0xffff), True)
        self.assertEqual(self.client.read_input_registers(0), [0xffff])

    @repeat
    def test_word_multi(self):
        # multi-write at max size
        words_l = [randint(0, 0xffff)] * 0x7b
        self.assertEqual(self.client.write_multiple_registers(0, words_l),
                         True)
        self.assertEqual(self.client.read_holding_registers(0, len(words_l)),
                         words_l)
        self.assertEqual(self.client.read_input_registers(0, len(words_l)),
                         words_l)

    @repeat
    def test_word_oversize(self):
        # write over sized
        words_l = [randint(0, 0xffff)] * 0x7c
        self.assertEqual(self.client.write_multiple_registers(0, words_l),
                         None)

    @repeat
    def test_bit_init(self):
        # bit space
        self.assertEqual(self.client.read_coils(0), [False],
                         'Default value is False when server start')
        self.assertEqual(self.client.read_discrete_inputs(0), [False],
                         'Default value is False when server start')

    @repeat
    def test_bit_single(self):
        # single read/write
        self.assertEqual(self.client.write_single_coil(0, True), True)
        self.assertEqual(self.client.read_coils(0), [True])
        self.assertEqual(self.client.read_discrete_inputs(0), [True])

    @repeat
    def test_bit_multi_min(self):
        # multi-write at min size
        bits_l = [getrandbits(1)] * 0x1
        self.assertEqual(self.client.write_multiple_coils(0, bits_l), True)
        self.assertEqual(self.client.read_coils(0, len(bits_l)), bits_l)
        self.assertEqual(self.client.read_discrete_inputs(0, len(bits_l)),
                         bits_l)

    @repeat
    def test_bit_multi_max(self):
        # multi-write at max size
        bits_l = [getrandbits(1)] * 0x7b0
        self.assertEqual(self.client.write_multiple_coils(0, bits_l), True)
        self.assertEqual(self.client.read_coils(0, len(bits_l)), bits_l)
        self.assertEqual(self.client.read_discrete_inputs(0, len(bits_l)),
                         bits_l)

    @repeat
    def test_bit_multi_oversize(self):
        # multi-write over sized
        bits_l = [getrandbits(1)] * 0x7b1
        self.assertEqual(self.client.write_multiple_coils(0, bits_l), None)
Exemplo n.º 5
0
    #numCoilsTest = random.randrange(2, 73)
    numCoilsTest = 72
    #print('numCoilsTest = ' + str(numCoilsTest))

    # get the coil bits data to write
    coil_bits_write.clear()
    for i in range(0, numCoilsTest):
        coil_bits_write.append(bool(random.getrandbits(1)))

    #print(coil_bits_write)

    # write coils
    # if open() is ok, read coils (modbus function 0x01)
    addr = 0
    if c.is_open():
        is_ok = c.write_multiple_coils(addr, coil_bits_write)
        if not is_ok:
            print('Dummy')
            print("bit #" + str(addr) + ": unable to write " + str(toggle))
            print('numCoilsTest = ' + str(numCoilsTest), end='\r')
            break

    #time.sleep(1)
    # if open() is ok, read coils (modbus function 0x01)
    addr = 72
    if c.is_open():
        # read 10 bits at address 0, store result in regs list
        coil_bits_read.clear()
        #coil_bits_read = c.read_coils(addr, numCoilsTest)
        coil_bits_read = c.read_discrete_inputs(addr, numCoilsTest)
        # if success display registers
Exemplo n.º 6
0
class Beckhoff(Adapter):
    """Beckhoff Modbus/TCP bus coupler (e.g. BK9000)"""
    def __init__(self, ip_address):
        super(Beckhoff, self).__init__(ip_address)
        self.mb_client = ModbusClient(host=ip_address, timeout=5)

    def start(self):
        while True:
            self.mb_client.open()
            if self.mb_client.is_open():
                break

            print 'Unable to connect to Beckhoff rack at {}; retrying...'.format(
                self.ip_address)
            sleep(1)

    def stop(self):
        self.mb_client.close()

    def read_all(self):
        # print 'Read inputs'
        if self.d_ins:
            data = self.mb_client.read_discrete_inputs(
                self.d_in_range[0],
                self.d_in_range[1] - self.d_in_range[0] + 1)
            if not data:
                raise ConnectionError('No data received for DIns')

            i0 = self.d_in_range[
                0]  # Starting index for looking up values from the data list
            for d in self.d_ins.values():
                d.val = data[d.address - i0]

        if self.a_ins:
            # print '\tRead input registers', self.a_in_range[0], self.a_in_range[1] - self.a_in_range[0] + 1
            data = self.mb_client.read_input_registers(
                self.a_in_range[0],
                self.a_in_range[1] - self.a_in_range[0] + 1)
            if not data:
                raise ConnectionError('No data received from AIns')

            i0 = self.a_in_range[
                0]  # Starting index for looking up values from the data list
            for d in self.a_ins.values():
                d.status = data[d.address - i0]
                d.val = data[d.address - i0 + 1]

    def write_all(self):
        # print 'Write outputs'
        if self.d_outs:
            data = []
            for i in xrange(self.d_out_range[0], self.d_out_range[1] + 1):
                try:
                    data.append(self.d_outs[i].raw)
                except KeyError:
                    data.append(0)  # Default value

            self.mb_client.write_multiple_coils(self.d_out_range[0], data)

        if self.a_outs:
            data = []
            for d in self.a_outs.values():
                data.append(0)
                data.append(d.raw)  # Only write to low words

            self.mb_client.write_multiple_registers(self.a_out_range[0], data)
Exemplo n.º 7
0
def modbus_polling_thread():
    global DATA_TO_HMI_HOLDING, DATA_FROM_HMI_HOLDING, DATA_FROM_HMI_COIL, DATA_TO_HMI_COIL
    global start_addr, test_signal_coil, test_signal_holding
    global addr_num
    c = ModbusClient(host=MODBUS_SPEC['SERVER_HMI_HOST'],
                     port=MODBUS_SPEC['SERVER_HMI_PORT'])
    # polling loop
    while True:
        # keep TCP open
        if not c.is_open():
            c.open()
        # read data from hmi(holding & coil)
        DATA_FROM_HMI_HOLDING = c.read_holding_registers(
            MDS_ADDR_R['addr_start_holding_R'],
            MDS_ADDR_R['addr_num_holding_R'])
        # if read is ok, store result in regs (with thread lock synchronization)
        #time.sleep(0.)
        DATA_FROM_HMI_COIL = c.read_coils(MDS_ADDR_R['addr_start_coil_R'],
                                          MDS_ADDR_R['addr_num_coil_R'])
        print('Holding %s COil %s', DATA_FROM_HMI_HOLDING, DATA_FROM_HMI_COIL)
        print('reading form mds slave')
        if DATA_FROM_HMI_HOLDING:
            with regs_lock:
                print('with regs_lock_holding')
                #for demonstrate usage
                #DATA_FROM_HMI = list(DATA_FROM_HMI_HOLDING)
                ###
                holding_payload_to_ros = {}
                #publish holding register data to ros driver
                if mqtt_client is not None:
                    #insert data from holding register
                    for x in range(0, MDS_ADDR_R['addr_num_holding_R']):
                        holding_payload_to_ros[
                            str(MDS_ADDR_R['addr_start_holding_R'] +
                                x)] = DATA_FROM_HMI_HOLDING[x]
                        #transfer dict to string with json.dumps(payload_)
                    #print('holding payload to ros driver %s',sort_key(holding_payload_to_ros))
                    input_holding = json.dumps(holding_payload_to_ros)
                    #transfer string to string with json.loads(input)
                    mqtt_client.publish(topic='DATA_TO_ROS_DRIVER_HOLDING_R',
                                        payload=input_holding,
                                        qos=2,
                                        retain=False)
                    time.sleep(0.05)
        if DATA_FROM_HMI_COIL:
            #insert data from coil register
            coil_payload_to_ros = {}
            for y in range(0, MDS_ADDR_R['addr_num_coil_R']):
                coil_payload_to_ros[str(MDS_ADDR_R['addr_start_coil_R'] +
                                        y)] = DATA_FROM_HMI_COIL[y]
            input_coil = json.dumps(coil_payload_to_ros)
            #print('coil payload to ros driver %s',sort_key(coil_payload_to_ros))
            if mqtt_client is not None:
                mqtt_client.publish(topic='DATA_TO_ROS_DRIVER_COIL_R',
                                    payload=input_coil,
                                    qos=2,
                                    retain=False)

        else:
            print('listenning to holding register fail')

        #write data to hmi(holding & coil)
        with msg_lock_w:
            print('with msg_lock_coiling')
            print('DATA_TO_HMI_HOLDING %s', DATA_TO_HMI_HOLDING)
            if DATA_TO_HMI_HOLDING is not None and len(
                    DATA_TO_HMI_HOLDING) is not 0 and len(
                        DATA_TO_HMI_HOLDING
                    ) == MDS_ADDR_W['addr_num_holding_W']:
                print('SDSDASD %s', MDS_ADDR_W['addr_start_holding_W'])
                if c.write_multiple_registers(
                        MDS_ADDR_W['addr_start_holding_W'],
                        DATA_TO_HMI_HOLDING):
                    print('write holding ok from addr %s with list %s',
                          MDS_ADDR_W['addr_start_holding_W'],
                          DATA_TO_HMI_HOLDING)
                else:
                    print('write holding error from addr %s with list %s',
                          MDS_ADDR_W['addr_start_holding_W'],
                          DATA_TO_HMI_HOLDING)
            else:
                print('holding data missing with %s with desired len %s',
                      DATA_TO_HMI_HOLDING, MDS_ADDR_W['addr_num_holding_W'])
            time.sleep(0.1)
            if DATA_TO_HMI_COIL is not None and len(
                    DATA_TO_HMI_COIL) is not 0 and len(
                        DATA_TO_HMI_COIL) == MDS_ADDR_W['addr_num_coil_W']:
                print(MDS_ADDR_W['addr_start_coil_W'])
                is_ok = c.write_multiple_coils(MDS_ADDR_W['addr_start_coil_W'],
                                               DATA_TO_HMI_COIL)
                if is_ok:
                    print('write coil ok from addr %s with list %s',
                          MDS_ADDR_W['addr_start_coil_W'], DATA_TO_HMI_COIL)
                else:
                    print('write coil error from addr %s with list %s',
                          MDS_ADDR_W['addr_start_coil_W'], DATA_TO_HMI_COIL)
            else:
                print('coil data missing with %s with desired len %s',
                      DATA_TO_HMI_COIL, MDS_ADDR_W['addr_num_coil_W'])
            #write something to HMI
        time.sleep(0.1)