Skip to content

Commit 8135820

Browse files
author
Tom Softreck
committed
update
1 parent d75e6cc commit 8135820

4 files changed

Lines changed: 183 additions & 6 deletions

File tree

bin/modapi

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
#!/usr/bin/env python3
2+
"""
3+
modapi - Command line interface for Modbus API
4+
"""
5+
6+
import sys
7+
import os
8+
from pathlib import Path
9+
10+
# Add parent directory to path to import modapi
11+
sys.path.insert(0, str(Path(__file__).resolve().parent.parent))
12+
13+
from modapi.__main__ import main
14+
15+
if __name__ == '__main__':
16+
sys.exit(main())

modapi/__main__.py

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -77,19 +77,28 @@ def main():
7777
timeout=args.timeout
7878
)
7979
elif args.command == 'shell':
80+
# Extract shell command and its arguments
81+
shell_args = []
82+
if hasattr(args, 'command') and isinstance(args.command, list) and len(args.command) > 0:
83+
shell_args = args.command
84+
8085
# Convert args to sys.argv format for shell_main
8186
sys_argv = ['modapi']
82-
if args.interactive:
83-
sys_argv.append('--interactive')
84-
if args.verbose:
85-
sys_argv.append('--verbose')
8687
if args.modbus_port:
8788
sys_argv.extend(['--port', args.modbus_port])
8889
if args.baudrate:
89-
sys_argv.extend(['--baudrate', str(args.baudrate)])
90+
sys_argv.extend(['--baud', str(args.baudrate)])
9091
if args.timeout:
9192
sys_argv.extend(['--timeout', str(args.timeout)])
92-
sys_argv.extend(args.command)
93+
if hasattr(args, 'interactive') and args.interactive:
94+
sys_argv.append('--interactive')
95+
if hasattr(args, 'verbose') and args.verbose:
96+
sys_argv.append('--verbose')
97+
98+
# Add shell command and arguments
99+
if shell_args:
100+
sys_argv.append(shell_args[0]) # Command
101+
sys_argv.extend(shell_args[1:]) # Arguments
93102

94103
# Run shell main
95104
sys.argv = sys_argv

pyproject.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@ authors = ["Tom Sapletta <info@softreck.dev>"]
66
readme = "README.md"
77
packages = [{include = "modapi"}]
88

9+
[tool.poetry.scripts]
10+
modapi = "modapi.__main__:main"
11+
912
[tool.poetry.dependencies]
1013
python = ">=3.10,<4.0"
1114
pymodbus = {extras = ["serial"], version = "^3.10.0"}

scripts/modapi

Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
#!/usr/bin/env python3
2+
"""
3+
modapi - Direct command-line interface for Modbus API
4+
"""
5+
6+
import os
7+
import sys
8+
import argparse
9+
from pathlib import Path
10+
11+
# Add parent directory to path to import modapi
12+
sys.path.insert(0, str(Path(__file__).resolve().parent.parent))
13+
14+
from modapi.client import ModbusClient, auto_detect_modbus_port
15+
16+
def main():
17+
"""Main entry point for the direct CLI"""
18+
parser = argparse.ArgumentParser(description='modapi - Direct Modbus API commands')
19+
parser.add_argument('--port', help='Modbus serial port')
20+
parser.add_argument('--baud', type=int, help='Baud rate')
21+
parser.add_argument('--timeout', type=float, help='Timeout in seconds')
22+
parser.add_argument('--unit', type=int, default=1, help='Modbus unit ID')
23+
parser.add_argument('command', help='Command: wc (write coil), rc (read coil), etc.')
24+
parser.add_argument('address', type=int, help='Register address')
25+
parser.add_argument('value', nargs='?', help='Value for write commands')
26+
parser.add_argument('count', nargs='?', type=int, help='Count for read commands')
27+
28+
args = parser.parse_args()
29+
30+
# Initialize client
31+
port = args.port
32+
if not port:
33+
port = auto_detect_modbus_port()
34+
if not port:
35+
print("Error: Could not auto-detect Modbus port")
36+
sys.exit(1)
37+
38+
client = ModbusClient(
39+
port=port,
40+
baudrate=args.baud,
41+
timeout=args.timeout
42+
)
43+
44+
if not client.connect():
45+
print(f"Error: Failed to connect to port {port}")
46+
sys.exit(1)
47+
48+
try:
49+
# Process commands
50+
cmd = args.command.lower()
51+
52+
if cmd == 'wc': # Write coil
53+
if args.value is None:
54+
print("Error: Value required for write coil command")
55+
sys.exit(1)
56+
57+
value = args.value.lower() in ('1', 'true', 'on', 'yes')
58+
result = client.write_coil(args.address, value, unit=args.unit)
59+
60+
if result:
61+
print(f"Success: Wrote {'ON' if value else 'OFF'} to coil {args.address}")
62+
else:
63+
print(f"Error: Failed to write to coil {args.address}")
64+
sys.exit(1)
65+
66+
elif cmd == 'rc': # Read coil(s)
67+
count = args.count or 1
68+
result = client.read_coils(args.address, count, unit=args.unit)
69+
70+
if result is not None:
71+
if count == 1:
72+
print(f"Coil {args.address}: {'ON' if result[0] else 'OFF'}")
73+
else:
74+
print(f"Coils {args.address}-{args.address + count - 1}:")
75+
for i, val in enumerate(result):
76+
print(f" {args.address + i}: {'ON' if val else 'OFF'}")
77+
else:
78+
print(f"Error: Failed to read coil(s) at {args.address}")
79+
sys.exit(1)
80+
81+
elif cmd == 'wh': # Write holding register
82+
if args.value is None:
83+
print("Error: Value required for write holding register command")
84+
sys.exit(1)
85+
86+
value = int(args.value)
87+
result = client.write_register(args.address, value, unit=args.unit)
88+
89+
if result:
90+
print(f"Success: Wrote {value} to register {args.address}")
91+
else:
92+
print(f"Error: Failed to write to register {args.address}")
93+
sys.exit(1)
94+
95+
elif cmd == 'rh': # Read holding register(s)
96+
count = args.count or 1
97+
result = client.read_holding_registers(args.address, count, unit=args.unit)
98+
99+
if result is not None:
100+
if count == 1:
101+
print(f"Register {args.address}: {result[0]}")
102+
else:
103+
print(f"Registers {args.address}-{args.address + count - 1}:")
104+
for i, val in enumerate(result):
105+
print(f" {args.address + i}: {val}")
106+
else:
107+
print(f"Error: Failed to read register(s) at {args.address}")
108+
sys.exit(1)
109+
110+
elif cmd == 'ri': # Read input register(s)
111+
count = args.count or 1
112+
result = client.read_input_registers(args.address, count, unit=args.unit)
113+
114+
if result is not None:
115+
if count == 1:
116+
print(f"Input register {args.address}: {result[0]}")
117+
else:
118+
print(f"Input registers {args.address}-{args.address + count - 1}:")
119+
for i, val in enumerate(result):
120+
print(f" {args.address + i}: {val}")
121+
else:
122+
print(f"Error: Failed to read input register(s) at {args.address}")
123+
sys.exit(1)
124+
125+
elif cmd == 'rd': # Read discrete input(s)
126+
count = args.count or 1
127+
result = client.read_discrete_inputs(args.address, count, unit=args.unit)
128+
129+
if result is not None:
130+
if count == 1:
131+
print(f"Discrete input {args.address}: {'ON' if result[0] else 'OFF'}")
132+
else:
133+
print(f"Discrete inputs {args.address}-{args.address + count - 1}:")
134+
for i, val in enumerate(result):
135+
print(f" {args.address + i}: {'ON' if val else 'OFF'}")
136+
else:
137+
print(f"Error: Failed to read discrete input(s) at {args.address}")
138+
sys.exit(1)
139+
140+
else:
141+
print(f"Error: Unknown command '{cmd}'")
142+
print("Available commands: wc, rc, wh, rh, ri, rd")
143+
sys.exit(1)
144+
145+
finally:
146+
client.close()
147+
148+
if __name__ == '__main__':
149+
main()

0 commit comments

Comments
 (0)