modpoll 1.3.3__tar.gz → 1.4.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {modpoll-1.3.3 → modpoll-1.4.1}/PKG-INFO +8 -3
- {modpoll-1.3.3 → modpoll-1.4.1}/README.md +6 -1
- {modpoll-1.3.3 → modpoll-1.4.1}/modpoll/arg_parser.py +2 -2
- {modpoll-1.3.3 → modpoll-1.4.1}/modpoll/modbus_task.py +42 -16
- {modpoll-1.3.3 → modpoll-1.4.1}/pyproject.toml +1 -1
- {modpoll-1.3.3 → modpoll-1.4.1}/LICENSE +0 -0
- {modpoll-1.3.3 → modpoll-1.4.1}/modpoll/__init__.py +0 -0
- {modpoll-1.3.3 → modpoll-1.4.1}/modpoll/__main__.py +0 -0
- {modpoll-1.3.3 → modpoll-1.4.1}/modpoll/main.py +0 -0
- {modpoll-1.3.3 → modpoll-1.4.1}/modpoll/mqtt_task.py +0 -0
- {modpoll-1.3.3 → modpoll-1.4.1}/modpoll/utils.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
2
|
Name: modpoll
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.4.1
|
|
4
4
|
Summary: A New Command-line Tool for Modbus and MQTT
|
|
5
5
|
Home-page: https://github.com/gavinying/modpoll
|
|
6
6
|
Author: Ying Shaodong
|
|
@@ -146,7 +146,7 @@ ref,input_reg11,30012,int32,rw
|
|
|
146
146
|
ref,input_reg12,30014,int32,rw
|
|
147
147
|
ref,input_reg13,30016,float32,rw
|
|
148
148
|
ref,input_reg14,30018,float32,rw
|
|
149
|
-
poll,holding_register,40000,
|
|
149
|
+
poll,holding_register,40000,44,BE_BE
|
|
150
150
|
ref,holding_reg01,40000,uint16,rw
|
|
151
151
|
ref,holding_reg02,40001,uint16,rw
|
|
152
152
|
ref,holding_reg03,40002,uint16,rw
|
|
@@ -161,6 +161,11 @@ ref,holding_reg11,40012,int32,rw
|
|
|
161
161
|
ref,holding_reg12,40014,int32,rw
|
|
162
162
|
ref,holding_reg13,40016,float32,rw
|
|
163
163
|
ref,holding_reg14,40018,float32,rw
|
|
164
|
+
ref,holding_reg15,40020,uint64,rw
|
|
165
|
+
ref,holding_reg16,40024,int64,rw
|
|
166
|
+
ref,holding_reg17,40028,float64,rw
|
|
167
|
+
ref,holding_reg18,40032,float64,rw
|
|
168
|
+
ref,holding_reg19,40036,string16,rw
|
|
164
169
|
```
|
|
165
170
|
|
|
166
171
|
This configuration tells *modpoll* to do the following for each poll,
|
|
@@ -122,7 +122,7 @@ ref,input_reg11,30012,int32,rw
|
|
|
122
122
|
ref,input_reg12,30014,int32,rw
|
|
123
123
|
ref,input_reg13,30016,float32,rw
|
|
124
124
|
ref,input_reg14,30018,float32,rw
|
|
125
|
-
poll,holding_register,40000,
|
|
125
|
+
poll,holding_register,40000,44,BE_BE
|
|
126
126
|
ref,holding_reg01,40000,uint16,rw
|
|
127
127
|
ref,holding_reg02,40001,uint16,rw
|
|
128
128
|
ref,holding_reg03,40002,uint16,rw
|
|
@@ -137,6 +137,11 @@ ref,holding_reg11,40012,int32,rw
|
|
|
137
137
|
ref,holding_reg12,40014,int32,rw
|
|
138
138
|
ref,holding_reg13,40016,float32,rw
|
|
139
139
|
ref,holding_reg14,40018,float32,rw
|
|
140
|
+
ref,holding_reg15,40020,uint64,rw
|
|
141
|
+
ref,holding_reg16,40024,int64,rw
|
|
142
|
+
ref,holding_reg17,40028,float64,rw
|
|
143
|
+
ref,holding_reg18,40032,float64,rw
|
|
144
|
+
ref,holding_reg19,40036,string16,rw
|
|
140
145
|
```
|
|
141
146
|
|
|
142
147
|
This configuration tells *modpoll* to do the following for each poll,
|
|
@@ -39,8 +39,8 @@ def get_parser():
|
|
|
39
39
|
parser.add_argument(
|
|
40
40
|
"--interval",
|
|
41
41
|
type=float,
|
|
42
|
-
default=
|
|
43
|
-
help="The time interval in seconds between two polling, Defaults to
|
|
42
|
+
default=0.5,
|
|
43
|
+
help="The time interval in seconds between two polling, Defaults to 0.5",
|
|
44
44
|
)
|
|
45
45
|
parser.add_argument(
|
|
46
46
|
"--tcp", help="Act as a Modbus TCP master, connecting to host TCP"
|
|
@@ -58,6 +58,7 @@ class Poller:
|
|
|
58
58
|
self.readableReferences: List[Reference] = []
|
|
59
59
|
self.disabled = False
|
|
60
60
|
self.failcounter = 0
|
|
61
|
+
self.logger = logging.getLogger(__name__)
|
|
61
62
|
|
|
62
63
|
def poll(self, master) -> bool:
|
|
63
64
|
if self.disabled or not master:
|
|
@@ -104,9 +105,18 @@ class Poller:
|
|
|
104
105
|
cur_ref += 1
|
|
105
106
|
if cur_ref >= self.start_address + ref_count:
|
|
106
107
|
break
|
|
107
|
-
|
|
108
|
+
try:
|
|
109
|
+
self._decode_and_update_reference(ref, decoder)
|
|
110
|
+
self.device.update_reference(ref)
|
|
111
|
+
except UnicodeDecodeError:
|
|
112
|
+
self.logger.error(
|
|
113
|
+
f"Failed to decode unicode string for reference: {ref.name}, check the reference address or length of string in configuration file"
|
|
114
|
+
)
|
|
115
|
+
except:
|
|
116
|
+
self.logger.error(
|
|
117
|
+
f"Failed to decode value for reference: {ref.name}"
|
|
118
|
+
)
|
|
108
119
|
cur_ref += ref.ref_width
|
|
109
|
-
self.device.update_reference(ref)
|
|
110
120
|
self.update_statistics(True)
|
|
111
121
|
return True
|
|
112
122
|
except ModbusException:
|
|
@@ -171,8 +181,6 @@ class Poller:
|
|
|
171
181
|
ref.update_value(
|
|
172
182
|
decoder.decode_string(ref.ref_width * 2).decode("utf-8").rstrip("\x00")
|
|
173
183
|
)
|
|
174
|
-
else:
|
|
175
|
-
decoder.skip_bytes(2) # Skip unknown types
|
|
176
184
|
|
|
177
185
|
def add_readable_reference(self, ref: "Reference"):
|
|
178
186
|
if ref not in self.readableReferences:
|
|
@@ -264,10 +272,11 @@ class ModbusHandler:
|
|
|
264
272
|
config_file: str,
|
|
265
273
|
mqtt_handler: Optional[MqttHandler] = None,
|
|
266
274
|
timeout: float = 3.0,
|
|
267
|
-
interval: float =
|
|
275
|
+
interval: float = 0.5,
|
|
268
276
|
daemon: bool = False,
|
|
269
277
|
mqtt_publish_topic_pattern: Optional[str] = None,
|
|
270
278
|
mqtt_diagnostics_topic_pattern: Optional[str] = None,
|
|
279
|
+
mqtt_single_publish: bool = False,
|
|
271
280
|
):
|
|
272
281
|
self.modbus_client = modbus_client
|
|
273
282
|
self.config_file = config_file
|
|
@@ -277,6 +286,7 @@ class ModbusHandler:
|
|
|
277
286
|
self.daemon = daemon
|
|
278
287
|
self.mqtt_publish_topic_pattern = mqtt_publish_topic_pattern
|
|
279
288
|
self.mqtt_diagnostics_topic_pattern = mqtt_diagnostics_topic_pattern
|
|
289
|
+
self.mqtt_single_publish = mqtt_single_publish
|
|
280
290
|
self.connected = False
|
|
281
291
|
self.deviceList: List[Device] = []
|
|
282
292
|
self.logger = logging.getLogger(__name__)
|
|
@@ -319,7 +329,7 @@ class ModbusHandler:
|
|
|
319
329
|
continue
|
|
320
330
|
device_name = row[1].strip()
|
|
321
331
|
try:
|
|
322
|
-
device_id = int(row[2])
|
|
332
|
+
device_id = int(row[2], 0)
|
|
323
333
|
except ValueError:
|
|
324
334
|
self.logger.error(f"Invalid device ID for {device_name}")
|
|
325
335
|
continue
|
|
@@ -357,8 +367,8 @@ class ModbusHandler:
|
|
|
357
367
|
return None
|
|
358
368
|
fc = row[1].lower()
|
|
359
369
|
try:
|
|
360
|
-
start_address = int(row[2])
|
|
361
|
-
size = int(row[3])
|
|
370
|
+
start_address = int(row[2], 0)
|
|
371
|
+
size = int(row[3], 0)
|
|
362
372
|
except ValueError:
|
|
363
373
|
self.logger.error("Invalid start address or size for poller")
|
|
364
374
|
return None
|
|
@@ -401,7 +411,7 @@ class ModbusHandler:
|
|
|
401
411
|
return None
|
|
402
412
|
ref_name = row[1].replace(" ", "_")
|
|
403
413
|
try:
|
|
404
|
-
address = int(row[2])
|
|
414
|
+
address = int(row[2], 0)
|
|
405
415
|
except ValueError:
|
|
406
416
|
self.logger.error(f"Invalid address for reference {ref_name}")
|
|
407
417
|
return None
|
|
@@ -507,22 +517,37 @@ class ModbusHandler:
|
|
|
507
517
|
print(table)
|
|
508
518
|
|
|
509
519
|
def publish_data(self, timestamp=None, on_change=False):
|
|
510
|
-
if not self.mqtt_handler:
|
|
511
|
-
return
|
|
512
|
-
if not self.mqtt_publish_topic_pattern:
|
|
520
|
+
if not self.mqtt_handler or not self.mqtt_publish_topic_pattern:
|
|
513
521
|
return
|
|
522
|
+
|
|
514
523
|
for dev in self.deviceList:
|
|
524
|
+
if not dev.pollSuccess:
|
|
525
|
+
self.logger.debug(
|
|
526
|
+
f"Skip publishing for disconnected device: {dev.name}"
|
|
527
|
+
)
|
|
528
|
+
continue
|
|
529
|
+
|
|
515
530
|
payload = {}
|
|
516
531
|
for ref in dev.references.values():
|
|
517
532
|
if not on_change or ref.val != ref.last_val:
|
|
518
|
-
|
|
533
|
+
ref_val = (
|
|
519
534
|
round(ref.val, FLOAT_TYPE_PRECISION)
|
|
520
535
|
if isinstance(ref.val, float)
|
|
521
536
|
else ref.val
|
|
522
537
|
)
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
538
|
+
key = f"{ref.name}|{ref.unit}" if ref.unit else ref.name
|
|
539
|
+
payload[key] = ref_val
|
|
540
|
+
|
|
541
|
+
if self.mqtt_single_publish:
|
|
542
|
+
topic = f"{self.mqtt_publish_topic_pattern.replace('{{device_name}}', dev.name)}/{ref.name}"
|
|
543
|
+
if isinstance(ref_val, list):
|
|
544
|
+
for i, entry in enumerate(ref_val):
|
|
545
|
+
self.mqtt_handler.publish(f"{topic}/{i}", entry)
|
|
546
|
+
else:
|
|
547
|
+
self.mqtt_handler.publish(topic, ref_val)
|
|
548
|
+
|
|
549
|
+
if payload and not self.mqtt_single_publish:
|
|
550
|
+
if timestamp is not None:
|
|
526
551
|
payload["timestamp"] = timestamp
|
|
527
552
|
topic = self.mqtt_publish_topic_pattern.replace(
|
|
528
553
|
"{{device_name}}", dev.name
|
|
@@ -581,6 +606,7 @@ def setup_modbus_handlers(args, mqtt_handler: Optional[MqttHandler] = None):
|
|
|
581
606
|
daemon=args.daemon,
|
|
582
607
|
mqtt_publish_topic_pattern=args.mqtt_publish_topic_pattern,
|
|
583
608
|
mqtt_diagnostics_topic_pattern=args.mqtt_diagnostics_topic_pattern,
|
|
609
|
+
mqtt_single_publish=args.mqtt_single,
|
|
584
610
|
)
|
|
585
611
|
if modbus_handler.load_config():
|
|
586
612
|
modbus_handlers.append(modbus_handler)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|