From b10656bba3ae98d7c3ba035c55f29bb19411938f Mon Sep 17 00:00:00 2001 From: bav6096 <benedikt.deike@informatik.uni-hamburg.de> Date: Tue, 7 Dec 2021 02:43:02 +0100 Subject: [PATCH] refactor codebase --- CMakeLists.txt | 4 +- scripts/play_animation | 23 --- scripts/play_animation.py | 27 ++++ scripts/reset_display.py | 18 +++ src/{visualising/animations => }/__init__.py | 0 src/visualising/__init__.py | 1 + src/visualising/animation.py | 130 --------------- .../animations/animation_happy.txt | 4 - src/visualising/arduino.py | 65 -------- src/visualising/communication/__init__.py | 1 + src/visualising/communication/arduino.py | 74 +++++++++ .../communication/channel/__init__.py | 0 .../communication/channel/connection.py | 70 ++++++++ .../communication/channel/message.py | 83 ++++++++++ .../communication/illustration/__init__.py | 0 .../communication/illustration/animation.py | 23 +++ .../communication/illustration/ensemble.py | 9 ++ .../communication/illustration/frame.py | 27 ++++ .../communication/illustration/pixel.py | 13 ++ src/visualising/connection.py | 102 ------------ src/visualising/countenance.py | 30 ---- src/visualising/expression/__init__.py | 0 src/visualising/expression/expression.py | 0 .../expression/library/__init__.py | 0 .../expression/library/animation_happy.txt | 12 ++ .../library}/animation_ok.txt | 0 .../library}/animation_sad.txt | 0 .../expression/library/expression_angry.txt | 2 + .../expression/library/expression_clean.txt | 2 + .../expression/library/expression_happy.txt | 2 + .../expression/library/expression_ok.txt | 0 .../library/transition_happy_happy.txt | 34 ++++ src/visualising/expression/tool.py | 149 ++++++++++++++++++ src/visualising/message.py | 50 ------ 34 files changed, 549 insertions(+), 406 deletions(-) delete mode 100755 scripts/play_animation create mode 100755 scripts/play_animation.py create mode 100644 scripts/reset_display.py rename src/{visualising/animations => }/__init__.py (100%) delete mode 100755 src/visualising/animation.py delete mode 100644 src/visualising/animations/animation_happy.txt delete mode 100755 src/visualising/arduino.py create mode 100644 src/visualising/communication/__init__.py create mode 100755 src/visualising/communication/arduino.py create mode 100644 src/visualising/communication/channel/__init__.py create mode 100755 src/visualising/communication/channel/connection.py create mode 100755 src/visualising/communication/channel/message.py create mode 100644 src/visualising/communication/illustration/__init__.py create mode 100755 src/visualising/communication/illustration/animation.py create mode 100644 src/visualising/communication/illustration/ensemble.py create mode 100644 src/visualising/communication/illustration/frame.py create mode 100644 src/visualising/communication/illustration/pixel.py delete mode 100755 src/visualising/connection.py delete mode 100755 src/visualising/countenance.py create mode 100644 src/visualising/expression/__init__.py create mode 100755 src/visualising/expression/expression.py create mode 100644 src/visualising/expression/library/__init__.py create mode 100644 src/visualising/expression/library/animation_happy.txt rename src/visualising/{animations => expression/library}/animation_ok.txt (100%) rename src/visualising/{animations => expression/library}/animation_sad.txt (100%) create mode 100644 src/visualising/expression/library/expression_angry.txt create mode 100644 src/visualising/expression/library/expression_clean.txt create mode 100644 src/visualising/expression/library/expression_happy.txt create mode 100644 src/visualising/expression/library/expression_ok.txt create mode 100644 src/visualising/expression/library/transition_happy_happy.txt create mode 100644 src/visualising/expression/tool.py delete mode 100755 src/visualising/message.py diff --git a/CMakeLists.txt b/CMakeLists.txt index e4a86b7..4f777fb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -49,8 +49,6 @@ catkin_python_setup() ## Generate messages in the 'msg' folder add_message_files( FILES - State.msg - States.msg ) ## Generate services in the 'srv' folder @@ -162,6 +160,8 @@ include_directories( ## in contrast to setup.py, you can choose the destination catkin_install_python(PROGRAMS nodes/visualiser + scripts/play_animation.py + scripts/reset_display.py DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION} ) diff --git a/scripts/play_animation b/scripts/play_animation deleted file mode 100755 index af2e9f7..0000000 --- a/scripts/play_animation +++ /dev/null @@ -1,23 +0,0 @@ -#!/usr/bin/env python - -import argparse - -from visualising.arduino import Arduino -from visualising.animation import Animation -from visualising.connection import Connection - -parser = argparse.ArgumentParser(description="script to test animations") -parser.add_argument("-f", "--frames", help="frames to be played", required=True) -parser.add_argument("-t", "--time", type=int, help="time between frames", required=True) -parser.add_argument("-i", "--iterations", type=int, help="number of iterations", required=True) -args = vars(parser.parse_args()) - -animation = args["frames"] -frame_time = args["time"] -num_iter = args["iterations"] -port = "/dev/ttyUSB0" -baud = 57600 - -arduino = Arduino(Connection(port, baud)) -arduino.stream_animation(Animation(Animation.read_frames_from_file(animation), frame_time, num_iter)) - diff --git a/scripts/play_animation.py b/scripts/play_animation.py new file mode 100755 index 0000000..be6e951 --- /dev/null +++ b/scripts/play_animation.py @@ -0,0 +1,27 @@ +#!/usr/bin/env python + +import argparse + +from visualising.expression.tool import Tool +from visualising.communication.arduino import Arduino +from visualising.communication.illustration.animation import Animation +from visualising.communication.channel.connection import Connection + +parser = argparse.ArgumentParser(description="script to play an animation") +parser.add_argument("-p", "--port", help="port to which the Arduino is connected", type=str, default="/dev/ttyUSB0") +parser.add_argument("-b", "--baud", help="baud rate of the connection", type=int, default=57600) +parser.add_argument("-f", "--file", help="file in the library directory to be played", type=str, required=True) +parser.add_argument("-t", "--time", help="time between ensembles", type=int, default=100) +parser.add_argument("-i", "--iterations", help="number of iterations", type=int, default=1) +args = vars(parser.parse_args()) + +port = args["port"] +baud = args["baud"] +file = args["file"] +time = args["time"] +iter = args["iterations"] + +arduino = Arduino(Connection(port, baud)) +animation = Tool.create_animation(file, time, iter) + +arduino.stream_animation(animation) diff --git a/scripts/reset_display.py b/scripts/reset_display.py new file mode 100644 index 0000000..c2e86e2 --- /dev/null +++ b/scripts/reset_display.py @@ -0,0 +1,18 @@ +#!/usr/bin/env python + +import argparse + +from visualising.communication.arduino import Arduino +from visualising.communication.channel.connection import Connection + +parser = argparse.ArgumentParser(description="script to reset the NeoPixel rings") +parser.add_argument("-p", "--port", help="port to which the Arduino is connected", type=str, default="/dev/ttyUSB0") +parser.add_argument("-b", "--baud", help="baud rate of the connection", type=int, default=57600) +args = vars(parser.parse_args()) + +port = args["port"] +baud = args["baud"] + +arduino = Arduino(Connection(port, baud)) + +arduino.reset_display() diff --git a/src/visualising/animations/__init__.py b/src/__init__.py similarity index 100% rename from src/visualising/animations/__init__.py rename to src/__init__.py diff --git a/src/visualising/__init__.py b/src/visualising/__init__.py index e69de29..8b13789 100644 --- a/src/visualising/__init__.py +++ b/src/visualising/__init__.py @@ -0,0 +1 @@ + diff --git a/src/visualising/animation.py b/src/visualising/animation.py deleted file mode 100755 index fc698b6..0000000 --- a/src/visualising/animation.py +++ /dev/null @@ -1,130 +0,0 @@ -#!/usr/bin/env python - -import re -import importlib.resources - -from . import animations - - -class Animation: - def __init__(self, frames, frame_time=0, num_iter=1): - num_frames = len(frames) - - if num_frames % 2 != 0: - raise ValueError("Number of frames must be even!") - - for frame in frames: - num_values = len(frame) - - if num_values != 48: - raise ValueError("All frames must have exactly 48 values!") - - is_numeric = True - is_positive = True - is_small = True - - for value in frame: - is_numeric = is_numeric and isinstance(value, int) - is_positive = is_positive and not (value < 0) - is_small = is_small and value < 256 - - if not is_numeric: - raise TypeError("All frame values must be integers!") - if not is_positive: - raise ValueError("All frame values must be positive!") - if not is_small: - raise ValueError("All frame values must be smaller than 256!") - - self.frames = frames - self.num_frames = num_frames - self.num_ensembles = int(num_frames / 2) - - if not isinstance(frame_time, int): - raise TypeError("Frame time must be an integer!") - if frame_time < 0: - raise ValueError("Frame time must be positive!") - if frame_time > 4294967295: - raise ValueError("Frame time is to high!") - - # The frame time is given in milliseconds. - self.frame_time = frame_time - - if not isinstance(num_iter, int): - raise TypeError("Number of iterations must be an integer!") - if num_iter > 255: - raise ValueError("Animation has to many iterations!") - if num_iter <= 0: - raise ValueError("Animation must have at least one iteration!") - - self.num_iter = num_iter - - @staticmethod - def read_frames_from_file(filename): - file = importlib.resources.open_text(animations, filename) - - frames = [] - for line in file: - frame = [] - - for value in line.split(','): - frame.append(int(re.search(r'\d+', value).group())) - - frames.append(frame) - - return frames - - @staticmethod - def create_empty_ensemble(): - frame1 = [0] * 16 - frame2 = [0] * 16 - return [frame1, frame2] - - @staticmethod - def set_pixel_color(ensemble, pixel, r, g, b): - ensemble_len = len(ensemble) - - if ensemble_len != 2: - raise ValueError("Ensemble must contain exactly two frames!") - - for frame in ensemble: - num_values = len(frame) - - if num_values != 48: - raise ValueError("All frames must have exactly 48 values!") - - is_numeric = True - is_positive = True - is_small = True - - for value in frame: - is_numeric = is_numeric and isinstance(value, int) - is_positive = is_positive and not (value < 0) - is_small = is_small and value < 256 - - if not is_numeric: - raise TypeError("All frame values must be integers!") - if not is_positive: - raise ValueError("All frame values must be positive!") - if not is_small: - raise ValueError("All frame values must be smaller than 256!") - - if not isinstance(pixel, int): - raise TypeError("Pixel must be an integer!") - if not (0 < pixel < 33): - raise ValueError("Pixel must be in the range of 1 to 32!") - if not (isinstance(r, int) and isinstance(g, int) and isinstance(b, int)): - raise TypeError("Color values must be integers!") - if not ((0 < r < 256) and (0 < g < 256) and (0 < b < 256)): - raise ValueError("Color values must be in the range of 1 to 255!") - - if pixel < 16: - index = 0 - else: - index = 1 - pixel = pixel - 16 - - ensemble[index][pixel + 0] = r - ensemble[index][pixel + 1] = g - ensemble[index][pixel + 2] = b - - return ensemble diff --git a/src/visualising/animations/animation_happy.txt b/src/visualising/animations/animation_happy.txt deleted file mode 100644 index d7827c7..0000000 --- a/src/visualising/animations/animation_happy.txt +++ /dev/null @@ -1,4 +0,0 @@ -0,0,50,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 -50,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 \ No newline at end of file diff --git a/src/visualising/arduino.py b/src/visualising/arduino.py deleted file mode 100755 index f1c6ef1..0000000 --- a/src/visualising/arduino.py +++ /dev/null @@ -1,65 +0,0 @@ -#!/usr/bin/env python - -import time - -from visualising.message import FrameMsg, InstrMsg -from visualising.animation import Animation -from visualising.connection import ArduinoException - - -class Arduino: - def __init__(self, connection): - self.connection = connection - - # Creates instruction message and sends it to the Arduino, - # to play a loaded animation. - def start_playback(self): - self.connection.confirm_msg(InstrMsg('B').create_msg(), [b'\x1f'], 5) - - # Creates instruction message and sends it to the Arduino, - # to pause a playing animation. - def pause_playback(self): - self.connection.confirm_msg(InstrMsg('C').create_msg(), [b'\x5f'], 5) - - # Creates instruction message and sends it to the Arduino, - # to resume playback of a loaded animation. - def resume_playback(self): - self.connection.confirm_msg(InstrMsg('D').create_msg(), [b'\x6f'], 5) - - # Creates one instruction messages and multiple frame messages to load an animation - # onto the Arduino. - def load_animation(self, animation): - if animation.num_ensembles > 16: - raise ValueError("Animation has to many frames!") - else: - self.connection.confirm_msg(InstrMsg('A', animation).create_msg(), [b'\x0f'], 5) - - for frame in animation.frames: - self.connection.confirm_msg(FrameMsg(frame).create_msg(), [b'\x2f', b'\x3f'], 10) - - # First, an animation is loaded onto the Arduino. The playback of this - # animation is then started. In addition, it is checked whether the - # animation was played successfully. - def play_animation(self, animation): - self.load_animation(animation) - self.start_playback() - - # Estimated playback time in seconds. - playback_time = animation.frame_time * animation.num_ensembles * 0.001 - # Current time in seconds. - current_time = time.time() - - while bytes(self.connection.receive_msg()) != bytes(b'\x4f'): - # Wait 10 times the estimated animation playback time for a response. - if time.time() > current_time + playback_time * 10: - raise ArduinoException("Successful playback of animation can't be confirmed!") - - # Streams an animation to the Arduino. For this purpose, two frames of - # the animation are repeatedly sent to the Arduino. The Arduino plays - # the received frames and then confirms the playback, whereupon new - # frames are sent. - def stream_animation(self, animation): - for _ in range(animation.num_iter): - for i in range(animation.num_ensembles): - ensemble = [animation.frames[2 * i + 0], animation.frames[2 * i + 1]] - self.play_animation(Animation(ensemble, animation.frame_time, 1)) diff --git a/src/visualising/communication/__init__.py b/src/visualising/communication/__init__.py new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/src/visualising/communication/__init__.py @@ -0,0 +1 @@ + diff --git a/src/visualising/communication/arduino.py b/src/visualising/communication/arduino.py new file mode 100755 index 0000000..f07fa4e --- /dev/null +++ b/src/visualising/communication/arduino.py @@ -0,0 +1,74 @@ +#!/usr/bin/env python + +import time + +from visualising.communication.channel.connection import FrameMsg +from visualising.communication.channel.connection import InstrMsg +from visualising.communication.channel.connection import ArduinoMsg +from visualising.communication.illustration.animation import Animation + + +class Arduino: + def __init__(self, connection): + self.connection = connection + + # Creates instruction message and sends it to the Arduino, + # to play a loaded animation. + def start_playback(self): + self.connection.confirm_msg(InstrMsg('B'), [ArduinoMsg(b'\x1f')], 5) + + # Creates instruction message and sends it to the Arduino, + # to pause a playing animation. + def pause_playback(self): + self.connection.confirm_msg(InstrMsg('C'), [ArduinoMsg(b'\x5f')], 5) + + # Creates instruction message and sends it to the Arduino, + # to resume playback of a loaded animation. + def resume_playback(self): + self.connection.confirm_msg(InstrMsg('D'), [ArduinoMsg(b'\x6f')], 5) + + # Creates instruction message and sends it to the Arduino, + # to clear the NeoPixel rings. + def reset_display(self): + self.connection.confirm_msg(InstrMsg('E'), [ArduinoMsg(b'\x7f')], 5) + + # Creates one instruction messages and multiple frame messages to load an animation + # onto the Arduino. + def load_animation(self, animation): + if len(animation.ensembles) > 16: + raise ValueError("Animation has to many ensembles!") + + self.connection.confirm_msg(InstrMsg('A', animation), [ArduinoMsg(b'\x0f')], 5) + + for ensemble in animation.ensembles: + l_frame = ensemble.l_frame + r_frame = ensemble.r_frame + + self.connection.confirm_msg(FrameMsg(l_frame), [ArduinoMsg(b'\x2f'), ArduinoMsg(b'\x3f')], 10) + self.connection.confirm_msg(FrameMsg(r_frame), [ArduinoMsg(b'\x2f'), ArduinoMsg(b'\x3f')], 10) + + # First, an animation is loaded onto the Arduino. The playback of this + # animation is then started. In addition, it is checked whether the + # animation was played successfully. + def play_animation(self, animation): + self.load_animation(animation) + self.start_playback() + + # Estimated playback time in seconds. + playback_time = animation.ensemble_time * len(animation.ensembles) * 0.001 + # Current time in seconds. + curr_time = time.time() + + while not ArduinoMsg(b'\x4f').compare(self.connection.receive_msg()): + # Wait 10 times the estimated animation playback time for a response. + if time.time() > curr_time + playback_time * 10: + raise ArduinoException("Successful playback of animation can't be confirmed!") + + # Streams an entities to the Arduino. For this purpose, two frames_library of + # the entities are repeatedly sent to the Arduino. The Arduino plays + # the received frames_library and then confirms the playback, whereupon new + # frames_library are sent. + def stream_animation(self, animation): + for _ in range(animation.num_iter): + for ensemble in animation.ensembles: + self.play_animation(Animation([ensemble], animation.ensemble_time, 1)) diff --git a/src/visualising/communication/channel/__init__.py b/src/visualising/communication/channel/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/visualising/communication/channel/connection.py b/src/visualising/communication/channel/connection.py new file mode 100755 index 0000000..0f76509 --- /dev/null +++ b/src/visualising/communication/channel/connection.py @@ -0,0 +1,70 @@ +#!/usr/bin/env python + +import time +import serial + +from visualising.communication.channel.message import FrameMsg +from visualising.communication.channel.message import InstrMsg +from visualising.communication.channel.message import ArduinoMsg + + +class ArduinoException(Exception): + def __init__(self, desc): + self.desc = desc + + +class Connection: + def __init__(self, port, baud): + self.connection = serial.Serial() + self.connection.port = port + self.connection.baudrate = baud + self.connection.timeout = 1 + + self.connection.open() # Establishes a serial connection. + + time.sleep(2) # Prevents errors regarding pyserial. + + # Send a message to the Arduino. + def send_msg(self, msg): + for byte in msg.content: + self.connection.write(byte) + + self.connection.reset_output_buffer() # Clear serial output buffer. + + # Receive a message from the Arduino. + def receive_msg(self): + try: + # Since the timeout is set to one second, there is a waiting period of + # exactly one second to read a byte from the serial buffer. If no byte + # is read in this one second period, an exception is thrown. + byte = self.connection.read(1) + except SerialException: # Arduino has not responded in time. + byte = b'\xff' + + self.connection.reset_input_buffer() # Clear serial input buffer. + + return ArduinoMsg(byte) + + # Send a message and confirm that the response is as expected. + # If the response is not as expected, try again as often as + # specified. In case any response wasn't even once one of the + # expected responses raise an exception. + def confirm_msg(self, msg, responses, resends): + def evaluate(): + bool = resends > 0 + for exp_resp in responses: + bool = bool and not resp.compare(exp_resp) + return bool + + if not resends > 0: + raise ValueError("Number of resends must be greater than zero!") + + while True: + self.send_msg(msg) + resp = self.receive_msg() + if not evaluate(): + break + resends -= 1 + + if not resends > 0: + raise ArduinoException(resp.desc) diff --git a/src/visualising/communication/channel/message.py b/src/visualising/communication/channel/message.py new file mode 100755 index 0000000..5150bef --- /dev/null +++ b/src/visualising/communication/channel/message.py @@ -0,0 +1,83 @@ +#!/usr/bin/env python + +class FrameMsg: + # Creates a 50-bit frame message. The first and last bit are intended for authentication + # as a frame message. The remaining 48 bits store exactly one frame. + def __init__(self, frame): + self.content = [bytes('{', 'ascii')] + + for pixel in frame.pixels: + r = pixel.r + g = pixel.g + b = pixel.b + + self.content.append(r.to_bytes(1, byteorder='big')) + self.content.append(g.to_bytes(1, byteorder='big')) + self.content.append(b.to_bytes(1, byteorder='big')) + + self.content.append(bytes('}', 'ascii')) + + +class InstrMsg: + # Creates a 50-bit instruction message. Since only 7 bits at most are required for the + # actual message, most of the message consists only of zeros. The first and last bit + # are intended for authentication as an instruction message. + def __init__(self, instr, animation=None): + self.content = [bytes('[', 'ascii'), bytes(instr, 'ascii')] + + if animation is not None: + num_frames = len(animation.ensembles) * 2 + self.content.append(num_frames.to_bytes(1, byteorder='big')) + + ensemble_time_bytes = animation.ensemble_time.to_bytes(4, byteorder='big') + self.content.append((ensemble_time_bytes[0]).to_bytes(1, byteorder='big')) + self.content.append((ensemble_time_bytes[1]).to_bytes(1, byteorder='big')) + self.content.append((ensemble_time_bytes[2]).to_bytes(1, byteorder='big')) + self.content.append((ensemble_time_bytes[3]).to_bytes(1, byteorder='big')) + + self.content.append(animation.num_iter.to_bytes(1, byteorder='big')) + + for _ in range(41): + self.content.append((0).to_bytes(1, byteorder='big')) + else: + for _ in range(47): + self.content.append((0).to_bytes(1, byteorder='big')) + + self.content.append(bytes(']', 'ascii')) + + +class ArduinoMsg: + # Generates an Arduino message, which is composed of a message byte from the Arduino + # and a translation of the message byte. + def __init__(self, byte): + self.content = byte + + translation = { + 240: "Animation has to many frame ensembles!", # 0xF0 + 241: "No entities is loaded!", # 0xF1 + 242: "Instruction was not recognized!", # 0xF2 + 243: "No entities message expected!", # 0xF3 + 244: "Message has wrong format!", # 0xF4 + 245: "Animation is playing!", # 0xF5 + 246: "Animation has no iteration!", # 0xF6 + 247: "Unequal number of frames_library for the left and right NeoPixel ring!", # 0xF7 + 248: "Animation has no frame ensemble!", # 0xF8 + 255: "Timeout!", # 0xFF + 0: "No response!", # 0x00 + 15: "Waiting for frames_library to be send!", # 0x0F + 31: "Animation playback has been started!", # 0x1F + 47: "Frame successfully received!", # 0x2F + 63: "Last frame successfully received!", # 0x3F + 79: "Animation successfully played!", # 0x4F + 95: "Animation playback has been paused!", # 0x5F + 111: "Animation playback has been resumed!", # 0x6F + 127: "Displays have been cleared!", # 0x7F + } + + try: + self.desc = translation[int.from_bytes(byte, byteorder='big')] + except KeyError: + self.desc = "Unknown response!" + + def compare(self, msg): + return bytes(self.content) == bytes(msg.content) diff --git a/src/visualising/communication/illustration/__init__.py b/src/visualising/communication/illustration/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/visualising/communication/illustration/animation.py b/src/visualising/communication/illustration/animation.py new file mode 100755 index 0000000..f6d4ed5 --- /dev/null +++ b/src/visualising/communication/illustration/animation.py @@ -0,0 +1,23 @@ +#!/usr/bin/env python + +from visualising.communication.illustration.ensemble import Ensemble + + +class Animation: + def __init__(self, ensembles, ensemble_time=0, num_iter=1): + self.ensembles = ensembles + + if ensemble_time < 0: + raise ValueError("Ensemble time must be positive!") + if ensemble_time > 4294967295: + raise ValueError("Ensemble time is to high!") + + # The ensemble time is given in milliseconds. + self.ensemble_time = ensemble_time + + if num_iter > 255: + raise ValueError("Animation has to many iterations!") + if num_iter <= 0: + raise ValueError("Animation must have at least one iteration!") + + self.num_iter = num_iter diff --git a/src/visualising/communication/illustration/ensemble.py b/src/visualising/communication/illustration/ensemble.py new file mode 100644 index 0000000..6a6ca87 --- /dev/null +++ b/src/visualising/communication/illustration/ensemble.py @@ -0,0 +1,9 @@ +#!/usr/bin/env python + +from visualising.communication.illustration.frame import Frame + + +class Ensemble: + def __init__(self, l_frame, r_frame): + self.l_frame = l_frame + self.r_frame = r_frame diff --git a/src/visualising/communication/illustration/frame.py b/src/visualising/communication/illustration/frame.py new file mode 100644 index 0000000..b8aa281 --- /dev/null +++ b/src/visualising/communication/illustration/frame.py @@ -0,0 +1,27 @@ +#!/usr/bin/env python + +from visualising.communication.illustration.pixel import Pixel + + +class Frame: + def __init__(self, pixels): + if len(pixels) != 16: + raise ValueError("Frames must have exactly 16 pixels!") + + self.pixels = pixels + + def adjust_brightness(self, pixel, factor): + if not 0 <= pixel <= 15: + raise ValueError("Pixel index must be an integer between 0 and 15!") + + r = min(max(self.frame[pixel].r * factor, 0), 255) + g = min(max(self.frame[pixel].g * factor, 0), 255) + b = min(max(self.frame[pixel].b * factor, 0), 255) + + self.pixels[pixel] = Pixel(r, g, b) + + def adjust_color(self, pixel, r, g, b): + if not 0 <= pixel <= 15: + raise ValueError("Pixel index must be an integer between 0 and 15!") + + self.pixels[pixel] = Pixel(r, g, b) diff --git a/src/visualising/communication/illustration/pixel.py b/src/visualising/communication/illustration/pixel.py new file mode 100644 index 0000000..8bf68ce --- /dev/null +++ b/src/visualising/communication/illustration/pixel.py @@ -0,0 +1,13 @@ +#!/usr/bin/env python + +class Pixel: + def __init__(self, r, g, b): + if r < 0 or g < 0 or b < 0: + raise ValueError("All pixel color values must be positive!") + if r > 255 or g > 255 or b > 255: + raise ValueError("All pixel color values must be smaller than 256!") + + self.r = int(r) + self.g = int(g) + self.b = int(b) + diff --git a/src/visualising/connection.py b/src/visualising/connection.py deleted file mode 100755 index ae62629..0000000 --- a/src/visualising/connection.py +++ /dev/null @@ -1,102 +0,0 @@ -#!/usr/bin/env python - -import time -import serial - - -class ArduinoException(Exception): - def __init__(self, desc): - self.desc = desc - - -class Connection: - def __init__(self, port, baud): - self.connection = serial.Serial() - self.connection.port = port - self.connection.baudrate = baud - self.connection.timeout = 1 - - self.connection.open() # Establishes a serial connection. - - time.sleep(2) # Prevents errors regarding pyserial. - - # Send a message to the Arduino. - def send_msg(self, msg): - for byte in msg: - self.connection.write(byte) - self.connection.reset_output_buffer() # Clear serial output buffer. - - # Receive a message from the Arduino. - def receive_msg(self): - try: - # Since the timeout is set to one second, there is a waiting period of - # exactly one second to read a byte from the serial buffer. If no byte - # is read in this one second period, an exception is thrown. - msg = self.connection.read(1) - except SerialException: # Arduino has not responded in time. - msg = b'\xff' - - self.connection.reset_input_buffer() # Clear serial input buffer. - return msg - - # Translates a 8-bit message from the Arduino. - @staticmethod - def translate_msg(msg): - translation = { - 240: "Animation has to many frame ensembles!", # 0xF0 - 241: "No animation is loaded!", # 0xF1 - 242: "Instruction was not recognized!", # 0xF2 - 243: "No data message expected!", # 0xF3 - 244: "Message has wrong format!", # 0xF4 - 245: "Animation is playing!", # 0xF5 - 246: "Animation has no iteration!", # 0xF6 - 247: "Unequal number of frames for the left and right NeoPixel ring!", # 0xF7 - 248: "Animation has no frame ensemble!", # 0xF8 - 255: "Timeout!", # 0xFF - 0: "No response!", # 0x00 - 15: "Waiting for frames to be send!", # 0x0F - 31: "Animation playback has been started!", # 0x1F - 47: "Frame successfully received!", # 0x2F - 63: "Last frame successfully received!", # 0x3F - 79: "Animation successfully played!", # 0x4F - 95: "Animation playback has been paused!", # 0x5F - 111: "Animation playback has been resumed!", # 0x6F - } - - try: - return translation[int.from_bytes(msg, byteorder='big')] - except KeyError: - return "Unknown response!" - - # Send a message and confirm that the response is as expected. - # If the response is not as expected, try again as often as - # specified. In case any response wasn't even once one of the - # expected responses raise an exception. - def confirm_msg(self, msg, expected_responses, num_resend): - def evaluate(): - bool = num_resend > 0 - for element in expected_responses: - bool = bool and (bytes(element) != bytes(response)) - return bool - - if not isinstance(expected_responses, list): - raise TypeError("Expected responses must be given as a list!") - if not expected_responses: - raise ValueError("There must be at least one expected response!") - if not isinstance(num_resend, int): - raise TypeError("Number of resends must be an integer!") - if not num_resend > 0: - raise ValueError("Number of resends must be greater than zero!") - - self.send_msg(msg) - response = self.receive_msg() - - condition = evaluate() - while condition: - num_resend = num_resend - 1 - self.send_msg(msg) - response = self.receive_msg() - condition = evaluate() - - if not num_resend > 0: - raise ArduinoException(Connection.translate_msg(response)) diff --git a/src/visualising/countenance.py b/src/visualising/countenance.py deleted file mode 100755 index c26f685..0000000 --- a/src/visualising/countenance.py +++ /dev/null @@ -1,30 +0,0 @@ -#!/usr/bin/env python - -from visualising.animation import Animation - -class Emotion: - def __init__(self): - self.frame_buffer = [] - - self. - - self.happy = Animation(Animation.read_frames_from_file("animation_happy.txt"), 100, 1) - self.ok = Animation(Animation.read_frames_from_file("animation_ok.txt"), 100, 1) - self.sad = Animation(Animation.read_frames_from_file("animation_sad.txt"), 100, 1) - - def transition_start(self, animation): - - - def transition_change(self, animation): - - def visualise(self, animation): - - - while not rospy.is_shutdown(): - if self.watchdog.aggregated_domains > 0.7: - self.stream_animation(sad) - else: - if self.watchdog.aggregated_domains > 0.4: - self.stream_animation(ok) - else: - self.stream_animation(happy) \ No newline at end of file diff --git a/src/visualising/expression/__init__.py b/src/visualising/expression/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/visualising/expression/expression.py b/src/visualising/expression/expression.py new file mode 100755 index 0000000..e69de29 diff --git a/src/visualising/expression/library/__init__.py b/src/visualising/expression/library/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/visualising/expression/library/animation_happy.txt b/src/visualising/expression/library/animation_happy.txt new file mode 100644 index 0000000..6282148 --- /dev/null +++ b/src/visualising/expression/library/animation_happy.txt @@ -0,0 +1,12 @@ +50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,50,50,50 +50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,50,50,50 +50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 +50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,50,50,50,50,50,50 +0,0,0,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 +50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,50,50,50,50,50,50,50,50,50 +0,0,0,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 +50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,50,50,50,50,50,50,50,50,50 +50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 +50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,50,50,50,50,50,50 +50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,50,50,50 +50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,50,50,50 diff --git a/src/visualising/animations/animation_ok.txt b/src/visualising/expression/library/animation_ok.txt similarity index 100% rename from src/visualising/animations/animation_ok.txt rename to src/visualising/expression/library/animation_ok.txt diff --git a/src/visualising/animations/animation_sad.txt b/src/visualising/expression/library/animation_sad.txt similarity index 100% rename from src/visualising/animations/animation_sad.txt rename to src/visualising/expression/library/animation_sad.txt diff --git a/src/visualising/expression/library/expression_angry.txt b/src/visualising/expression/library/expression_angry.txt new file mode 100644 index 0000000..bfb8ee5 --- /dev/null +++ b/src/visualising/expression/library/expression_angry.txt @@ -0,0 +1,2 @@ +50,50,50,50,50,50,0,0,0,0,0,0,50,50,50,50,50,50,0,0,0,0,0,0,50,50,50,50,50,50,0,0,0,0,0,0,50,50,50,50,50,50,0,0,0,0,0,0 +50,50,50,50,50,50,0,0,0,0,0,0,50,50,50,50,50,50,0,0,0,0,0,0,50,50,50,50,50,50,0,0,0,0,0,0,50,50,50,50,50,50,0,0,0,0,0,0 \ No newline at end of file diff --git a/src/visualising/expression/library/expression_clean.txt b/src/visualising/expression/library/expression_clean.txt new file mode 100644 index 0000000..146cef1 --- /dev/null +++ b/src/visualising/expression/library/expression_clean.txt @@ -0,0 +1,2 @@ +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 \ No newline at end of file diff --git a/src/visualising/expression/library/expression_happy.txt b/src/visualising/expression/library/expression_happy.txt new file mode 100644 index 0000000..12b6349 --- /dev/null +++ b/src/visualising/expression/library/expression_happy.txt @@ -0,0 +1,2 @@ +50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,50,50,50 +50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,50,50,50 diff --git a/src/visualising/expression/library/expression_ok.txt b/src/visualising/expression/library/expression_ok.txt new file mode 100644 index 0000000..e69de29 diff --git a/src/visualising/expression/library/transition_happy_happy.txt b/src/visualising/expression/library/transition_happy_happy.txt new file mode 100644 index 0000000..493d681 --- /dev/null +++ b/src/visualising/expression/library/transition_happy_happy.txt @@ -0,0 +1,34 @@ +50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,50,50,50 +50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,50,50,50 +50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 +50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 +0,0,0,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 +0,0,0,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 +0,0,0,0,0,0,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,0,0,0,0,0,0,0,0,0,0,0,0 +0,0,0,0,0,0,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,0,0,0,0,0,0,0,0,0,0,0,0 +0,0,0,0,0,0,0,0,0,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,0,0,0,0,0,0,0,0,0 +0,0,0,0,0,0,0,0,0,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,0,0,0,0,0,0,0,0,0 +0,0,0,0,0,0,0,0,0,0,0,0,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,0,0,0,0,0,0 +0,0,0,0,0,0,0,0,0,0,0,0,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,0,0,0,0,0,0 +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,0,0,0 +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,0,0,0 +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50 +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50 +50,50,50,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50 +50,50,50,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50 +50,50,50,50,50,50,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50 +50,50,50,50,50,50,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50 +50,50,50,50,50,50,50,50,50,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50 +50,50,50,50,50,50,50,50,50,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50 +50,50,50,50,50,50,50,50,50,50,50,50,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50 +50,50,50,50,50,50,50,50,50,50,50,50,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50 +50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50 +50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50 +50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,50,50,50,50,50,50,50,50,50,50,50,50 +50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,50,50,50,50,50,50,50,50,50,50,50,50 +50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,50,50,50,50,50,50,50,50,50 +50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,50,50,50,50,50,50,50,50,50 +50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,50,50,50,50,50,50 +50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,50,50,50,50,50,50 +50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,50,50,50 +50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,50,50,50 diff --git a/src/visualising/expression/tool.py b/src/visualising/expression/tool.py new file mode 100644 index 0000000..7f59fa5 --- /dev/null +++ b/src/visualising/expression/tool.py @@ -0,0 +1,149 @@ +#!/usr/bin/env python + +import re +import importlib.resources + +from visualising.communication.illustration.animation import Animation +from visualising.communication.illustration.ensemble import Ensemble +from visualising.communication.illustration.frame import Frame +from visualising.communication.illustration.pixel import Pixel +from visualising.expression import library + + +class Tool: + def __init__(self): + pass + + @staticmethod + def read_raw_animation(filename): + def read_raw_frame(line): + raw_frame = [] + for value in line.split(","): + raw_frame.append( + int(re.search(r'\d+', value).group()) + ) + return raw_frame + + file = importlib.resources.open_text(library, filename) + + raw_ensembles = [] + while True: + l_line = file.readline() + r_line = file.readline() + + if bool(l_line) != bool(r_line): + raise ValueError("Animation must have an even number of frames!") + if not l_line and not r_line: + break + + l_raw_frame = read_raw_frame(l_line) + r_raw_frame = read_raw_frame(r_line) + + raw_ensembles.append([l_raw_frame, r_raw_frame]) + + return raw_ensembles + + @staticmethod + def create_animation(filename, ensemble_time=0, num_iter=1): + def create_frame(raw_pixels): + if len(raw_pixels) != 48: + raise ValueError("Frame must have exactly 48 values!") + + pixels = [] + + i = 0 + while i < 48: + r = raw_pixels[i + 0] + g = raw_pixels[i + 1] + b = raw_pixels[i + 2] + + pixels.append(Pixel(r, g, b)) + i = i + 3 + + return Frame(pixels) + + raw_ensembles = Tool.read_raw_animation(filename) + + ensembles = [] + for raw_ensemble in raw_ensembles: + if len(raw_ensemble) != 2: + raise ValueError("Ensemble must have exactly two frames!") + + l_frame = create_frame(raw_ensemble[0]) + r_frame = create_frame(raw_ensemble[1]) + + ensembles.append(Ensemble(l_frame, r_frame)) + + return Animation(ensembles, ensemble_time, num_iter) + + @staticmethod + def merge_frames(l_frames, r_frames): + if len(l_frames) != len(r_frames): + raise ValueError("Merging different number of frames_library not allowed!") + + merged_frames = [] + for l_frame, r_frame in zip(l_frames, r_frames): + merged_frames = merged_frames + l_frame + merged_frames = merged_frames + r_frame + + return merged_frames + + @staticmethod + def concatenate_frames(start, end): + return start.extend(end) + + @staticmethod + def create_empty_ensemble(): + frame1 = [0] * 16 + frame2 = [0] * 16 + return [frame1, frame2] + + @staticmethod + def set_pixel_color(ensemble, pixel, r, g, b): + ensemble_len = len(ensemble) + + if ensemble_len != 2: + raise ValueError("Ensemble must contain exactly two frames_library!") + + for frame in ensemble: + num_values = len(frame) + + if num_values != 48: + raise ValueError("All frames_library must have exactly 48 values!") + + is_numeric = True + is_positive = True + is_small = True + + for value in frame: + is_numeric = is_numeric and isinstance(value, int) + is_positive = is_positive and not (value < 0) + is_small = is_small and value < 256 + + if not is_numeric: + raise TypeError("All frame values must be integers!") + if not is_positive: + raise ValueError("All frame values must be positive!") + if not is_small: + raise ValueError("All frame values must be smaller than 256!") + + if not isinstance(pixel, int): + raise TypeError("Pixel must be an integer!") + if not (0 < pixel < 33): + raise ValueError("Pixel must be in the range of 1 to 32!") + if not (isinstance(r, int) and isinstance(g, int) and isinstance(b, int)): + raise TypeError("Color values must be integers!") + if not ((0 < r < 256) and (0 < g < 256) and (0 < b < 256)): + raise ValueError("Color values must be in the range of 1 to 255!") + + if pixel < 16: + index = 0 + else: + index = 1 + pixel = pixel - 16 + + ensemble[index][pixel + 0] = r + ensemble[index][pixel + 1] = g + ensemble[index][pixel + 2] = b + + return ensemble diff --git a/src/visualising/message.py b/src/visualising/message.py deleted file mode 100755 index 06ad431..0000000 --- a/src/visualising/message.py +++ /dev/null @@ -1,50 +0,0 @@ -#!/usr/bin/env python - -class FrameMsg: - def __init__(self, frame): - self.frame = frame - - # Creates a 50-bit data Message. The first and last bit are intended for authentication - # as a data message. The remaining 48 bits store exactly one animation frame. - def create_msg(self): - msg = [bytes('{', 'ascii')] - - for color in self.frame: - msg.append(color.to_bytes(1, byteorder='big')) - - msg.append(bytes('}', 'ascii')) - - return msg - - -class InstrMsg: - def __init__(self, instr, animation=None): - self.instr = instr - self.animation = animation - - # Creates a 50-bit instruction message. Since only 7 bits at most are required for the - # actual message, most of the message consists only of zeros. The first and last bit - # are intended for authentication as an instruction message. - def create_msg(self): - msg = [bytes('[', 'ascii'), bytes(self.instr, 'ascii')] - - if self.animation is not None: - msg.append(self.animation.num_frames.to_bytes(1, byteorder='big')) - - frame_time_bytes = self.animation.frame_time.to_bytes(4, byteorder='big') - msg.append((frame_time_bytes[0]).to_bytes(1, byteorder='big')) - msg.append((frame_time_bytes[1]).to_bytes(1, byteorder='big')) - msg.append((frame_time_bytes[2]).to_bytes(1, byteorder='big')) - msg.append((frame_time_bytes[3]).to_bytes(1, byteorder='big')) - - msg.append(self.animation.num_iter.to_bytes(1, byteorder='big')) - - for _ in range(41): - msg.append((0).to_bytes(1, byteorder='big')) - else: - for _ in range(47): - msg.append((0).to_bytes(1, byteorder='big')) - - msg.append(bytes(']', 'ascii')) - - return msg -- GitLab