Skip to content

Commit 704ed23

Browse files
committed
Merged sanjaykv/modbus-simulator into master
2 parents 44ad9b2 + 558aa2c commit 704ed23

File tree

4 files changed

+139
-123
lines changed

4 files changed

+139
-123
lines changed

README.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,4 +53,7 @@ A standalone application specific to target OS can be created with Kivy package
5353
3. [Windows](http://kivy.org/docs/guide/packaging-windows.html)
5454

5555
## OSx Standalone application
56-
[Modbus-Simulator v0.0.2](https://drive.google.com/open?id=0B8MS3muxUtROTHNMcW00QkNJdDQ)
56+
[Modbus-Simulator v0.0.4](https://drive.google.com/open?id=0B8MS3muxUtROTHNMcW00QkNJdDQ)
57+
58+
# NOTE:
59+
Tested only on OSX

main.py

Lines changed: 95 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111
from kivy.uix.label import Label
1212
from kivy.uix.textinput import TextInput
1313
from kivy.uix.settings import (Settings, SettingsWithSidebar)
14+
from kivy.uix.listview import ListView, ListItemButton
15+
from kivy.adapters.listadapter import ListAdapter
1416
import DataModel
1517
from modbus import ModbusSimu, BLOCK_TYPES, configure_modbus_logger
1618
from settings import SettingIntegerWithRange
@@ -30,6 +32,7 @@
3032
class FloatInput(TextInput):
3133
pat2 = re.compile(r'\d+(?:,\d+)?')
3234
pat = re.compile('[^0-9]')
35+
3336
def insert_text(self, substring, from_undo=False):
3437
pat = self.pat
3538
if '.' in self.text:
@@ -74,14 +77,15 @@ class Gui(BoxLayout):
7477
data_models = ObjectProperty()
7578

7679
# Data models
80+
data_count = ObjectProperty()
7781
data_model_coil = ObjectProperty()
7882
data_model_discrete_inputs = ObjectProperty()
7983
data_model_input_registers = ObjectProperty()
8084
data_model_holding_registers = ObjectProperty()
8185

8286
# Helpers
83-
slaves = ["%s" %i for i in xrange(1, 248)]
84-
data_map = {}
87+
# slaves = ["%s" %i for i in xrange(1, 248)]
88+
_data_map = {"tcp": {}, "rtu": {}}
8589
active_slave = None
8690
server_running = False
8791
simulating = False
@@ -90,7 +94,9 @@ class Gui(BoxLayout):
9094
restart_simu = False
9195
sync_modbus_thread = None
9296
sync_modbus_time_interval = 5
93-
modbus_device = None
97+
_modbus_device = {"tcp": None, 'rtu': None}
98+
_slaves = {"tcp": None, "rtu": None}
99+
94100
last_active_port = {"tcp": "", "serial": ""}
95101
active_server = "tcp"
96102
_serial_settings_changed = False
@@ -131,6 +137,37 @@ def __init__(self, **kwargs):
131137
self._sync_modbus_block_values
132138
)
133139
self.sync_modbus_thread.start()
140+
self._slave_misc = {"tcp": [self.slave_start_add.text,
141+
self.slave_end_add.text,
142+
self.slave_count.text],
143+
"rtu": [self.slave_start_add.text,
144+
self.slave_end_add.text,
145+
self.slave_count.text]}
146+
147+
@property
148+
def modbus_device(self):
149+
return self._modbus_device[self.active_server]
150+
151+
@modbus_device.setter
152+
def modbus_device(self, value):
153+
self._modbus_device[self.active_server] = value
154+
155+
@property
156+
def slave(self):
157+
return self._slaves[self.active_server]
158+
159+
@slave.setter
160+
def slave(self, value):
161+
self._slaves[self.active_server] = value
162+
163+
@property
164+
def data_map(self):
165+
return self._data_map[self.active_server]
166+
167+
@data_map.setter
168+
def data_map(self, value):
169+
self._data_map[self.active_server] = value
170+
134171

135172
def _init_coils(self):
136173
time_interval = int(eval(self.config.get("Simulation",
@@ -225,29 +262,25 @@ def _create_modbus_device(self):
225262
else:
226263
create_new = True
227264
if create_new:
265+
228266
self.modbus_device = ModbusSimu(server=self.active_server,
229267
port=self.port.text,
230268
**kwargs
231269
)
270+
if self.slave is None:
271+
272+
adapter = ListAdapter(
273+
data=[],
274+
cls=ListItemButton,
275+
selection_mode='single'
276+
)
277+
self.slave = ListView(adapter=adapter)
278+
279+
self._serial_settings_changed = False
232280

233281
def start_server(self, btn):
234282
if btn.state == "down":
235283
self._create_modbus_device()
236-
# if not self.modbus_device:
237-
# self.modbus_device = ModbusSimu(server=self.active_server,
238-
# port=self.port.text
239-
# )
240-
# else:
241-
# if self.modbus_device.server_type == self.active_server:
242-
# if self.modbus_device.port != self.port.text:
243-
# self.modbus_device = ModbusSimu(
244-
# server=self.active_server,
245-
# port=self.port.text
246-
# )
247-
# else:
248-
# self.modbus_device = ModbusSimu(server=self.active_server,
249-
# port=self.port.text
250-
# )
251284

252285
self.modbus_device.start()
253286
self.server_running = True
@@ -277,14 +310,10 @@ def update_tcp_connection_info(self, checkbox, value):
277310
if value:
278311
self.interface_settings.current = checkbox
279312
self.port.text = self.last_active_port['tcp']
280-
# tcp_label = Label(text="Port")
281-
# tcp_input = TextInput(text="5440", multiline=False)
282-
# self.interface_settings.add_widget(tcp_label)
283-
# self.interface_settings.add_widget(tcp_input)
284-
# self.modbus_device = ModbusSimu(port=int(tcp_input.text))
313+
self._restore()
285314
else:
286315
self.last_active_port['tcp'] = self.port.text
287-
# self.interface_settings.clear_widgets()
316+
self._backup()
288317

289318
def update_serial_connection_info(self, checkbox, value):
290319
self.active_server = "rtu"
@@ -293,9 +322,11 @@ def update_serial_connection_info(self, checkbox, value):
293322
if self.last_active_port['serial'] == "":
294323
self.last_active_port['serial'] = '/dev/ptyp0'
295324
self.port.text = self.last_active_port['serial']
325+
self._restore()
296326

297327
else:
298328
self.last_active_port['serial'] = self.port.text
329+
self._backup()
299330

300331
def show_error(self, e):
301332
self.info_label.text = str(e)
@@ -342,7 +373,6 @@ def add_slaves(self, *args):
342373
"dirty": False
343374
}
344375
}
345-
346376
self.modbus_device.add_slave(slave_to_add)
347377
for block_name, block_type in BLOCK_TYPES.items():
348378
self.modbus_device.add_block(slave_to_add,
@@ -351,6 +381,7 @@ def add_slaves(self, *args):
351381
data.append(str(slave_to_add))
352382
self.slave_list.adapter.data = data
353383
self.slave_list._trigger_reset_populate()
384+
354385
for item in selected:
355386
index = self.slave_list.adapter.data.index(item.text)
356387
if not self.slave_list.adapter.get_view(index).is_selected:
@@ -426,18 +457,20 @@ def update_data_models(self, *args):
426457
# self.data_map[self.active_slave][current_tab]['dirty'] = False
427458
_data = self.data_map[self.active_slave][current_tab]
428459
item_strings = _data['item_strings']
429-
if len(item_strings) < self.block_size:
430-
updated_data, item_strings = ct.content.add_data(1, item_strings)
431-
_data['data'].update(updated_data)
432-
_data['item_strings'] = item_strings
433-
for k, v in updated_data.iteritems():
434-
self.modbus_device.set_values(int(self.active_slave),
435-
current_tab, k, v)
436-
else:
437-
msg = ("OutOfModbusBlockError: address %s"
438-
" is out of block size %s" %(len(item_strings),
439-
self.block_size))
440-
self.show_error(msg)
460+
for i in xrange(int(self.data_count.text)):
461+
if len(item_strings) < self.block_size:
462+
updated_data, item_strings = ct.content.add_data(1, item_strings)
463+
_data['data'].update(updated_data)
464+
_data['item_strings'] = item_strings
465+
for k, v in updated_data.iteritems():
466+
self.modbus_device.set_values(int(self.active_slave),
467+
current_tab, k, v)
468+
else:
469+
msg = ("OutOfModbusBlockError: address %s"
470+
" is out of block size %s" %(len(item_strings),
471+
self.block_size))
472+
self.show_error(msg)
473+
break
441474

442475
def sync_data_callback(self, blockname, data):
443476
ct = self.data_models.current_tab
@@ -500,7 +533,8 @@ def refresh(self):
500533
def update_backend(self, slave_id, blockname, new_data, ):
501534
self.modbus_device.remove_block(slave_id, blockname)
502535
self.modbus_device.add_block(slave_id, blockname,
503-
BLOCK_TYPES[blockname], 0, 100)
536+
BLOCK_TYPES[blockname], 0,
537+
self.block_size)
504538
for k, v in new_data.iteritems():
505539
self.modbus_device.set_values(slave_id, blockname, k, int(v))
506540

@@ -563,6 +597,29 @@ def _sync_modbus_block_values(self):
563597
value['data'].update(updated)
564598
self.refresh()
565599

600+
def _backup(self):
601+
if self.slave is not None:
602+
self.slave.adapter.data = self.slave_list.adapter.data
603+
self._slave_misc[self.active_server] = [
604+
self.slave_start_add.text,
605+
self.slave_end_add.text,
606+
self.slave_count.text
607+
]
608+
609+
def _restore(self):
610+
if self.slave is None:
611+
612+
adapter = ListAdapter(
613+
data=[],
614+
cls=ListItemButton,
615+
selection_mode='single'
616+
)
617+
self.slave = ListView(adapter=adapter)
618+
self.slave_list.adapter.data = self.slave.adapter.data
619+
(self.slave_start_add.text,
620+
self.slave_end_add.text,
621+
self.slave_count.text) = self._slave_misc[self.active_server]
622+
self.slave_list._trigger_reset_populate()
566623

567624
setting_panel = """
568625
[

modbus.py

Lines changed: 3 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -137,90 +137,9 @@ def stop(self):
137137
self._serial.close()
138138
self._server_add = ()
139139

140-
141-
class ModbusMaster(object):
142-
# to fix:
143-
# ModbusRTU Master is having issues while getting data from slave when run
144-
# from with in test cases .
145-
def __init__(self, master="tcp", *args, **kwargs):
146-
self._master = master
147-
if master == 'rtu':
148-
self._serial = PseudoSerial(kwargs['port'])
149-
kwargs.pop('port', None)
150-
kwargs['serial'] = self._serial.ser
151-
self.master = None
152-
# else:
153-
self.master = MASTERS.get(master, None)(*args, **kwargs)
154-
self.master.set_timeout(5)
155-
self._is_opened = False
156-
157-
def close(self):
158-
self.master.close()
159-
if self._master == 'rtu':
160-
self._serial.close()
161-
self._is_opened = self.master._is_opened
162-
self.master = None
163-
164-
def get_value(self, ptype, slave_id, function_code, starting_address,
165-
quantity_of_x=1, output_value=0, data_format="",
166-
expected_length=-1, **kwargs):
167-
# if ptype in ["BI", "BO"]:
168-
# return self._get_value(slave_id, function_code, starting_address,
169-
# quantity_of_x, output_value, data_format,
170-
# expected_length)
171-
# else:
172-
return self._get_formatted_value(ptype, slave_id,
173-
function_code, starting_address,
174-
quantity_of_x, **kwargs)
175-
176-
def _get_value(self, slave_id, function_code, starting_address,
177-
quantity_of_x=1, output_value=0, data_format="",
178-
expected_length=-1):
179-
starting_address -= ADDRESS_RANGE[function_code]
180-
return self.master.execute(slave_id, function_code, starting_address,
181-
quantity_of_x,
182-
output_value, data_format,
183-
expected_length
184-
)
185-
186-
def _get_formatted_value(self, ptype, slave_id, function_code,
187-
starting_address,
188-
quantity_of_x, **kwargs):
189-
# pre process
190-
# The register data in the response message are packed as two bytes
191-
# per register, with the binary contents right justified within
192-
# each byte. For each register, the first byte contains the high
193-
# order bits and the second contains the low order bits.
194-
wordcount = kwargs.get("wordcount", 1)
195-
quantity_of_x *= wordcount
196-
197-
raw_val = self._get_value(slave_id, function_code,
198-
starting_address, quantity_of_x)
199-
formatter = kwargs.get("formatter", "default")
200-
201-
# post process
202-
if ptype not in ["BI", "BO"]:
203-
byteorder = kwargs.get("byteorder", "big")
204-
wordorder = kwargs.get("wordorder", "big")
205-
if byteorder != "big":
206-
raw_val = swap_bytes(raw_val)
207-
if wordcount > 1:
208-
raw_val = process_words(raw_val)
209-
if wordorder != "big":
210-
raw_val = change_word_endianness(raw_val)
211-
if formatter == "float1":
212-
raw_val = pack_float(raw_val)
213-
scalemultiplier = kwargs.get("scalemultiplier", 1)
214-
215-
scaledivisor = kwargs.get("scaledivisor", 1.0)
216-
scaledivisor = float(scaledivisor)
217-
218-
bit = kwargs.get("bit", "")
219-
if bit != "":
220-
raw_val = get_bit(raw_val[0], bit)
221-
raw_val = [v*scalemultiplier for v in raw_val]
222-
raw_val = [v/scaledivisor for v in raw_val]
223-
return raw_val
140+
def get_slaves(self):
141+
if self.server is not None:
142+
return self.server._databank._slaves
224143

225144

226145
def swap_bytes(byte_array):

0 commit comments

Comments
 (0)