Skip to content

Commit

Permalink
implement basic functions
Browse files Browse the repository at this point in the history
  • Loading branch information
gavinying committed Sep 17, 2021
1 parent 8a7b8f0 commit 5a2d1f0
Show file tree
Hide file tree
Showing 6 changed files with 642 additions and 0 deletions.
8 changes: 8 additions & 0 deletions modpoll/__init__.py
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
2 changes: 2 additions & 0 deletions modpoll/__main__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
from .main import app
app()
32 changes: 32 additions & 0 deletions modpoll/arg_parser.py
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
79 changes: 79 additions & 0 deletions modpoll/main.py
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()
Loading

0 comments on commit 5a2d1f0

Please sign in to comment.