import ast import copy import csv class Move: moveType = None # i.e spin, extension, delay etc index = None # stores the index on which the move acts on # (in this case the side of the machine or the index of the LED) position = None # which position to goto (angle or retraction since I've reused the variable) delay = None # only used for delay moves colour = None # colour set to LEDs during the move # Validation dicts. Used to check weather the data that is given is valid _validMoves = {'spin', 'extension', 'delay', 'stop', 'led', 'photo'} _validSides = {'b': 0, 'r': 1, 'f': 2, 'l': 3, 0: 0, 1: 1, 2: 2, 3: 3} _validAngles = {0, 1, 2, 3} _validPositions = {'f', 'e', 'r'} def __init__(self, _move_type, _index=None, _position=None, _colour=None, _delay=0.0): if _move_type in self._validMoves: self.moveType = _move_type else: raise ValueError("Invalid Move Type") if _move_type == 'spin': try: self.index = self._validSides.get(_index) except KeyError: raise ValueError("Invalid Side Num") if int(_position) in range(4): self.position = int(_position) else: raise ValueError("Invalid position num") elif _move_type == 'extension': try: self.index = self._validSides.get(_index) except KeyError: raise ValueError("Invalid Side Num") if _position in self._validPositions: self.position = _position else: raise ValueError("Position must be either f, e or r") elif _move_type == 'delay': self.delay = _delay elif _move_type == 'stop': # stops the servos 'pushing' when they are not being activated anymore self.index = _index elif _move_type == 'led': if _index in range(-1, 8): self.index = _index # in this case, index is the index of the LED else: raise ValueError("Invalid LED Index") # todo error checking self.colour = _colour def execute(self, _machine): if self.moveType == 'spin': _machine.goto(self.index, self.position) elif self.moveType == 'extension': if self.position == 'e': _machine.extend(self.index) elif self.position == 'r': _machine.retract(self.index) elif self.position == 'f': _machine.full_extend(self.index) elif self.moveType == 'delay': return {'type': 'delay', 'content': self.delay} elif self.moveType == 'stop': # todo check if this works _machine.set_angle(self.index, None) _machine.set_angle(self.index + 4, None) elif self.moveType == 'led': _machine.set_leds(self.index, self.colour) elif self.moveType == 'photo': _machine.take_photo() return {'type': 'none'} class MoveList: moves: list[Move] = [] moves2 = None counter = 1 machine = None waitSeconds = 0 _running = False def __init__(self, _machine, _moves: list[Move] = None, csv_path=None): self.machine = _machine if _moves is None: self.read_from_csv(csv_path) else: self.moves = _moves def read_from_csv(self, csv_path): with open(csv_path, newline='') as csvfile: csv_reader = csv.DictReader(csvfile, delimiter=',', quotechar='|') for row in csv_reader: row_d = dict(row) # bit of a mess here to ensure all values are cast correctly move_type = row_d['move_type'] if not move_type: raise ValueError("move_type passed empty") index = None if row_d['index']: index = int(row_d['index']) position = None if row_d['position']: try: position = int(row_d['position']) except ValueError: position = row_d['position'] colour = None if row_d['colour'] is not None: colour = row_d['colour'] delay = 0 if row_d['delay']: delay = float(row_d['delay']) self.moves.append(Move( move_type, index, position, colour, delay) ) def add_move(self, _move_type, _index=None, _position=None, _colour=None, _delay=0.0): self.moves.append(Move(_move_type, _index, _position, _colour, _delay)) def send_list_request(self): self.machine.execute_move_list(self.moves) def execute(self): # dynamically executes the moves each time tick() is called self.moves2 = copy.copy(self.moves) self._running = True def tick(self): if self._running: self.waitSeconds -= .05 if self.waitSeconds <= 0: while True: move_delay = 0 try: move_response = self.moves2[0].execute(self.machine) except IndexError: self._running = False self.counter -= 1 if self.counter != 0: self.execute() break self.moves2.pop(0) if move_response['type'] == 'delay': move_delay = move_response['value'] self.waitSeconds = move_delay elif move_response['type'] == 'camera': # hand down the response to be further handled return move_response