initial modbus polling utils
This commit is contained in:
commit
0fe31a31a5
16
README.md
Normal file
16
README.md
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
# modbus-utils
|
||||||
|
|
||||||
|
Internal Modbus TCP polling scripts for Galion Systems client sites.
|
||||||
|
|
||||||
|
Reads holding registers from client PLCs, returns tag:value pairs for
|
||||||
|
upstream logging. See `config/sites.yaml.example` for config format.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
```python
|
||||||
|
from galion_modbus.poll import poll_site
|
||||||
|
from galion_modbus.sites import load_sites
|
||||||
|
|
||||||
|
sites = load_sites()
|
||||||
|
data = poll_site(sites["walnut_creek"])
|
||||||
|
```
|
||||||
19
config/sites.yaml.example
Normal file
19
config/sites.yaml.example
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
# Copy to sites.yaml and populate with real site data
|
||||||
|
sites:
|
||||||
|
walnut_creek:
|
||||||
|
plcs:
|
||||||
|
- host: 10.10.1.5
|
||||||
|
unit: 1
|
||||||
|
start: 0
|
||||||
|
count: 20
|
||||||
|
tags:
|
||||||
|
"0": flow_rate_gpm
|
||||||
|
"1": tank_level_ft
|
||||||
|
"2": pump1_status
|
||||||
|
"3": pump2_status
|
||||||
|
gary_in:
|
||||||
|
plcs:
|
||||||
|
- host: 10.20.1.10
|
||||||
|
unit: 2
|
||||||
|
start: 100
|
||||||
|
count: 10
|
||||||
1
galion_modbus/__init__.py
Normal file
1
galion_modbus/__init__.py
Normal file
@ -0,0 +1 @@
|
|||||||
|
"""Galion Systems Modbus polling utilities."""
|
||||||
33
galion_modbus/poll.py
Normal file
33
galion_modbus/poll.py
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
"""Simple Modbus TCP poller for client site PLCs."""
|
||||||
|
from pymodbus.client import ModbusTcpClient
|
||||||
|
import logging
|
||||||
|
|
||||||
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
def read_holding_registers(host, port=502, unit=1, address=0, count=10):
|
||||||
|
c = ModbusTcpClient(host, port=port)
|
||||||
|
c.connect()
|
||||||
|
try:
|
||||||
|
rr = c.read_holding_registers(address, count, unit)
|
||||||
|
if rr.isError():
|
||||||
|
log.warning("read error from %s unit=%d", host, unit)
|
||||||
|
return None
|
||||||
|
return rr.registers
|
||||||
|
finally:
|
||||||
|
c.close()
|
||||||
|
|
||||||
|
|
||||||
|
def poll_site(site_cfg):
|
||||||
|
"""Poll all configured PLCs for a site and return {tag: value}."""
|
||||||
|
results = {}
|
||||||
|
for plc in site_cfg.get("plcs", []):
|
||||||
|
regs = read_holding_registers(
|
||||||
|
plc["host"], plc.get("port", 502),
|
||||||
|
plc.get("unit", 1), plc.get("start", 0), plc.get("count", 20)
|
||||||
|
)
|
||||||
|
if regs is not None:
|
||||||
|
for i, val in enumerate(regs):
|
||||||
|
tag = plc.get("tags", {}).get(str(i), f"{plc['host']}:r{i}")
|
||||||
|
results[tag] = val
|
||||||
|
return results
|
||||||
10
galion_modbus/sites.py
Normal file
10
galion_modbus/sites.py
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
"""Site PLC configs — loaded from sites.yaml at runtime."""
|
||||||
|
import yaml
|
||||||
|
import os
|
||||||
|
|
||||||
|
_SITES_PATH = os.path.join(os.path.dirname(__file__), "..", "config", "sites.yaml")
|
||||||
|
|
||||||
|
|
||||||
|
def load_sites():
|
||||||
|
with open(_SITES_PATH) as fh:
|
||||||
|
return yaml.safe_load(fh)
|
||||||
2
requirements.txt
Normal file
2
requirements.txt
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
pymodbus>=3.5.0
|
||||||
|
pyyaml>=6.0
|
||||||
Loading…
Reference in New Issue
Block a user