|
| 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