Skip to content
Snippets Groups Projects
Commit 2c09a192 authored by bav6096's avatar bav6096
Browse files

added abstract expression

parent cd00d37b
No related branches found
No related tags found
No related merge requests found
Showing
with 143 additions and 138 deletions
......@@ -162,8 +162,8 @@ catkin_install_python(PROGRAMS
nodes/visualiser
scripts/play_animation.py
scripts/reset_display.py
scripts/build_emotion.py
scripts/build_state_cycle.py
scripts/emotion.py
scripts/abstract.py
scripts/write.py
DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}
)
......
#!/usr/bin/env python
import argparse
import time
from visualising.expression.tool import Tool
from visualising.expression.abstract import Abstract
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")
......@@ -14,13 +18,19 @@ args = vars(parser.parse_args())
port = args["port"]
baud = args["baud"]
time = args["time"]
delay = args["time"]
color = [10, 10, 10]
arduino = Arduino(Connection(port, baud))
states = [0.4, 1, 0.2, 0.3, 0.9, 0.7]
ensemble = Tool.build_state_cycle(states)
states = [0, 1.0, 0.10, 0.15, 0.20, 0.85, 0.30, 0.35, 0.40, 0.45, 0.50, 0.55, 0.60, 0.65,
0.70, 0.75, 0.80, 0.25, 0.90, 0.05, 0.95]
text = ["A", "CPU", "A", "A", "A", "ARM", "A", "A", "A", "A", "A", "A", "A", "A",
"A", "A", "A", "A", "RAM", "A", "CAMERA"]
ensemble = Abstract.cycle(states)
time.sleep(10)
arduino.stream_animation(Tool.highlight(ensemble, states, 4))
arduino.stream_animations(Abstract.highlight(ensemble, states, text, 4))
# arduino.stream_animation(Animation([ensemble], 50, 2))
File moved
......@@ -21,6 +21,6 @@ time = args["time"]
iter = args["iterations"]
arduino = Arduino(Connection(port, baud))
animation = Tool.create_animation(file, time, iter)
animation = Tool.create_animation(file, "alphabet", time, iter)
arduino.stream_animation(animation)
......@@ -90,3 +90,7 @@ class Arduino:
for _ in range(animation.num_iter):
for ensemble in animation.ensembles:
self.play_animation(Animation([ensemble], animation.ensemble_time, 1))
def stream_animations(self, animations):
for animation in animations:
self.stream_animation(animation)
......@@ -3,10 +3,7 @@
import time
import serial
from visualising.communication.channel.message.message import Message
from visualising.communication.channel.message.response import Response
from visualising.communication.channel.message.msg_frame import MsgFrame
from visualising.communication.channel.message.msg_instr import MsgInstr
class ArduinoException(Exception):
......@@ -27,9 +24,6 @@ class Connection:
# Send a message to the Arduino.
def send_msg(self, msg):
if not isinstance(msg, Message):
raise TypeError("The parameter msg must be an object of the Message class!")
for byte in msg.export_message():
self.connection.write(byte)
......@@ -54,12 +48,10 @@ class Connection:
# specified. In case any response wasn't even once one of the
# expected responses raise an exception.
def confirm_msg(self, msg, expectations, resends):
if not isinstance(msg, Message):
raise TypeError("The parameter msg must be an object of the Message class!")
if not isinstance(expectations, list) or not all(isinstance(expectation, Response) for expectation in
expectations):
raise TypeError("The parameter responses must be a list of objects of the Response class!")
if not isinstance(resends, int) or not resends > 0:
if not len(expectations) > 0:
raise ValueError("The parameter expectations must be a lister with objects of the class Response, "
"the length of which must be greater than 1!")
if not resends > 0:
raise ValueError("The parameter resends must be an integer greater 0!")
def evaluate():
......
#!/usr/bin/env python
from visualising.communication.channel.message.message import Message
from visualising.communication.illustration.frame import Frame
class MsgFrame(Message):
def __init__(self, frame):
self._frame = frame
@property
def frame(self):
return self._frame
@frame.setter
def frame(self, frame):
if not isinstance(frame, Frame):
raise TypeError("The parameter frame must be an object of the Frame class!")
self._frame = frame
self.frame = frame
# 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 export_message(self):
message = [bytes('{', 'ascii')]
for pixel in self._frame.pixels:
for pixel in self.frame.pixels:
r = pixel.color.r
g = pixel.color.g
b = pixel.color.b
......
#!/usr/bin/env python
from visualising.communication.channel.message.message import Message
from visualising.communication.illustration.animation import Animation
class MsgInstr(Message):
def __init__(self, instr, animation=None):
self._instr = instr
self._animation = animation
self.animation = animation
@property
def instr(self):
......@@ -19,33 +18,23 @@ class MsgInstr(Message):
raise ValueError("Unknown instruction!")
self._instr = instr
@property
def animation(self):
return self._animation
@animation.setter
def animation(self, animation):
if not isinstance(animation, Animation):
raise TypeError("The parameter animation must be an object of the Animation class!")
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 export_message(self):
message = [bytes('[', 'ascii'), bytes(self._instr, 'ascii')]
if self._animation is not None:
num_frames = len(self._animation.ensembles) * 2
if self.animation is not None:
num_frames = len(self.animation.ensembles) * 2
message.append(num_frames.to_bytes(1, byteorder='big'))
ensemble_time_bytes = self._animation.ensemble_time.to_bytes(4, byteorder='big')
ensemble_time_bytes = self.animation.ensemble_time.to_bytes(4, byteorder='big')
message.append((ensemble_time_bytes[0]).to_bytes(1, byteorder='big'))
message.append((ensemble_time_bytes[1]).to_bytes(1, byteorder='big'))
message.append((ensemble_time_bytes[2]).to_bytes(1, byteorder='big'))
message.append((ensemble_time_bytes[3]).to_bytes(1, byteorder='big'))
message.append(self._animation.num_iter.to_bytes(1, byteorder='big'))
message.append(self.animation.num_iter.to_bytes(1, byteorder='big'))
for _ in range(41):
message.append((0).to_bytes(1, byteorder='big'))
......
......@@ -4,17 +4,7 @@ class Response:
# 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._byte = byte
@property
def byte(self):
return self._byte
@byte.setter
def byte(self, byte):
if not isinstance(byte, bytes):
raise TypeError("The parameter byte must be an object of the byte class!")
self._byte = byte
self.byte = byte
def translate(self):
translation = {
......@@ -40,11 +30,9 @@ class Response:
}
try:
return translation[int.from_bytes(self._byte, byteorder='big')]
return translation[int.from_bytes(self.byte, byteorder='big')]
except KeyError:
return "Unknown response!"
def compare(self, response):
if not isinstance(response, Response):
raise TypeError("The parameter response must be an object of the Response class!")
return bytes(self._byte) == bytes(response.byte)
return bytes(self.byte) == bytes(response.byte)
#!/usr/bin/env python
from visualising.communication.illustration.ensemble import Ensemble
class Animation:
# The ensemble time is given in milliseconds.
def __init__(self, ensembles, ensemble_time=0, num_iter=1):
self._ensembles = ensembles
self.ensembles = ensembles
self._ensemble_time = ensemble_time
self._num_iter = num_iter
@property
def ensembles(self):
return self._ensembles
@ensembles.setter
def ensembles(self, ensembles):
if not isinstance(ensembles, Ensemble):
raise TypeError("The parameter ensembles must be an object of the Ensemble class!")
self._ensembles = ensembles
@property
def ensemble_time(self):
return self._ensemble_time
@ensemble_time.setter
def ensemble_time(self, ensemble_time):
if not isinstance(ensemble_time, int) or not 0 <= ensemble_time <= 4294967295:
if not 0 <= ensemble_time <= 4294967295:
raise ValueError("The parameter ensemble_time must be an integer between 0 an 4294967295!")
# The ensemble time is given in milliseconds.
self._ensemble_time = ensemble_time
@property
......@@ -36,6 +23,6 @@ class Animation:
@num_iter.setter
def num_iter(self, num_iter):
if not isinstance(num_iter, int) or not 1 <= num_iter <= 255:
if not 1 <= num_iter <= 255:
raise ValueError("The parameter num_iter must be an integer between 1 an 255!")
self._num_iter = num_iter
......@@ -15,7 +15,7 @@ class RGB(Color):
@r.setter
def r(self, r):
if not isinstance(r, int) or not 0 <= r <= 255:
if not 0 <= r <= 255:
raise ValueError("The parameter r must be an integer between 0 and 255!")
self._r = r
......@@ -25,7 +25,7 @@ class RGB(Color):
@g.setter
def g(self, g):
if not isinstance(g, int) or not 0 <= g <= 255:
if not 0 <= g <= 255:
raise ValueError("The parameter g must be an integer between 0 and 255!")
self._g = g
......@@ -35,7 +35,7 @@ class RGB(Color):
@b.setter
def b(self, b):
if not isinstance(b, int) or not 0 <= b <= 255:
if not 0 <= b <= 255:
raise ValueError("The parameter b must be an integer between 0 and 255!")
self._b = b
......
#!/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
@property
def l_frame(self):
return self._l_frame
@l_frame.setter
def l_frame(self, l_frame):
if not isinstance(l_frame, Frame):
raise TypeError("The parameter left must be an object of the Frame class!")
self._l_frame = l_frame
@property
def r_frame(self):
return self._r_frame
@r_frame.setter
def r_frame(self, r_frame):
if not isinstance(r_frame, Frame):
raise TypeError("The parameter right must be an object of the Frame class!")
self._r_frame = r_frame
self.l_frame = l_frame
self.r_frame = r_frame
def replace_pixel(self, index, pixel):
if not 0 <= index <= 31:
raise ValueError("The parameter index must be an integer between 0 and 31!")
if index < 16:
self.l_frame.replace_pixel(index, pixel)
else:
self.r_frame.replace_pixel(index - 16, pixel)
#!/usr/bin/env python
from visualising.communication.illustration.pixel import Pixel
from visualising.communication.illustration.color.color import Color
class Frame:
def __init__(self, pixels):
self._pixels = pixels
......@@ -14,18 +10,16 @@ class Frame:
@pixels.setter
def pixels(self, pixels):
if not isinstance(pixels, list) or not all(isinstance(pixel, Pixel) for pixel in pixels) or len(pixels) != 16:
if len(pixels) != 16:
raise ValueError("The parameter pixels must be a list of exactly 16 objects of the Pixel class!")
self._pixels = pixels
def illuminated(self, index):
if not isinstance(index, int) or not 0 <= index <= 15:
if not 0 <= index <= 15:
raise ValueError("The parameter index must be an integer between 0 and 15!")
return self._pixels[index].color.illuminated
def replace_pixel(self, index, pixel):
if not isinstance(index, int) or not 0 <= index <= 15:
if not 0 <= index <= 15:
raise ValueError("The parameter index must be an integer between 0 and 15!")
if not isinstance(pixel, Pixel):
raise TypeError("The parameter pixel must be an object of the Pixel class!")
self._pixels[index] = pixel
#!/usr/bin/env python
from visualising.communication.illustration.color.color import Color
class Pixel:
def __init__(self, color):
self._color = color
@property
def color(self):
return self._color
@color.setter
def color(self, color):
if not isinstance(color, Color):
raise TypeError("The parameter color must be an object of the Color class!")
self._color = color
self.color = color
......@@ -3,12 +3,93 @@ import rospy
import numpy as np
from visualising.expression.tool import Tool
from visualising.expression.expression import Expression
from visualising.communication.illustration.animation import Animation
from visualising.communication.illustration.color.rgb import RGB
class Abstract:
class Abstract(Expression):
def __init__(self, arduino):
self.arduino = arduino
self.playback_start = 0
def react(self, state):
if len(state[1]) > 16:
rospy.logwarn("Only displaying the first 16 metric aggregations!")
if len(state[1]) > 32:
rospy.logwarn("Only displaying the first 32 metric aggregations!")
state = state[:32]
@staticmethod
def cycle(state_vector):
ensemble = Tool.create_empty_ensemble()
index = 0
for state in state_vector:
color = Tool.state_to_color(state, RGB(0, 50, 0), RGB(50, 0, 0))
ensemble = Tool.set_pixel_color(ensemble, index, color)
index = index + 1
return ensemble
@staticmethod
def highlight(cycle, states, text, number):
number = min(len(states), number)
highlight = sorted(zip(states, range(len(states))), reverse=True)[:number]
cycle_animation = Animation([cycle], 5000, 1)
animations = [cycle_animation]
for tuple in highlight:
ensembles = []
highlight = Tool.create_empty_ensemble()
ring = ""
highlight_pixel = None
color = None
for i in range(32):
if tuple[1] != i:
highlight = Tool.set_pixel_color(highlight, i, RGB(0, 0, 0))
else:
highlight_pixel = i
if i < 16:
ring = "l"
color = cycle.l_frame.pixels[i].color
else:
ring = "r"
color = cycle.r_frame.pixels[i % 16].color
highlight = Tool.set_pixel_color(highlight, i, color)
ensembles.append(highlight)
ensembles = ensembles + Abstract.write(text[highlight_pixel], ring, highlight_pixel, color)
animations.append(Animation(ensembles, 1000, 1))
animations.append(cycle_animation)
animations.append(Animation([Tool.create_empty_ensemble()], 1000, 1))
return animations
@staticmethod
def write(text, ring, highlight=None, color=None):
if not isinstance(text, str) or not text.isalpha():
raise ValueError("The parameter text must be a string that only contains alphabetic characters!")
text = text.upper()
ensembles = []
for char in text:
if ring == "l":
file = "l_" + char + ".txt"
elif ring == "r":
file = "r_" + char + ".txt"
else:
raise ValueError("The parameter index must be a string that is either l or r!")
letter = Tool.create_ensembles(file, "alphabet")
if highlight is not None:
letter = [Tool.set_pixel_color(letter[0], highlight, color)]
ensembles = ensembles + letter
if highlight is not None:
ensembles.append(Tool.set_pixel_color(Tool.create_empty_ensemble(), highlight, color))
else:
ensembles.append(Tool.create_empty_ensemble())
return ensembles
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
10,10,10,10,10,10,10,10,10,10,10,10,10,0,0,10,10,10,10,10,10,0,0,0,0,0,0,0,0,0,10,10,10,10,10,10,10,0,0,10,10,10,10,10,10,10,10,10
10,10,10,10,10,10,10,10,10,10,10,10,10,0,0,10,10,10,10,10,10,0,0,0,0,0,0,0,0,0,0,0,0,10,10,10,10,10,10,10,0,0,10,10,10,10,10,10
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,0,0,0,0,0,0,10,10,10,10,10,10,10,10,10
\ No newline at end of file
10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,0,0,0,0,0,0,0,0,0,0,0,0,10,10,10
\ No newline at end of file
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment