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