def test_encode_differences(self): colours = [Colour() for each in self.leds] colours[2] = Colour("#123456") colours[4] = Colour("#789abc") colours, leds = itf.encode_differences(self.colours_prev, colours, self.leds) assert colours == [Colour("#123456"), Colour("#789abc")] assert leds == [2, 4]
def clear_leds(self, verbose=False): if self.simulate: self.print_leds() else: self._send(('C'), verbose=verbose) self.apply_leds(verbose=verbose) self.led_colours = [Colour() for each in LEDS]
def draw_bbox(self, frame): frame = frame.copy() x_min, y_min, x_max, y_max = self.bbox if self.label is None: # if the tank is not marked yet # draw a thin white box colour = (255, 255, 255) box_thickness = 1 cv2.rectangle(frame, (x_min, y_min), (x_max, y_max), colour, box_thickness) else: # if the tank is marked if self.label == -1: # no tank label_text = 'Not Tank' colour = (0, 0, 0) box_thickness = 1 text_size = 0.5 elif self.label == -2: # tank on tank occlusion label_text = 'T-T Occ' colour = (0, 0, 255) box_thickness = 3 text_size = 0.5 else: # the label is positive, meaning it is a proper tank label_text = 'ID: {}'.format(self.label) colour = Colour.choose_colour(self.label) box_thickness = 2 text_size = 0.5 text_colour = (255, 255, 255) cv2.rectangle(frame, (x_min, y_min), (x_max, y_max), colour, box_thickness) cv2.putText(frame, label_text, (x_min, y_min - 5), cv2.FONT_HERSHEY_SIMPLEX, text_size, text_colour, 1, cv2.LINE_AA) return frame
def test_encode_groups_complex(self): colours = [Colour() for each in self.leds] colours[2] = Colour("#123456") colours[4] = Colour("#789abc") colours[5] = Colour("#789abc") colours[6] = Colour("#789abc") groups = itf.encode_groups(colours, self.leds) assert list(groups) == [ (Colour(), [0, 1]), (Colour("#123456"), [2]), (Colour(), [3]), (Colour("#789abc"), [4, 5, 6]), (Colour(), [7, 8, 9]), ]
def cloud_drift( arduino, sky_colour=Colour(0, 0, 0), leds=range(60), velocity=5, ): ''' velocity: velocity of cloud shadow in LEDs / second ''' CLOUD_WIDTH = 25 start_position = leds[0] if velocity > 0 else leds[-1] + CLOUD_WIDTH cloud = Cloud(start_position, CLOUD_WIDTH, opacity=0.75) seconds = abs((len(leds) + CLOUD_WIDTH) / velocity) frames = int(ceil(FPS * seconds)) print(seconds, frames) for each in range(frames): cloud.position += velocity / FPS colours = cloud.calc_colours(leds, sky_colour) arduino.set_leds_to_colours(colours, leds) time.sleep(1.0 / FPS) # restore sky to original colour arduino.set_leds_to_colours([sky_colour] * len(leds), leds)
def handle_fade(json): global g colour_new = Colour(json['data']) # print(colour.rgb) get_arduino().fade_from_to(g['sky_colour'], colour_new, leds=LEDS, time=5.00) g['sky_colour'] = colour_new
def handle_set(json): global g colour = Colour(json['data']) # print(colour.rgb) get_arduino().set_leds_to_colours([colour] * len(LEDS), LEDS) g['sky_colour'] = colour
def _line(self, pieces, line): if len(pieces) != 7: raise PartError, "Invalid line data in %s at line %i" % (self.path, line) colour = int(pieces[0]) p1 = map(float, pieces[1:4]) p2 = map(float, pieces[4:7]) return Line(Colour(colour), Vector(*p1), Vector(*p2))
def _triangle(self, pieces, line): if len(pieces) != 10: raise PartError, "Invalid triangle data in %s at line %i" % (self.path, line) colour = int(pieces[0]) p1 = map(float, pieces[1:4]) p2 = map(float, pieces[4:7]) p3 = map(float, pieces[7:10]) return Triangle(Colour(colour), Vector(*p1), Vector(*p2), Vector(*p3))
def test_set_leds_to_colours(self, patched_send): # check compression happens correctly new_colours = [Colour() for each in range(10)] new_colours[2] = Colour("#123456") new_colours[4] = Colour("#789abc") new_colours[5] = Colour("#789abc") new_colours[6] = Colour("#789abc") self.arduino.set_leds_to_colours(new_colours, range(10)) calls = [ call(ANY, ('G', 2, 2, 148, 201, 86), verbose=ANY), call(ANY, ('G', 4, 6, 148, 92, 188), verbose=ANY), call(ANY, ('A'), verbose=ANY), ] patched_send.assert_has_calls(calls, any_order=False) # check that previous led colours are remembered patched_send.reset_mock() new_colours[2] = Colour() self.arduino.set_leds_to_colours(new_colours, range(10)) calls = [ call(ANY, ('G', 2, 2, 0, 0, 0), verbose=ANY), call(ANY, ('A'), verbose=ANY), ] patched_send.assert_has_calls(calls, any_order=False)
def _optional_line(self, pieces, line): if len(pieces) != 13: raise PartError, "Invalid line data in %s at line %i" % (self.path, line) colour = int(pieces[0]) p1 = map(float, pieces[1:4]) p2 = map(float, pieces[4:7]) p3 = map(float, pieces[7:10]) p4 = map(float, pieces[10:13]) return OptionalLine(Colour(colour), Vector(*p1), Vector(*p2), Vector(*p3), Vector(*p4))
def _quadrilateral(self, pieces, line): if len(pieces) != 13: raise PartError, "Invalid quadrilateral data in %s at line %i" % (self.path, line) colour = int(pieces[0]) p1 = map(float, pieces[1:4]) p2 = map(float, pieces[4:7]) p3 = map(float, pieces[7:10]) p4 = map(float, pieces[10:13]) return Quadrilateral(Colour(colour), Vector(*p1), Vector(*p2), Vector(*p3), Vector(*p4))
def test_encode(self): colours = [Colour() for each in self.leds] colours[2] = Colour("#123456") colours[4] = Colour("#789abc") colours[5] = Colour("#789abc") colours[6] = Colour("#789abc") groups = itf.encode(self.colours_prev, colours, self.leds) assert list(groups) == [ (Colour("#123456"), [2]), (Colour("#789abc"), [4, 5, 6]), ]
def _subfile(self, pieces, line): if len(pieces) != 14: raise PartError, "Invalid part data in %s at line %i" % (self.path, line) colour = int(pieces[0]) position = map(float, pieces[1:4]) rows = [map(float, pieces[4:7]), map(float, pieces[7:10]), map(float, pieces[10:13])] part = pieces[13].upper() if part.endswith(".DAT"): part = part[:-4] return Piece(Colour(colour), Vector(*position), Matrix(rows), part)
def fade_from_to( self, colour_old, colour_new, leds: List[int] = LEDS, time=1.00, fps=10 ): frames = int(ceil(fps * time)) h_old, s_old, v_old = colour_old.hsv h_new, s_new, v_new = colour_new.hsv for h, s, v in zip( linspace(h_old, h_new, frames), linspace(s_old, s_new, frames), linspace(v_old, v_new, frames), ): # print(Colour().from_hsv(h, s, v).hsv) self.set_leds_to_colours([Colour().from_hsv(h, s, v)] * len(leds), leds) sleep(1.0 / fps)
app = Flask(__name__) app.config.from_object('config_default') app.config.from_object('config') if not app.config['SECRET_KEY']: raise ValueError( "Please set constant SECRET_KEY in 'app/config.py' for Flask application" ) effects.FPS = app.config['FPS'] PORT = app.config['PORT'] BAUD = app.config['BAUD'] LEDS = app.config['LEDS'] g = {'sky_colour': Colour(128, 128, 128)} # turn the flask app into a socketio app socketio = SocketIO(app, async_mode=None, logger=True, engineio_logger=True) def get_arduino(): global g if 'arduino' not in g: print(" *** Connecting to Arduino ***") g['arduino'] = Arduino() g['arduino'].connect(port=PORT, baud=BAUD, acknowledge=True) # for each in g: # print(each)
def test_set_colour_block(self): self.arduino.set_colour_block(Colour("#12456"), range(10)) assert hasattr(self.arduino, 'ser') is False
def test_set_leds_to_colours(self): new_colours = [Colour() for each in range(10)] self.arduino.set_leds_to_colours(new_colours, range(10)) assert hasattr(self.arduino, 'ser') is False
def lightning_flash(arduino, sky_colour=Colour(0, 0, 0), leds=range(60)): # Based on the Storm_Cloud Arduino sketch: flash_sequences = ( [ # RGB Colour(255, 255, 255), # white sky_colour, # off Colour(100, 100, 150), # slight blue sky_colour, # off Colour(50, 50, 50), # off sky_colour, # off Colour(255, 255, 255), # white sky_colour, # off sky_colour, # off sky_colour, # off Colour(255, 120, 255), # purple ], [ Colour(100, 100, 150), # slight blue sky_colour, # off Colour(255, 255, 255), # white sky_colour, # off sky_colour, # off Colour(255, 120, 255), # purple sky_colour, # off sky_colour, # off Colour(255, 255, 255), # white sky_colour, # off Colour(50, 50, 50), # off ], [ Colour(255, 200, 120), # orange sky_colour, # off sky_colour, # off sky_colour, # off Colour(255, 255, 255), # white sky_colour, # off Colour(255, 120, 255), # purple sky_colour, # off Colour(50, 50, 50), # off sky_colour, # off Colour(200, 150, 100), # slight orange ], ) for i, each in enumerate(random.choice(flash_sequences)): arduino.set_leds_to_colours([each] * len(leds), leds) time.sleep(random.randrange(5, 100) / 1000) # restore sky to original colour arduino.set_leds_to_colours([sky_colour] * len(leds), leds)
def test_encode_groups_simple(self): groups = itf.encode_groups(self.colours_prev, self.leds) assert list(groups) == [(Colour(), list(range(10)))]
class Arduino: block_until = 0 led_colours = [Colour() for each in LEDS] simulate = False def connect(self, port=None, baud=9600, acknowledge=False, verbose=False): if verbose: print(f"Connecting to '{PORT}' at {baud} baud...") if port is None: self.simulate = True if self.simulate: print("Connected to Arduino (simulated)") else: self.ser = Serial(port, baud, timeout=5) try: if b'ready' in self.ser.readline(): if acknowledge: self.clear_leds() return True except self.ser.SerialTimeoutException: print("Data could not be read") def disconnect(self): if self.simulate: print("Closing connection") else: if self.ser: self.ser.close() def print_leds(self): output = '' for colour in self.led_colours: output += stylize(" ", bg(colour.hex)) print(output) def _send_str(self, command: bytes, verbose=False, read_nack=False): """ Commands: C: clear all lights to black A: apply all lights as set R: set a specific LED to a RGB colour H: set a specific LED to a HSV colour """ if verbose: print(f"Sending {command}") self.ser.write(command) # Read back negative acknowledge, will use timeout # Data only provide if command not recognised if read_nack: try: print("Reading...") print(self.ser.readline()) except self.ser.SerialTimeoutException: print("Data could not be read") def _send(self, command: Tuple, verbose=False): """ Convert a command-tuple to a command-string eg: (R, 255, 0, 128) -> b"R\xFF\x00\x80" """ # wait for strip to write previous command before sending next command while now() < self.block_until: sleep(TIME_BETWEEN_CMDS) try: self._send_str( b'<' + bytes((ord(command[0]),) + command[1:]) + b'>', verbose=verbose ) except TypeError: # for command letter only with no parameters self._send_str(b'<' + bytes([ord(command[0])]) + b'>', verbose=verbose) def apply_leds(self, verbose=False): if self.simulate: self.print_leds() else: self._send(('A'), verbose=verbose) self.block_until = now() + TIME_BETWEEN_APPLIES def clear_leds(self, verbose=False): if self.simulate: self.print_leds() else: self._send(('C'), verbose=verbose) self.apply_leds(verbose=verbose) self.led_colours = [Colour() for each in LEDS] def set_colour_block(self, colour: Colour, leds: List[int] = LEDS, verbose=False): ''' Sends commands directly over serial ''' if self.simulate: pass else: led_min = leds[0] led_max = leds[-1] self._send(('G', led_min, led_max, *colour.hsv), verbose=verbose) def set_leds_to_colours( self, new_colours: List[Colour], leds: List[int] = LEDS, verbose=False ): ''' Encodes LED commands to reduce serial communications ''' groups = encode(self.led_colours, new_colours, leds) for c, l in groups: self.set_colour_block(c, l, verbose=verbose) # remember colours for next command for n, l in zip(new_colours, leds): self.led_colours[l] = n self.apply_leds(verbose=verbose) def fade_from_to( self, colour_old, colour_new, leds: List[int] = LEDS, time=1.00, fps=10 ): frames = int(ceil(fps * time)) h_old, s_old, v_old = colour_old.hsv h_new, s_new, v_new = colour_new.hsv for h, s, v in zip( linspace(h_old, h_new, frames), linspace(s_old, s_new, frames), linspace(v_old, v_new, frames), ): # print(Colour().from_hsv(h, s, v).hsv) self.set_leds_to_colours([Colour().from_hsv(h, s, v)] * len(leds), leds) sleep(1.0 / fps)
def setup(self): self.leds = range(10) self.colours_prev = [Colour() for each in self.leds]
def test_set_colour_block(self, patched_send): self.arduino.set_colour_block(Colour("#12456"), range(10)) patched_send.assert_called_with(ANY, ('G', 0, 9, 76, 232, 69), verbose=ANY)