From 683a9f8d096b2450131d4cd202b979425453af2a Mon Sep 17 00:00:00 2001 From: ilyavasilev Date: Tue, 28 Nov 2023 21:25:21 +0300 Subject: [PATCH] =?UTF-8?q?=D0=A0=D0=B5=D0=B0=D0=BB=D0=B8=D0=B7=D0=BE?= =?UTF-8?q?=D0=B2=D0=B0=D0=BD=20=D0=BA=D0=BE=D0=BC=D0=B0=D0=BD=D0=B4=D0=BD?= =?UTF-8?q?=D1=8B=D0=B9=20=D0=B8=D0=BD=D1=82=D0=B5=D1=80=D1=84=D0=B5=D0=B9?= =?UTF-8?q?=D1=81=20=D0=BD=D0=B5=20=D0=B1=D0=BB=D0=BE=D0=BA=D0=B8=D1=80?= =?UTF-8?q?=D1=83=D1=8E=D1=89=D0=B8=D0=B9=D1=81=D1=8F=20=D0=BF=D0=BE=D1=81?= =?UTF-8?q?=D0=BB=D0=B5=20=D0=B2=D0=B2=D0=BE=D0=B4=D0=B0=20=D0=BA=D0=BE?= =?UTF-8?q?=D0=BC=D0=B0=D0=BD=D0=B4=D1=8B=20|=20HW4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- scada_system/noblocking_turtle_shell.py | 66 ++++++++++++++++++++++ scada_system/turtle_shell.py | 74 +++++++++++++++++++++++++ 2 files changed, 140 insertions(+) create mode 100644 scada_system/noblocking_turtle_shell.py create mode 100644 scada_system/turtle_shell.py diff --git a/scada_system/noblocking_turtle_shell.py b/scada_system/noblocking_turtle_shell.py new file mode 100644 index 0000000..0d6cc6b --- /dev/null +++ b/scada_system/noblocking_turtle_shell.py @@ -0,0 +1,66 @@ +import cmd +import threading + +from queue import Queue + +from equipment.turtle_device import TurtleDevice + + +class TurtleDeviceThread(threading.Thread): + def __init__(self): + super().__init__() + self.device = TurtleDevice() + self.queue = Queue() + + self.device.open() + + def add_task(self, action: str, *args, **kwargs): + self.queue.put((action, args, kwargs)) + + def run(self): + while True: + action, args, kwargs = self.queue.get() + if action == "stop": + self.queue.task_done() + break + self.device.execute(action, *args, **kwargs) + self.queue.task_done() + + +class NoBlockingTurtleShell(cmd.Cmd): + intro = 'Welcome to the turtle shell. Type help or ? to list commands.\n' + prompt = '(turtle) ' + file = None + + def __init__(self, turtle_thread: TurtleDeviceThread): + super().__init__() + self.turtle_thread = turtle_thread + + def do_execute(self, arg): + 'Executes a command with the given arguments: EXECUTE forward 100' + command_and_args = parse(arg) + if len(command_and_args) == 1: + self.turtle_thread.add_task(command_and_args[0]) + return None + + self.turtle_thread.add_task(command_and_args[0], *command_and_args[1:]) + + def do_exit(self, arg): + 'Stops the message processing thread: EXIT' + print('The message processing thread has stopped.') + self.turtle_thread.add_task('stop') + return True + +def parse(arg): + 'Convert a series of zero or more numbers to an argument tuple' + def perform_elem(elem: str): + return int(elem) if elem.isdecimal() else elem + return tuple(map(perform_elem, arg.split())) + +if __name__ == '__main__': + turtle_thread = TurtleDeviceThread() + turtle_thread.daemon = True + thread_shell = threading.Thread(target=NoBlockingTurtleShell(turtle_thread).cmdloop) + thread_shell.start() + turtle_thread.run() + thread_shell.join() \ No newline at end of file diff --git a/scada_system/turtle_shell.py b/scada_system/turtle_shell.py new file mode 100644 index 0000000..ac22aab --- /dev/null +++ b/scada_system/turtle_shell.py @@ -0,0 +1,74 @@ +import cmd, sys +from turtle import * + + +class TurtleShell(cmd.Cmd): + intro = 'Welcome to the turtle shell. Type help or ? to list commands.\n' + prompt = '(turtle) ' + file = None + + # ----- basic turtle commands ----- + def do_forward(self, arg): + 'Move the turtle forward by the specified distance: FORWARD 10' + forward(*parse(arg)) + def do_right(self, arg): + 'Turn turtle right by given number of degrees: RIGHT 20' + right(*parse(arg)) + def do_left(self, arg): + 'Turn turtle left by given number of degrees: LEFT 90' + left(*parse(arg)) + def do_goto(self, arg): + 'Move turtle to an absolute position with changing orientation. GOTO 100 200' + goto(*parse(arg)) + def do_home(self, arg): + 'Return turtle to the home position: HOME' + home() + def do_circle(self, arg): + 'Draw circle with given radius an options extent and steps: CIRCLE 50' + circle(*parse(arg)) + def do_position(self, arg): + 'Print the current turtle position: POSITION' + print('Current position is %d %d\n' % position()) + def do_heading(self, arg): + 'Print the current turtle heading in degrees: HEADING' + print('Current heading is %d\n' % (heading(),)) + def do_color(self, arg): + 'Set the color: COLOR BLUE' + color(arg.lower()) + def do_undo(self, arg): + 'Undo (repeatedly) the last turtle action(s): UNDO' + def do_reset(self, arg): + 'Clear the screen and return turtle to center: RESET' + reset() + def do_bye(self, arg): + 'Stop recording, close the turtle window, and exit: BYE' + print('Thank you for using Turtle') + self.close() + bye() + return True + + # ----- record and playback ----- + def do_record(self, arg): + 'Save future commands to filename: RECORD rose.cmd' + self.file = open(arg, 'w') + def do_playback(self, arg): + 'Playback commands from a file: PLAYBACK rose.cmd' + self.close() + with open(arg) as f: + self.cmdqueue.extend(f.read().splitlines()) + def precmd(self, line): + line = line.lower() + if self.file and 'playback' not in line: + print(line, file=self.file) + return line + def close(self): + if self.file: + self.file.close() + self.file = None + +def parse(arg): + 'Convert a series of zero or more numbers to an argument tuple' + return tuple(map(int, arg.split())) + +if __name__ == '__main__': + TurtleShell().cmdloop() \ No newline at end of file