diff --git a/klippy/klippy.py b/klippy/klippy.py index 097cff99..23338d87 100644 --- a/klippy/klippy.py +++ b/klippy/klippy.py @@ -6,7 +6,7 @@ # This file may be distributed under the terms of the GNU GPLv3 license. import sys, os, gc, optparse, logging, time, collections, importlib import util, reactor, queuelogger, msgproto -import gcode, configfile, pins, mcu, toolhead, webhooks +import gcode, configfile, pins, mcu, toolhead, webhooks, tcp_server message_ready = "Printer is ready" @@ -303,6 +303,8 @@ def main(): opts.add_option("-I", "--input-tty", dest="inputtty", default='/tmp/printer', help="input tty name (default is /tmp/printer)") + opts.add_option("-T", "--input-tcp-port", dest="inputtcpport", + help="input TCP port") opts.add_option("-a", "--api-server", dest="apiserver", help="api server unix domain socket filename") opts.add_option("-l", "--logfile", dest="logfile", @@ -331,6 +333,8 @@ def main(): start_args['debuginput'] = options.debuginput debuginput = open(options.debuginput, 'rb') start_args['gcode_fd'] = debuginput.fileno() + elif options.inputtcpport: + start_args['gcode_fd'] = tcp_server.create_server(int(options.inputtcpport)) else: start_args['gcode_fd'] = util.create_pty(options.inputtty) if options.debugoutput: diff --git a/klippy/tcp_server.py b/klippy/tcp_server.py new file mode 100644 index 00000000..834a5a72 --- /dev/null +++ b/klippy/tcp_server.py @@ -0,0 +1,83 @@ +import socket, threading, logging + +def create_server(port): + s = Server(port) + return s.get_backend_fd() + +class Server: + def __init__(self, port): + self.port = port + self.socket_backend, self.socket_helper = socket.socketpair() + + self.lock = threading.Lock() + self.clients = [] + + server_address = ('0.0.0.0', port) + self.external_server = socket.socket(family=socket.AF_INET, type=socket.SOCK_STREAM) + self.external_server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + self.external_server.bind(server_address) + self.external_server.listen() + + self.start_copying() + + async_accepter = threading.Thread(target=self.server_accept) + async_accepter.daemon = True + async_accepter.start() + + def get_backend_fd(self): + return self.socket_backend.fileno() + + def server_accept(self): + self.accept_in_loop() + + def start_copying(self): + async_writer = threading.Thread(target=self.write_to_clients) + async_writer.daemon = True + async_writer.start() + + def accept_in_loop(self): + while True: + try: + conn, address = self.external_server.accept() + except Exception as e: + logging.info("Server: accept error: ", e) + pass + + # logging.info("Server: accepted: "+ str(address)) + conn.setblocking(False) + + self.lock.acquire() + self.clients.append(conn) + self.lock.release() + + async_reader = threading.Thread(target=self.read_from_client, args=(conn,)) + async_reader.daemon = True + async_reader.start() + + def read_from_client(self, conn): + while True: + try: + data = conn.recv(1024) + if data: + # logging.info("Server: in: "+ str(data)) + self.socket_helper.sendall(data) + except Exception as e: + "" + + def write_to_clients(self): + while True: + self.lock.acquire() + clients = self.clients + self.lock.release() + + if len(clients) == 0: + continue + + try: + data = self.socket_helper.recv(1024) + if data: + # logging.info("Server: out: "+ str(data)) + for c in clients: + c.sendall(data) + except Exception as e: + ""