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)
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
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)
#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
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)
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)