-
Notifications
You must be signed in to change notification settings - Fork 22
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
6 changed files
with
642 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
try: | ||
import importlib.metadata as importlib_metadata | ||
except ModuleNotFoundError: | ||
import importlib_metadata | ||
__version__ = importlib_metadata.version(__name__) | ||
|
||
from .main import app | ||
from . import arg_parser |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
from .main import app | ||
app() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
import argparse | ||
|
||
from . import __version__ | ||
|
||
def get_parser(): | ||
parser = argparse.ArgumentParser(description=f'modpoll v{__version__} - A command line tool to communicate with modbus devices') | ||
parser.add_argument('--version', action='version', version=f'modpoll v{__version__}') | ||
parser.add_argument('--loglevel', default='INFO', help='log level (DEBUG/INFO/WARNING/ERROR/CRITICAL), Defaults to INFO') | ||
# parser.add_argument('--config', required=True, help='Configuration file. Required!') | ||
parser.add_argument('--config', required=True, help='Modbus configuration file. Required!') | ||
parser.add_argument('--rate', default=5.0, type=float, help='The sampling rate (s) to poll modbus device, Defaults to 5.0') | ||
parser.add_argument('--interval', default=1.0, type=float, help='The time interval (s) between two polling, Defaults to 1.0') | ||
parser.add_argument('--tcp', help='Act as a Modbus TCP master, connecting to host TCP') | ||
parser.add_argument('--tcp-port', default=502, type=int, help='Port for MODBUS TCP. Defaults to 502') | ||
parser.add_argument('--rtu', help='pyserial URL (or port name) for RTU serial port') | ||
parser.add_argument('--rtu-baud', default=9600, type=int, help='Baud rate for serial port. Defaults to 9600') | ||
parser.add_argument('--rtu-parity', default='none', choices=['even', 'odd', 'none'], help='Parity for serial port. Defaults to none') | ||
parser.add_argument('--timeout', default=3.0, type=float, help='Response time-out for MODBUS devices, Defaults to 3.0') | ||
parser.add_argument('--export', default=None, help='Export references/registers to local csv file') | ||
parser.add_argument('--mqtt-host', default=None, help='MQTT server address. Skip MQTT setup if not specified') | ||
parser.add_argument('--mqtt-port', default=1883, type=int, help='1833 for non-TLS or 8883 for TLS, Defaults to 1883') | ||
parser.add_argument('--mqtt-topic-prefix', default='modbus/', help='Topic prefix to be used for subscribing/publishing. Defaults to "modbus/"') | ||
parser.add_argument('--mqtt-qos', default=0, type=int, help='MQTT QoS value (0/1/2). Defaults to 0') | ||
parser.add_argument('--mqtt-user', default=None, help='Username for authentication (optional)') | ||
parser.add_argument('--mqtt-pass', default=None, help='Password for authentication (optional)') | ||
parser.add_argument('--mqtt-use-tls', action='store_true', help='Use TLS') | ||
parser.add_argument('--mqtt-insecure', action='store_true', help='Use TLS without providing certificates') | ||
parser.add_argument('--mqtt-cacerts', default=None, help="Path to keychain including ") | ||
parser.add_argument('--mqtt-tls-version', default=None, help='TLS protocol version, can be one of tlsv1.2 tlsv1.1 or tlsv1') | ||
parser.add_argument('--diagnostics-rate', default=0, type=float, help='Time in seconds after which for each device diagnostics are published via mqtt.') | ||
parser.add_argument('--autoremove', action='store_true', help='Automatically remove poller if modbus communication has failed 3 times.') | ||
return parser |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
import logging | ||
import time | ||
import sys | ||
import signal | ||
|
||
# from modpoll import __version__ | ||
from modpoll.arg_parser import get_parser | ||
from modpoll.mqtt_task import mqttc_setup, mqttc_publish, mqttc_close | ||
from modpoll.modbus_task import modbus_setup, modbus_poll, modbus_export, modbus_close | ||
|
||
# global objects | ||
run_loop = True | ||
|
||
# logging format | ||
LOG_SIMPLE = "%(asctime)s | %(levelname).1s | %(name)s | %(message)s" | ||
LOG_PROCESS = "%(asctime)s | %(levelname).1s | %(processName)s | %(name)s | %(message)s" | ||
LOG_THREAD = "%(asctime)s | %(levelname).1s | %(threadName)s | %(name)s | %(message)s" | ||
log = None | ||
|
||
def _signal_handler(signal, frame): | ||
print('Exiting ' + sys.argv[0]) | ||
_shutdown() | ||
|
||
|
||
def app(name="modpoll"): | ||
# log.info(f"Starting {name} v{__version__}") | ||
signal.signal(signal.SIGINT, _signal_handler) | ||
|
||
# parse args | ||
args = get_parser().parse_args() | ||
|
||
# get logger | ||
logging.basicConfig(level=args.loglevel, format=LOG_SIMPLE) | ||
global log | ||
log = logging.getLogger(__name__) | ||
|
||
# setup mqtt | ||
if args.mqtt_host: | ||
log.info(f"Setup MQTT connection to {args.mqtt_host}") | ||
if not mqttc_setup(args.mqtt_host, port=args.mqtt_port, user=args.mqtt_user, password=args.mqtt_pass, qos=args.mqtt_qos): | ||
log.error("fail to setup MQTT client") | ||
mqttc_close() | ||
exit(1) | ||
else: | ||
log.info("No MQTT host specified, skip MQTT setup.") | ||
|
||
# setup modbus | ||
if not modbus_setup(args): | ||
log.error("fail to setup modbus client(master)") | ||
modbus_close() | ||
mqttc_close() | ||
exit(1) | ||
|
||
# main loop | ||
last_check = 0 | ||
while run_loop: | ||
now = time.time() | ||
if last_check == 0: | ||
last_check = now | ||
continue | ||
# routine check | ||
if now > last_check + args.rate: | ||
elapsed = now - last_check | ||
last_check = now | ||
log.info(f"looping at rate:{args.rate}, actual:{elapsed}") | ||
modbus_poll() | ||
if args.export: | ||
modbus_export(args.export) | ||
modbus_close() | ||
mqttc_close() | ||
|
||
|
||
def _shutdown(): | ||
global run_loop | ||
run_loop = False | ||
|
||
|
||
if __name__ == "__main__": | ||
app() |
Oops, something went wrong.