From 718e9700ca8ebe5cd1eee41c734dd9848dbb0d02 Mon Sep 17 00:00:00 2001 From: vviora Date: Wed, 20 Dec 2023 19:17:55 +0300 Subject: [PATCH] modified: controls/device.py modified: equipment/turtle_device.py modified: noblocking_turtle_shell.py modified: turtle_shell.py --- controls/device.py | 25 +++----- equipment/turtle_device.py | 127 +++++++++++++++++++------------------ noblocking_turtle_shell.py | 50 +++++++-------- turtle_shell.py | 1 + 4 files changed, 102 insertions(+), 101 deletions(-) diff --git a/controls/device.py b/controls/device.py index 8e63d50..770d3bd 100644 --- a/controls/device.py +++ b/controls/device.py @@ -1,15 +1,16 @@ from dataclasses import dataclass from typing import Optional, Collection, Any -from abc import ABC, abstractmethod -from enum import Enum, auto +from abc import ABC, abstractmethod, abstractproperty +from enum import Enum + class DeviceLifecycleState(Enum): - INIT = "init" - OPEN = "open" - CLOSE = "close" + INIT = 0 + OPEN = 1 + CLOSE = 2 -class DevaceError(Exception): +class DeviceError(Exception): pass @@ -37,7 +38,6 @@ class ActionDescriptor: class Device(ABC): - # TODO(Homework #3) _state = DeviceLifecycleState.INIT @property @@ -47,13 +47,11 @@ class Device(ABC): def close(self): self._state = DeviceLifecycleState.CLOSE - @property - @abstractmethod + @abstractproperty def trait_descriptors(self) -> Collection[TraitDescriptor]: pass - @property - @abstractmethod + @abstractproperty def action_descriptors(self) -> Collection[ActionDescriptor]: pass @@ -71,7 +69,7 @@ class SynchronyDevice(Device): @abstractmethod def execute(self, action_name: str, *args, **kwargs): """Execute action `action_name`, using `args` and `kwargs` as action argument.""" - pass + raise DeviceError @abstractmethod def read(self, trait_name: str) -> Any: @@ -87,6 +85,3 @@ class SynchronyDevice(Device): def invalidate(self, trait_name: str): """Invalidate logical state of trait `trait_name`""" pass - - def close(self): - return super().close() \ No newline at end of file diff --git a/equipment/turtle_device.py b/equipment/turtle_device.py index 941d7f8..b0fccaa 100644 --- a/equipment/turtle_device.py +++ b/equipment/turtle_device.py @@ -1,73 +1,80 @@ -import time from turtle import Turtle -from equipment.device import SynchronyDevice -from equipment.device import Device -import inspect from typing import Optional, Collection, Any +from controls.device import * +import inspect + class TurtleDevice(SynchronyDevice): def open(self): - super().open() self.turtle = Turtle() + self.descriptors = dict(traits=dict(), actions=dict()) + super().open() - def close (self): + def close(self): + self.turtle.reset() super().close() - del self.turtle - - @property - def trait_descriptors(self): - l = list() - for i in inspect.getmembers(TurtleDevice): - if not (i[0].startswith("__") or i[0].startswith("_")) and not inspect.isfunction(i[1]): - l.append(i[0]) - return l - @property - def action_descriptors(self): - l = list() - #print(inspect.getmembers(TurtleDevice)) - #for i in dir(TurtleDevice): - # # print(type(getattr(TurtleDevice(), i))=='method') - # # if not i.startswith("__") and type(getattr(TurtleDevice(), i)) == types.MethodType: - # if not i.startswith("__") and inspect.isroutine(inspect.getattr_static(TurtleDevice, i)): - # sig = inspect.signature(getattr(TurtleDevice, i)) - # l.append(i + " " + str(sig)) - #return l - for i in inspect.getmembers(TurtleDevice): - # print(type(getattr(TurtleDevice, i))) - # print(type(getattr(TurtleDevice(), i))=='method') - if not (i[0].startswith("__") or i[0].startswith("_")) and inspect.isfunction(i[1]): - sig = inspect.signature(getattr(TurtleDevice, i[0])) - l.append(i[0]+str(sig)) - return l + def trait_descriptors(self) -> Collection[TraitDescriptor]: + return self.get_descriptors()["traits"] - def execute(self, action_name: str, *args, **kwargs): - """Execute action `action_name`, using `args` and `kwargs` as action argument.""" - if self.state == 1: - f = getattr(self.turtle, action_name) - f(int(*args), **kwargs) - else: - print("It's not opened") - self.open() - #time.sleep(30) - f = getattr(self.turtle, action_name) - f(int(*args), **kwargs) - - def read(self, trait_name: str): - """Read physical state of trait `trait_name` from device.""" - return getattr(self.turtle, trait_name) - - def write(self, trait_name: str, value: Any) -> bool: - """Pass `value` to trait `trait_name` of device.""" - setattr(self.turtle, trait_name, value) - - def invalidate(self, trait_name: str): - """Invalidate logical state of trait `trait_name`""" - if self.read(trait_name) == self.__getitem__(trait_name): - print("Everything's okey") - else: - print(f"Something went wrong with {trait_name}.") + def action_descriptors(self) -> Collection[ActionDescriptor]: + return self.get_descriptors()["actions"] def __getitem__(self, trait_name: str) -> Optional[Any]: """Return logical state of trait `trait_name`.""" - return getattr(self, trait_name) \ No newline at end of file + return self.descriptors[trait_name] + + def read_descriptors(self, arg): + self.descriptors = self.get_descriptors() + return self.descriptors + + def read(self, trait_name: str) -> Any: + """Read physical state of trait `trait_name` from device.""" + self.read_descriptors() + return self.descriptors[trait_name] + + def write(self, trait_name: str, value: Any) -> bool: + """Turtle traits are not writable""" + # trait_desc = self.trait_descriptors.get(trait_name) + # if trait_desc.writable: + # return getattr(self.turtle, trait_name)(value) + super().write() + + def execute(self, action_name: str, *args, **kwargs): + """Execute action `action_name`, using `args` and `kwargs` as action argument.""" + action = self.get_descriptors()["actions"].get(action_name) + if action: + getattr(self.turtle, action_name)(*args, **kwargs) + return + else: + raise DeviceError("No such action: `{}`".format(action_name)) + + def invalidate(self, trait_name: str): + """Invalidate logical state of trait `trait_name`""" + if self.trait_descriptors.get(trait_name): + self.trait_descriptors[trait_name] = None + raise DeviceError() + + def get_descriptors(self): + descriptors = dict(actions=dict(), traits=dict()) + + for m_name, member in inspect.getmembers(Turtle): + if m_name.startswith("_"): + continue + if m_name.lower() != m_name: + continue + doc = inspect.getdoc(member) + if doc is None: + continue + + if not inspect.isfunction(member): + descriptors["traits"][m_name] = TraitDescriptor( + m_name, doc, readable=True, writable=False + ) + else: + sig = inspect.signature(member) + params_dict = dict(sig.parameters) + descriptors["actions"][m_name] = ActionDescriptor( + m_name, arguments=params_dict, info=doc + ) + return descriptors diff --git a/noblocking_turtle_shell.py b/noblocking_turtle_shell.py index 471234d..011050a 100644 --- a/noblocking_turtle_shell.py +++ b/noblocking_turtle_shell.py @@ -1,19 +1,17 @@ import cmd -import threading -from queue import Empty, Queue +from threading import Thread, Event +from turtle import bye +from queue import Queue, Empty + from equipment.turtle_device import TurtleDevice -import argparse -import time -class TurtleDeviceThread(threading.Thread): - # TODO(Homework 4) +class TurtleDeviceThread(Thread): def __init__(self): super().__init__() self.device = TurtleDevice() self.queue = Queue() - self.event = Event() - + def run(self): while True: try: @@ -23,22 +21,9 @@ class TurtleDeviceThread(threading.Thread): else: if (item == 'exit'): break - self.device.execute(item[0], item[1:]) + self.device.execute(*item) self.queue.task_done() - def __message_processing__(self): - while True: - new_message = self.queue.get() - #print(type(new_message)) - #print(new_message) - #print("Name and args of action are {}".format(new_message)) - self.device.execute(*new_message) - #time.sleep(30) - self.queue.task_done() - if self.event.is_set(): - break - def __message_thread__(self): - thread = Thread(target = self.__message_processing__, daemon=True) - thread.start() + class NoBlockingTurtleShell(cmd.Cmd): intro = 'Welcome to the turtle shell. Type help or ? to list commands.\n' @@ -47,14 +32,27 @@ class NoBlockingTurtleShell(cmd.Cmd): def __init__(self, turtle_thread: TurtleDeviceThread): super(NoBlockingTurtleShell, self).__init__() - turtle_thread.__message_thread__() + self.turtle_thread = turtle_thread + def do_execute(self, arg): - turtle_thread.queue.put(parse(arg)) + self.turtle_thread.queue.put(parse(arg)) def do_exit(self, arg): - turtle_thread.event.set() + self.close() + self.turtle_thread.queue.put("exit") + print('Thank you for using Turtle') + return True + + + 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(arg.split()) if __name__ == '__main__': turtle_thread = TurtleDeviceThread() diff --git a/turtle_shell.py b/turtle_shell.py index ac22aab..20464cf 100644 --- a/turtle_shell.py +++ b/turtle_shell.py @@ -10,6 +10,7 @@ class TurtleShell(cmd.Cmd): # ----- 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'