Skip to content

Commit 4c6fabf

Browse files
author
krish_trip
committed
BEDGE-2726 merge conflict resolve
2 parents 5883bbc + cb786d2 commit 4c6fabf

File tree

4 files changed

+188
-36
lines changed

4 files changed

+188
-36
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,3 +63,6 @@ target/
6363

6464
#IniFiles
6565
*.ini
66+
67+
#JSONFiles
68+
*.json

modbus_simulator/main.py

Lines changed: 166 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
from modbus_simulator.utils.backgroundJob import BackgroundJob
1919
import re
2020
import os
21+
from json import load, dump
2122
from kivy.config import Config
2223
from kivy.lang import Builder
2324
import modbus_simulator.ui.datamodel
@@ -30,11 +31,14 @@
3031
'input registers': 'input_registers',
3132
'holding registers': 'holding_registers'
3233
}
34+
3335
settings_icon = resource_filename(__name__, "assets/Control-Panel.png")
3436
app_icon = resource_filename(__name__, "assets/riptideLogo.png")
3537
modbus_template = resource_filename(__name__, "templates/modbussimu.kv")
3638
Builder.load_file(modbus_template)
3739

40+
SLAVES_FILE = resource_filename(__name__, "slaves.json")
41+
3842

3943
class FloatInput(TextInput):
4044
pat2 = re.compile(r'\d+(?:,\d+)?')
@@ -58,6 +62,9 @@ class Gui(BoxLayout):
5862
# Checkbox to select between tcp/serial
5963
interfaces = ObjectProperty()
6064

65+
tcp = ObjectProperty()
66+
serial = ObjectProperty()
67+
6168
# Boxlayout to hold interface settings
6269
interface_settings = ObjectProperty()
6370

@@ -93,6 +100,8 @@ class Gui(BoxLayout):
93100
settings = ObjectProperty()
94101
riptide_logo = ObjectProperty()
95102

103+
reset_sim_btn = ObjectProperty()
104+
96105
# Helpers
97106
# slaves = ["%s" %i for i in xrange(1, 248)]
98107
_data_map = {"tcp": {}, "rtu": {}}
@@ -292,35 +301,41 @@ def _create_modbus_device(self):
292301

293302
def start_server(self, btn):
294303
if btn.state == "down":
295-
self._create_modbus_device()
296-
297-
self.modbus_device.start()
298-
self.server_running = True
299-
self.interface_settings.disabled = True
300-
self.interfaces.disabled = True
301-
self.slave_pane.disabled = False
302-
if len(self.slave_list.adapter.selection):
303-
self.data_model_loc.disabled = False
304-
if self.simulating:
305-
self._simulate()
306-
304+
self._start_server()
307305
btn.text = "Stop"
308-
309306
else:
310-
self.simulating = False
311-
self._simulate()
312-
self.modbus_device.stop()
313-
self.server_running = False
314-
self.interface_settings.disabled = False
315-
self.interfaces.disabled = False
316-
self.slave_pane.disabled = True
317-
self.data_model_loc.disabled = True
307+
self._stop_server()
318308
btn.text = "Start"
319309

310+
def _start_server(self):
311+
self._create_modbus_device()
312+
313+
self.modbus_device.start()
314+
self.server_running = True
315+
self.interface_settings.disabled = True
316+
self.interfaces.disabled = True
317+
self.slave_pane.disabled = False
318+
if len(self.slave_list.adapter.selection):
319+
self.data_model_loc.disabled = False
320+
if self.simulating:
321+
self._simulate()
322+
323+
def _stop_server(self):
324+
self.simulating = False
325+
self._simulate()
326+
self.modbus_device.stop()
327+
self.server_running = False
328+
self.interface_settings.disabled = False
329+
self.interfaces.disabled = False
330+
self.slave_pane.disabled = True
331+
self.data_model_loc.disabled = True
332+
320333
def update_tcp_connection_info(self, checkbox, value):
321334
self.active_server = "tcp"
322335
if value:
323336
self.interface_settings.current = checkbox
337+
if self.last_active_port['tcp'] == "":
338+
self.last_active_port['tcp'] = 5440
324339
self.port.text = self.last_active_port['tcp']
325340
self._restore()
326341
else:
@@ -335,7 +350,6 @@ def update_serial_connection_info(self, checkbox, value):
335350
self.last_active_port['serial'] = '/dev/ptyp0'
336351
self.port.text = self.last_active_port['serial']
337352
self._restore()
338-
339353
else:
340354
self.last_active_port['serial'] = self.port.text
341355
self._backup()
@@ -351,10 +365,14 @@ def add_slaves(self, *args):
351365
selected = self.slave_list.adapter.selection
352366
data = self.slave_list.adapter.data
353367
ret = self._process_slave_data(data)
368+
self._add_slaves(selected, data, ret)
369+
370+
def _add_slaves(self, selected, data, ret):
354371
if ret[0]:
355372
start_slave_add, slave_count = ret[1:]
356373
else:
357374
return
375+
358376
for slave_to_add in xrange(start_slave_add,
359377
start_slave_add + slave_count):
360378
if str(slave_to_add) in self.data_map:
@@ -388,7 +406,7 @@ def add_slaves(self, *args):
388406
self.modbus_device.add_slave(slave_to_add)
389407
for block_name, block_type in BLOCK_TYPES.items():
390408
self.modbus_device.add_block(slave_to_add,
391-
block_name, block_type, self.block_start, self.block_size)
409+
block_name, block_type, self.block_start, self.block_size)
392410

393411
data.append(str(slave_to_add))
394412
self.slave_list.adapter.data = data
@@ -462,25 +480,32 @@ def delete_slaves(self, *args):
462480
self.data_map.pop(slave)
463481

464482
def update_data_models(self, *args):
465-
ct = self.data_models.current_tab
483+
active = self.active_slave
484+
tab = self.data_models.current_tab
485+
count = self.data_count.text
486+
self._update_data_models(active, tab, count, 1)
487+
488+
def _update_data_models(self, active, tab, count, value):
489+
ct = tab
466490
current_tab = MAP[ct.text]
467491

468492
ct.content.update_view()
469493
# self.data_map[self.active_slave][current_tab]['dirty'] = False
470-
_data = self.data_map[self.active_slave][current_tab]
494+
_data = self.data_map[active][current_tab]
471495
item_strings = _data['item_strings']
472-
for i in xrange(int(self.data_count.text)):
496+
for i in xrange(int(count)):
473497
if len(item_strings) < self.block_size:
474-
updated_data, item_strings = ct.content.add_data(1, item_strings)
498+
_value = 1 if isinstance(value, int) else value[i]
499+
updated_data, item_strings = ct.content.add_data(_value, item_strings)
475500
_data['data'].update(updated_data)
476501
_data['item_strings'] = item_strings
477502
for k, v in updated_data.iteritems():
478-
self.modbus_device.set_values(int(self.active_slave),
503+
self.modbus_device.set_values(int(active),
479504
current_tab, k, v)
480505
else:
481506
msg = ("OutOfModbusBlockError: address %s"
482-
" is out of block size %s" %(len(item_strings),
483-
self.block_size))
507+
" is out of block size %s" % (len(item_strings),
508+
self.block_size))
484509
self.show_error(msg)
485510
break
486511

@@ -572,8 +597,10 @@ def change_datamodel_settings(self, key, value):
572597
def start_stop_simulation(self, btn):
573598
if btn.state == "down":
574599
self.simulating = True
600+
self.reset_sim_btn.disabled = True
575601
else:
576602
self.simulating = False
603+
self.reset_sim_btn.disabled = False
577604
if self.restart_simu:
578605
self.restart_simu = False
579606
self._simulate()
@@ -585,6 +612,13 @@ def _simulate(self):
585612
self.data_model_holding_registers.start_stop_simulation(
586613
self.simulating)
587614

615+
def reset_simulation(self, *args):
616+
if not self.simulating:
617+
self.data_model_coil.reset_block_values()
618+
self.data_model_discrete_inputs.reset_block_values()
619+
self.data_model_input_registers.reset_block_values()
620+
self.data_model_holding_registers.reset_block_values()
621+
588622
def _sync_modbus_block_values(self):
589623
"""
590624
track external changes in modbus block values and sync GUI
@@ -633,6 +667,90 @@ def _restore(self):
633667
self.slave_count.text) = self._slave_misc[self.active_server]
634668
self.slave_list._trigger_reset_populate()
635669

670+
def save_state(self):
671+
with open(SLAVES_FILE, 'w') as f:
672+
slave = [int(slave_no) for slave_no in self.slave_list.adapter.data]
673+
slaves_memory = []
674+
for slaves, mem in self.data_map.iteritems():
675+
for name, value in mem.iteritems():
676+
if len(value['data']) != 0:
677+
slaves_memory.append((slaves, name, map(int, value['data'].values())))
678+
679+
dump(dict(
680+
slaves_list=slave, active_server=self.active_server,
681+
port=self.port.text, slaves_memory=slaves_memory
682+
), f, indent=4)
683+
684+
def load_state(self):
685+
if not bool(eval(self.config.get("State", "load state"))) or \
686+
not os.path.isfile(SLAVES_FILE):
687+
return
688+
689+
with open(SLAVES_FILE, 'r') as f:
690+
try:
691+
data = load(f)
692+
except ValueError as e:
693+
self.show_error(
694+
"LoadError: Failed to load previous simulation state : %s "
695+
% e.message
696+
)
697+
return
698+
699+
if 'active_server' not in data or 'port' not in data \
700+
or 'slaves_list' not in data or 'slaves_memory' not in data:
701+
self.show_error("LoadError: Failed to load previous simulation state : JSON Key "
702+
"Missing")
703+
return
704+
705+
slaves_list = data['slaves_list']
706+
if not len(slaves_list):
707+
return
708+
709+
if data['active_server'] == 'tcp':
710+
self.tcp.active = True
711+
self.serial.active = False
712+
self.interface_settings.current = self.tcp
713+
else:
714+
self.tcp.active = False
715+
self.serial.active = True
716+
self.interface_settings.current = self.serial
717+
718+
self.active_server = data['active_server']
719+
self.port.text = data['port']
720+
721+
self._create_modbus_device()
722+
723+
start_slave = 0
724+
temp_list = []
725+
slave_count = 1
726+
for first, second in zip(slaves_list[:-1], slaves_list[1:]):
727+
if first+1 == second:
728+
slave_count += 1
729+
else:
730+
temp_list.append((slaves_list[start_slave], slave_count))
731+
start_slave += slave_count
732+
slave_count = 1
733+
temp_list.append((slaves_list[start_slave], slave_count))
734+
735+
for start_slave, slave_count in temp_list:
736+
self._add_slaves(
737+
self.slave_list.adapter.selection,
738+
self.slave_list.adapter.data,
739+
(True, start_slave, slave_count)
740+
)
741+
742+
memory_map = {
743+
'coils': self.data_models.tab_list[3],
744+
'discrete_inputs': self.data_models.tab_list[2],
745+
'input_registers': self.data_models.tab_list[1],
746+
'holding_registers': self.data_models.tab_list[0]
747+
}
748+
slaves_memory = data['slaves_memory']
749+
for slave_memory in slaves_memory:
750+
active_slave, memory_type, memory_data = slave_memory
751+
self._update_data_models(active_slave, memory_map[memory_type], len(memory_data), memory_data)
752+
753+
636754
setting_panel = """
637755
[
638756
{
@@ -821,12 +939,22 @@ def _restore(self):
821939
{
822940
"type": "numeric",
823941
"title": "Time interval",
824-
"desc": "When simulation is enabled, data is changed for eveery 'n' seconds defined here",
942+
"desc": "When simulation is enabled, data is changed for every 'n' seconds defined here",
825943
"section": "Simulation",
826944
"key": "time interval"
945+
},
946+
{
947+
"type": "title",
948+
"title": "State"
949+
},
950+
{
951+
"type": "bool",
952+
"title": "Load State",
953+
"desc": "Whether the previous state should be loaded or not, if not the original state is loaded",
954+
"section": "State",
955+
"key": "load state"
827956
}
828957
829-
830958
]
831959
"""
832960

@@ -844,6 +972,7 @@ def build(self):
844972
self.gui = Gui(
845973
modbus_log=os.path.join(self.user_data_dir, 'modbus.log')
846974
)
975+
self.gui.load_state()
847976
return self.gui
848977

849978
def on_pause(self):
@@ -856,6 +985,8 @@ def on_stop(self):
856985
self.gui._simulate()
857986
self.gui.modbus_device.stop()
858987
self.gui.sync_modbus_thread.cancel()
988+
self.config.write()
989+
self.gui.save_state()
859990

860991
def show_settings(self, btn):
861992
self.open_settings()
@@ -894,6 +1025,9 @@ def build_config(self, config):
8941025
config.add_section('Simulation')
8951026
config.set('Simulation', 'time interval', 1)
8961027

1028+
config.add_section('State')
1029+
config.set('State', 'load state', 1)
1030+
8971031
def build_settings(self, settings):
8981032
settings.register_type("numeric_range", SettingIntegerWithRange)
8991033
settings.add_json_panel('Modbus Settings', self.config,

modbus_simulator/templates/modbussimu.kv

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@
2424
data_models: data_model_screen
2525
info_label: info_lbl
2626
interfaces: interfaces
27+
tcp: chkbx
28+
serial: chkbx2
2729
port: txtBox
2830
interface_settings: interface_settings
2931
orientation: 'vertical'
@@ -42,6 +44,8 @@
4244
action_bar: action_bar
4345
settings: settings
4446
riptide_logo: riptide_logo
47+
reset_sim_btn: reset_simulation
48+
4549
BoxLayout:
4650
padding: '2sp'
4751
canvas:
@@ -73,7 +77,7 @@
7377
Label:
7478
text: "Serial"
7579
CheckBox:
76-
id: "chkbx2"
80+
id: chkbx2
7781
group: "interface"
7882
on_active: root.update_serial_connection_info(*args)
7983
Widget:
@@ -295,6 +299,10 @@
295299
with_previous: False
296300
disabled: True
297301
ActionOverflow:
302+
ActionButton:
303+
id: reset_simulation
304+
text: 'Reset Simulation'
305+
on_release: root.reset_simulation(*args)
298306
ActionToggleButton:
299307
text: 'Simulate'
300308
on_release: root.start_stop_simulation(*args)

0 commit comments

Comments
 (0)