#!/usr/bin/env python3 """ This script allows direct interaction with a MasterMACS-Controller over an ethernet connection. To read the manual, simply run this script without any arguments. Stefan Mathis, April 2025 """ import struct import socket import curses def packMasterMacsCommand(command): # 0x0D = Carriage return buf = struct.pack('B',0x0D) buf = bytes(command,'utf-8') + buf return bytes(command,'utf-8') def readMasterMacsReply(input): msg = bytearray() expectAck = True while True: b = input.recv(1) bint = int.from_bytes(b,byteorder='little') if bint == 2 or bint == 7: #STX or BELL expectAck = False continue if expectAck and bint == 6: # ACK return bytes(msg) else: if bint == 13 and not expectAck: # CR return bytes(msg) else: msg.append(bint) if __name__ == "__main__": from sys import argv try: addr = argv[1].split(':') s = socket.socket(socket.AF_INET,socket.SOCK_STREAM) s.connect((addr[0],int(addr[1]))) if len(argv) == 3: buf = packMasterMacsCommand(argv[2]) s.send(buf) reply = readMasterMacsReply(s) print(reply.decode('utf-8') + '\n') else: try: stdscr = curses.initscr() curses.noecho() curses.cbreak() stdscr.keypad(True) stdscr.scrollok(True) stdscr.addstr(">> ") stdscr.refresh() history = [""] ptr = len(history) - 1 while True: c = stdscr.getch() if c == curses.KEY_RIGHT: (y, x) = stdscr.getyx() if x < len(history[ptr]) + 3: stdscr.move(y, x+1) stdscr.refresh() elif c == curses.KEY_LEFT: (y, x) = stdscr.getyx() if x > 3: stdscr.move(y, x-1) stdscr.refresh() elif c == curses.KEY_UP: if ptr > 0: ptr -= 1 stdscr.addch("\r") stdscr.clrtoeol() stdscr.addstr(">> " + history[ptr]) elif c == curses.KEY_DOWN: if ptr < len(history) - 1: ptr += 1 stdscr.addch("\r") stdscr.clrtoeol() stdscr.addstr(">> " + history[ptr]) elif c == curses.KEY_ENTER or c == ord('\n') or c == ord('\r'): if history[ptr] == 'quit': break # because of arrow keys move back to the end of the line (y, x) = stdscr.getyx() stdscr.move(y, 3+len(history[ptr])) if history[ptr]: buf = packMasterMacsCommand(history[ptr]) s.send(buf) reply = readMasterMacsReply(s) stdscr.addstr("\n" + reply.decode('utf-8')[0:-1]) if ptr == len(history) - 1 and history[ptr] != "": history += [""] else: history[-1] = "" ptr = len(history) - 1 stdscr.addstr("\n>> ") stdscr.refresh() else: if ptr < len(history) - 1: # Modifying previous input if len(history[-1]) == 0: history[-1] = history[ptr] ptr = len(history) - 1 else: history += [history[ptr]] ptr = len(history) - 1 if c == curses.KEY_BACKSPACE: if len(history[ptr]) == 0: continue (y, x) = stdscr.getyx() history[ptr] = history[ptr][0:x-4] + history[ptr][x-3:] stdscr.addch("\r") stdscr.clrtoeol() stdscr.addstr(">> " + history[ptr]) stdscr.move(y, x-1) stdscr.refresh() else: (y, x) = stdscr.getyx() history[ptr] = history[ptr][0:x-3] + chr(c) + history[ptr][x-3:] stdscr.addch("\r") stdscr.clrtoeol() stdscr.addstr(">> " + history[ptr]) stdscr.move(y, x+1) stdscr.refresh() finally: # to quit curses.nocbreak() stdscr.keypad(False) curses.echo() curses.endwin() except: print(""" Invalid Arguments Option 1: Single Command ------------------------ Usage: writeRead.py pmachost:port command This then returns the response for command. Option 2: CLI Mode ------------------ Usage: writeRead.py pmachost:port You can then type in a command, hit enter, and the response will see the reponse, before being prompted to again enter a command. Type 'quit' to close prompt. """)