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.
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.3
2
2
  Name: modpoll
3
- Version: 1.3.3
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,20,BE_BE
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,20,BE_BE
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=1.0,
43
- help="The time interval in seconds between two polling, Defaults to 1.0",
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
- self._decode_and_update_reference(ref, decoder)
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 = 1.0,
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
- value = (
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
- payload[ref.name] = value
524
- if payload:
525
- if timestamp:
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)
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "modpoll"
3
- version = "v1.3.3"
3
+ version = "v1.4.1"
4
4
  description = "A New Command-line Tool for Modbus and MQTT"
5
5
  authors = ["Ying Shaodong <helloysd@gmail.com>"]
6
6
  repository = "https://github.com/gavinying/modpoll"
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes