def __init__(self, cfg, **kwargs): super(Gui, self).__init__(**kwargs) time_interval = kwargs.get("time_interval", 1) self.slave_list.adapter.bind(on_selection_change=self.select_slave) self.data_model_loc.disabled = True self.slave_pane.disabled = True minval = kwargs.get("bin_min_val", 0) maxval = kwargs.get("bin_max_val", 1) self.data_model_coil.init( blockname="coils", simulate=self.simulating, time_interval=time_interval, minval=minval, maxval=maxval, _parent=self ) self.data_model_discrete_inputs.init( blockname="discrete_inputs", simulate=self.simulating, time_interval=time_interval, minval=minval, maxval=maxval, _parent=self ) minval = kwargs.get("reg_min_val", 0) maxval = kwargs.get("reg_max_val", 65535) self.block_start = kwargs.get("block_start", 0) self.block_size = kwargs.get("block_size", 100) self.data_model_input_registers.init( blockname="input_registers", simulate=self.simulating, time_interval=time_interval, minval=minval, maxval=maxval, _parent=self ) self.data_model_holding_registers.init( blockname="holding_registers", simulate=self.simulating, time_interval=time_interval, minval=minval, maxval=maxval, _parent=self ) self.data_model_loc.disabled = True configure_modbus_logger(cfg) self.modbus_device = ModBusSimu(port=int(self.port.text)) self.simu_time_interval = time_interval self.sync_modbus_thread = BackgroundJob( "modbus_sync", self.sync_modbus_time_interval, self._sync_modbus_block_values ) self.sync_modbus_thread.start()
class Gui(BoxLayout): ''' Gui of widgets. This is the root widget of the app. ''' # ---------------------GUI------------------------ # # Checkbox to select between tcp/serial interfaces = ObjectProperty() # Boxlayout to hold interface settings interface_settings = ObjectProperty() # TCP port port = ObjectProperty() # Toggle button to start/stop modbus server start_stop_server = ObjectProperty() # Container for slave list slave_pane = ObjectProperty() # slave start address textbox slave_start_add = ObjectProperty() # slave end address textbox slave_end_add = ObjectProperty() # Slave device count text box slave_count = ObjectProperty() # Slave list slave_list = ObjectProperty() # Container for modbus data models data_model_loc = ObjectProperty() # Tabbed panel to hold various modbus datamodels data_models = ObjectProperty() # Data models data_model_coil = ObjectProperty() data_model_discrete_inputs = ObjectProperty() data_model_input_registers = ObjectProperty() data_model_holding_registers = ObjectProperty() # Helpers slaves = ["%s" %i for i in xrange(1, 248)] data_map = {} active_slave = None server_running = False simulating = False simu_time_interval = None anim = None restart_simu = False sync_modbus_thread = None sync_modbus_time_interval = 5 def __init__(self, cfg, **kwargs): super(Gui, self).__init__(**kwargs) time_interval = kwargs.get("time_interval", 1) self.slave_list.adapter.bind(on_selection_change=self.select_slave) self.data_model_loc.disabled = True self.slave_pane.disabled = True minval = kwargs.get("bin_min_val", 0) maxval = kwargs.get("bin_max_val", 1) self.data_model_coil.init( blockname="coils", simulate=self.simulating, time_interval=time_interval, minval=minval, maxval=maxval, _parent=self ) self.data_model_discrete_inputs.init( blockname="discrete_inputs", simulate=self.simulating, time_interval=time_interval, minval=minval, maxval=maxval, _parent=self ) minval = kwargs.get("reg_min_val", 0) maxval = kwargs.get("reg_max_val", 65535) self.block_start = kwargs.get("block_start", 0) self.block_size = kwargs.get("block_size", 100) self.data_model_input_registers.init( blockname="input_registers", simulate=self.simulating, time_interval=time_interval, minval=minval, maxval=maxval, _parent=self ) self.data_model_holding_registers.init( blockname="holding_registers", simulate=self.simulating, time_interval=time_interval, minval=minval, maxval=maxval, _parent=self ) self.data_model_loc.disabled = True configure_modbus_logger(cfg) self.modbus_device = ModBusSimu(port=int(self.port.text)) self.simu_time_interval = time_interval self.sync_modbus_thread = BackgroundJob( "modbus_sync", self.sync_modbus_time_interval, self._sync_modbus_block_values ) self.sync_modbus_thread.start() def start_server(self, btn): if btn.state == "down": self.modbus_device.start() self.server_running = True self.interface_settings.disabled = True self.interfaces.disabled = True self.slave_pane.disabled = False if len(self.slave_list.adapter.selection): self.data_model_loc.disabled = False if self.simulating: self._simulate() btn.text = "Stop" else: self.simulating = False self._simulate() self.modbus_device.stop() self.server_running = False self.interface_settings.disabled = False self.interfaces.disabled = False self.slave_pane.disabled = True self.data_model_loc.disabled = True btn.text = "Start" def update_tcp_connection_info(self, checkbox, value): if value: self.interface_settings.current = checkbox tcp_label = Label(text="Port") tcp_input = TextInput(text="5440", multiline=False) self.interface_settings.add_widget(tcp_label) self.interface_settings.add_widget(tcp_input) else: self.interface_settings.clear_widgets() def update_serial_connection_info(self, checkbox, value): if value: self.interface_settings.current = checkbox serial_label = Label(text="Serial Settings not supported !!") self.interface_settings.add_widget(serial_label) else: self.interface_settings.clear_widgets() def show_error(self, e): self.info_label.text = str(e) self.anim = Animation(top=190.0, opacity=1, d=2, t='in_back') +\ Animation(top=190.0, d=3) +\ Animation(top=0, opacity=0, d=2) self.anim.start(self.info_label) def add_slaves(self, *args): selected = self.slave_list.adapter.selection data = self.slave_list.adapter.data ret = self._process_slave_data(data) if ret[0]: start_slave_add, slave_count = ret[1:] else: return for slave_to_add in xrange(start_slave_add, start_slave_add + slave_count): if str(slave_to_add) in self.data_map: return self.data_map[str(slave_to_add)] = { "coils": { 'data': {}, 'item_strings': [], "instance": self.data_model_coil, "dirty": False }, "discrete_inputs": { 'data': {}, 'item_strings': [], "instance": self.data_model_discrete_inputs, "dirty": False }, "input_registers": { 'data': {}, 'item_strings': [], "instance": self.data_model_input_registers, "dirty": False }, "holding_registers": { 'data': {}, 'item_strings': [], "instance": self.data_model_holding_registers, "dirty": False } } self.modbus_device.add_slave(slave_to_add) for block_name, block_type in BLOCK_TYPES.items(): self.modbus_device.add_block(slave_to_add, block_name, block_type, self.block_start, self.block_size) data.append(str(slave_to_add)) self.slave_list.adapter.data = data self.slave_list._trigger_reset_populate() for item in selected: index = self.slave_list.adapter.data.index(item.text) if not self.slave_list.adapter.get_view(index).is_selected: self.slave_list.adapter.get_view(index).trigger_action( duration=0 ) self.slave_start_add.text = str(start_slave_add + slave_count) self.slave_end_add.text = self.slave_start_add.text self.slave_count.text = "1" def _process_slave_data(self, data): success = True data = sorted(data, key=int) # last_slave = 1 if not len(data) else data[-1] starting_address = int(self.slave_start_add.text) end_address = int(self.slave_end_add.text) if end_address < starting_address: end_address = starting_address try: slave_count = int(self.slave_count.text) except ValueError: slave_count = 1 if str(starting_address) in data: self.show_error("slave already present (%s)" % starting_address) success = False return [success] if starting_address < 1: self.show_error("slave address (%s)" " should be greater than 0 "% starting_address) success = False return [success] if starting_address > 247: self.show_error("slave address (%s)" " beyond supported modbus slave " "device address (247)" % starting_address) success = False return [success] size = (end_address - starting_address) + 1 size = slave_count if slave_count > size else size if (size + starting_address) > 247: self.show_error("address range (%s) beyond " "allowed modbus slave " "devices(247)" % (size + starting_address)) success = False return [success] self.slave_end_add.text = str(starting_address + size - 1) self.slave_count.text = str(size) return success, starting_address, size def delete_slaves(self, *args): selected = self.slave_list.adapter.selection slave = self.active_slave ct = self.data_models.current_tab for item in selected: self.modbus_device.remove_slave(int(item.text)) self.slave_list.adapter.data.remove(item.text) self.slave_list._trigger_reset_populate() ct.content.clear_widgets(make_dirty=True) if self.simulating: self.simulating = False self.restart_simu = True self._simulate() self.data_map.pop(slave) def update_data_models(self, *args): ct = self.data_models.current_tab current_tab = MAP[ct.text] ct.content.update_view() # self.data_map[self.active_slave][current_tab]['dirty'] = False _data = self.data_map[self.active_slave][current_tab] item_strings = _data['item_strings'] if len(item_strings) < self.block_size: updated_data, item_strings = ct.content.add_data(1, item_strings) _data['data'].update(updated_data) _data['item_strings'] = item_strings for k, v in updated_data.iteritems(): self.modbus_device.set_values(int(self.active_slave), current_tab, k, v) else: msg = ("OutOfModbusBlockError: address %s" " is out of block size %s" %(len(item_strings), self.block_size)) self.show_error(msg) def sync_data_callback(self, blockname, data): ct = self.data_models.current_tab current_tab = MAP[ct.text] if blockname != current_tab: current_tab = blockname try: _data = self.data_map[self.active_slave][current_tab] _data['data'].update(data) for k, v in data.iteritems(): self.modbus_device.set_values(int(self.active_slave), current_tab, k, int(v)) except KeyError: pass def delete_data_entry(self, *args): ct = self.data_models.current_tab current_tab = MAP[ct.text] _data = self.data_map[self.active_slave][current_tab] item_strings = _data['item_strings'] deleted, data = ct.content.delete_data(item_strings) dm = _data['data'] for index in deleted: dm.pop(index, None) if deleted: self.update_backend(int(self.active_slave), current_tab, data) msg = ("modbus-tk do not support deleting " "individual modbus register/discrete_inputs/coils" "The data is removed from GUI and the corresponding value is" "updated to '0' in backend . ") self.show_error(msg) def select_slave(self, adapter): ct = self.data_models.current_tab if len(adapter.selection) != 1: # Multiple selection - No Data Update ct.content.clear_widgets(make_dirty=True) if self.simulating: self.simulating = False self.restart_simu = True self._simulate() self.data_model_loc.disabled = True self.active_slave = None else: self.data_model_loc.disabled = False if self.restart_simu: self.simulating = True self.restart_simu = False self._simulate() self.active_slave = self.slave_list.adapter.selection[0].text self.refresh() def refresh(self): for child in self.data_models.tab_list: dm = self.data_map[self.active_slave][MAP[child.text]]['data'] child.content.refresh(dm) def update_backend(self, slave_id, blockname, new_data, ): self.modbus_device.remove_block(slave_id, blockname) self.modbus_device.add_block(slave_id, blockname, BLOCK_TYPES[blockname], 0, 100) for k, v in new_data.iteritems(): self.modbus_device.set_values(slave_id, blockname, k, int(v)) def change_simulation_settings(self, **kwargs): self.data_model_coil.reinit(**kwargs) self.data_model_discrete_inputs.reinit(**kwargs) self.data_model_input_registers.reinit(**kwargs) self.data_model_holding_registers.reinit(**kwargs) def change_datamodel_settings(self, key, value): if "max" in key: data = {"maxval": int(value)} else: data = {"minval": int(value)} if "bin" in key: self.data_model_coil.reinit(**data) self.data_model_discrete_inputs.reinit(**data) else: self.data_model_input_registers.reinit(**data) self.data_model_holding_registers.reinit(**data) def start_stop_simulation(self, btn): if btn.state == "down": self.simulating = True else: self.simulating = False if self.restart_simu: self.restart_simu = False self._simulate() def _simulate(self): self.data_model_coil.start_stop_simulation(self.simulating) self.data_model_discrete_inputs.start_stop_simulation(self.simulating) self.data_model_input_registers.start_stop_simulation(self.simulating) self.data_model_holding_registers.start_stop_simulation( self.simulating) def _sync_modbus_block_values(self): """ track external changes in modbus block values and sync GUI ToDo: A better way to update GUI when simulation is on going !! """ if not self.simulating: if self.active_slave: _data_map = self.data_map[self.active_slave] for block_name, value in _data_map.items(): updated = {} for k, v in value['data'].items(): actual_data = self.modbus_device.get_values( int(self.active_slave), block_name, int(k), 1 ) if actual_data[0] != int(v): updated[k] = actual_data[0] if updated: value['data'].update(updated) self.refresh()