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

refactored codebase

parent 33a15e65
No related branches found
No related tags found
No related merge requests found
string component # Name of the component, whose
# critical level is monitored.
float32 critical # Critical level of the family.
\ No newline at end of file
State[] states # Array of states.
...@@ -2,29 +2,33 @@ ...@@ -2,29 +2,33 @@
import re import re
import importlib.resources import importlib.resources
from . import animations from . import animations
from visualising.exception import AnimationException from visualising.exception import VisualiseException
class Animation: class Animation:
source = "animation.py" # Name of the file that contains the Animation class definition.
def __init__(self, frames, frame_time, num_iter): def __init__(self, frames, frame_time, num_iter):
# Set frames, number of frames and number of ensembles. # Set frames, number of frames and number of ensembles.
try: try:
animation_len = len(frames) animation_len = len(frames)
except TypeError: except TypeError:
raise AnimationException("P: -- ERROR -- The animation is empty!") raise VisualiseException(source, "The animation is empty!")
if animation_len % 2 != 0: if animation_len % 2 != 0:
raise AnimationException("P: -- ERROR -- Animation must contain an even number of frames!") raise VisualiseException(source, "Animation must contain an even number of frames!")
else: else:
for frame in frames: for frame in frames:
try: try:
frame_len = len(frame) frame_len = len(frame)
except TypeError: except TypeError:
raise AnimationException("P: -- ERROR -- At least one frame is empty!") raise VisualiseException(source, "At least one frame is empty!")
if frame_len != 48: if frame_len != 48:
raise AnimationException("P: -- ERROR -- At least one frame doesn't have exactly 48 values!") raise VisualiseException(source, "At least one frame doesn't have exactly 48 values!")
else: else:
is_numeric = True is_numeric = True
is_positive = True is_positive = True
...@@ -36,11 +40,11 @@ class Animation: ...@@ -36,11 +40,11 @@ class Animation:
is_small = is_small and value < 256 is_small = is_small and value < 256
if not is_numeric: if not is_numeric:
raise AnimationException("P: -- ERROR -- Not all values in the animation are integers!") raise VisualiseException(source, "Not all values in the animation are integers!")
elif not is_positive: elif not is_positive:
raise AnimationException("P: -- ERROR -- Not all values in the animation are positive!") raise VisualiseException(source, "Not all values in the animation are positive!")
elif not is_small: elif not is_small:
raise AnimationException("P: -- ERROR -- One or more values in the animation are to large!") raise VisualiseException(source, "One or more values in the animation are to large!")
else: else:
self.frames = frames self.frames = frames
self.num_frames = animation_len self.num_frames = animation_len
...@@ -48,21 +52,21 @@ class Animation: ...@@ -48,21 +52,21 @@ class Animation:
# Set frame time. # Set frame time.
if not isinstance(frame_time, int): if not isinstance(frame_time, int):
raise AnimationException("P: -- ERROR -- Frame time must be an integer!") raise VisualiseException(source, "Frame time must be an integer!")
elif frame_time < 0: elif frame_time < 0:
raise AnimationException("P: -- ERROR -- Frame time must be positive!") raise VisualiseException(source, "Frame time must be positive!")
elif frame_time > 4294967295: elif frame_time > 4294967295:
raise AnimationException("P: -- ERROR -- Animation delay is to high!") raise VisualiseException(source, "Animation delay is to high!")
else: else:
self.frame_time = frame_time self.frame_time = frame_time
# Set number of iterations. # Set number of iterations.
if not isinstance(num_iter, int): if not isinstance(num_iter, int):
raise AnimationException("P: -- ERROR -- Number of iterations must be an integer!") raise VisualiseException(source, "Number of iterations must be an integer!")
elif num_iter > 255: elif num_iter > 255:
raise AnimationException("P: -- ERROR -- Animation has to many iterations!") raise VisualiseException(source, "Animation has to many iterations!")
elif num_iter <= 0: elif num_iter <= 0:
raise AnimationException("P: -- ERROR -- Animation must have at least one iteration!") raise VisualiseException(source, "Animation must have at least one iteration!")
else: else:
self.num_iter = num_iter self.num_iter = num_iter
...@@ -71,11 +75,11 @@ class Animation: ...@@ -71,11 +75,11 @@ class Animation:
try: try:
file = importlib.resources.open_text(animations, filename) file = importlib.resources.open_text(animations, filename)
except AttributeError: except AttributeError:
raise AnimationException("P: -- ERROR -- The file is incorrectly formatted!") raise VisualiseException(source, "The file is incorrectly formatted!")
except FileNotFoundError: except FileNotFoundError:
raise AnimationException("P: -- ERROR -- The specified file does not exist!") raise VisualiseException(source, "The specified file does not exist!")
except TypeError: except TypeError:
raise AnimationException("P: -- ERROR -- No animation file was specified!") raise VisualiseException(source, "No animation file was specified!")
animation = [] animation = []
for line in file: for line in file:
...@@ -99,19 +103,19 @@ class Animation: ...@@ -99,19 +103,19 @@ class Animation:
try: try:
ensemble_len = len(ensemble) ensemble_len = len(ensemble)
except TypeError: except TypeError:
raise AnimationException("P: -- ERROR -- Ensemble is empty!") raise VisualiseException(source, "Ensemble is empty!")
if ensemble_len != 2: if ensemble_len != 2:
raise AnimationException("P: -- ERROR -- Ensemble doesn't contain exactly two frames!") raise VisualiseException(source, "Ensemble doesn't contain exactly two frames!")
else: else:
for frame in ensemble: for frame in ensemble:
try: try:
frame_len = len(frame) frame_len = len(frame)
except TypeError: except TypeError:
raise AnimationException("P: -- ERROR -- At least one frame is empty!") raise VisualiseException(source, "At least one frame is empty!")
if frame_len != 48: if frame_len != 48:
raise AnimationException("P: -- ERROR -- At least one frame doesn't have exactly 48 values!") raise VisualiseException(source, "At least one frame doesn't have exactly 48 values!")
else: else:
is_numeric = True is_numeric = True
is_positive = True is_positive = True
...@@ -123,22 +127,22 @@ class Animation: ...@@ -123,22 +127,22 @@ class Animation:
is_small = is_small and value < 256 is_small = is_small and value < 256
if not is_numeric: if not is_numeric:
raise AnimationException("P: -- ERROR -- Not all values in the ensemble are integers!") raise VisualiseException(source, "Not all values in the ensemble are integers!")
elif not is_positive: elif not is_positive:
raise AnimationException("P: -- ERROR -- Not all values in the ensemble are positive!") raise VisualiseException(source, "Not all values in the ensemble are positive!")
elif not is_small: elif not is_small:
raise AnimationException("P: -- ERROR -- One or more values in the ensemble are to large!") raise VisualiseException(source, "One or more values in the ensemble are to large!")
else: else:
if not (0 < pixel < 33): if not (0 < pixel < 33):
raise AnimationException("P: -- ERROR -- Pixel has to be in the range of 1 to 32!") raise VisualiseException(source, "Pixel has to be in the range of 1 to 32!")
elif not (0 < r < 256): elif not (0 < r < 256):
raise AnimationException("P: -- ERROR -- Red channel value has to be in the range of 1 to " raise VisualiseException(source, "Red channel value has to be in the range of 1 "
"255!") "to 255!")
elif not (0 < g < 256): elif not (0 < g < 256):
raise AnimationException("P: -- ERROR -- Green channel value has to be in the range of 1 " raise VisualiseException(source, "Green channel value has to be in the range of 1 "
"to 255!") "to 255!")
elif not (0 < b < 256): elif not (0 < b < 256):
raise AnimationException("P: -- ERROR -- Blue channel value has to be in the range of 1 " raise VisualiseException(source, "Blue channel value has to be in the range of 1 "
"to 255!") "to 255!")
else: else:
if pixel < 16: if pixel < 16:
......
#!/usr/bin/env python #!/usr/bin/env python
import time import time
from visualising.message import FrameMsg, InstrMsg from visualising.message import FrameMsg, InstrMsg
from visualising.animation import Animation from visualising.animation import Animation
from visualising.exception import ArduinoException from visualising.exception import VisualiseException
class Arduino: class Arduino:
def __init__(self, connection):
self.connection = connection
# Translates a 8-bit message from Arduino and prints the translation. source = "arduino.py" # Name of the file that contains the Arduino class definition.
@staticmethod resend = 5 # Number of times a message is resend to the Arduino.
def print_response(msg):
arduino_msg = {
240: "A: -- ERROR -- Animation has to many frame ensembles!",
241: "A: -- ERROR -- No animation is loaded!",
242: "A: -- ERROR -- Instruction was not recognized!",
243: "A: -- ERROR -- No data message expected!",
244: "A: -- ERROR -- Message has wrong format!",
245: "A: -- ERROR -- Animation is playing!",
246: "A: -- ERROR -- Animation has no iteration!",
247: "A: -- ERROR -- Unequal number of frames for the left and right NeoPixel ring!",
248: "A: -- ERROR -- Animation has no frame ensemble!",
0: "A: No Response!",
15: "A: -- SUCCESS -- Waiting for frames to be send!",
31: "A: -- SUCCESS -- Animation playback has been started!",
47: "A: -- SUCCESS -- Frame successfully received!",
63: "A: -- SUCCESS -- Last frame successfully received!",
79: "A: -- SUCCESS -- Animation successfully played!",
95: "A: -- SUCCESS -- Animation playback has been paused!",
111: "A: -- SUCCESS -- Animation playback has been resumed",
}
try: def __init__(self, connection):
print(arduino_msg[int.from_bytes(msg, byteorder='big')]) self.connection = connection
except KeyError:
raise ArduinoException("P: -- ERROR -- Unknown response from Arduino!")
# Creates one instruction messages and multiple frame messages to load an animation # Creates one instruction messages and multiple frame messages to load an animation
# onto the Arduino. # onto the Arduino.
def load_animation(self, animation): def load_animation(self, animation):
if animation.num_ensembles > 16: if animation.num_ensembles > 16:
raise ArduinoException("P: -- ERROR -- Animation hast to many frames!") raise VisualiseException(source, "Animation hast to many frames!")
else: else:
self.connection.send_msg(InstrMsg('A', animation).create_arduino_msg()) try:
msg = self.connection.receive_msg() self.connection.confirm_msg(InstrMsg('A', animation).create_msg(), [b'\x0f'], resend)
except VisualiseException:
raise
Arduino.print_response(msg)
if bytes(msg) == bytes(b'\x0f'):
for frame in animation.frames: for frame in animation.frames:
self.connection.send_msg(FrameMsg(frame).create_arduino_msg()) try:
msg = self.connection.receive_msg() self.connection.confirm_msg(FrameMsg(frame).create_msg(), [b'\x2f', b'\x3f'], resend)
except VisualiseException:
d = 10 raise
while bytes(msg) != bytes(b'\x2f') and bytes(msg) != bytes(b'\x3f') and d > 0:
d = d - 1
Arduino.print_response(msg)
self.connection.send_msg(FrameMsg(frame).create_arduino_msg())
msg = self.connection.receive_msg()
if d > 0:
Arduino.print_response(msg)
else:
raise ArduinoException("P: -- ERROR -- Unable to send frame message!")
# Creates instruction message and sends it to the Arduino, # Creates instruction message and sends it to the Arduino,
# to play a loaded animation. # to play a loaded animation.
def start_playback(self): def start_playback(self):
self.connection.send_msg(InstrMsg('B').create_arduino_msg()) try:
Arduino.print_response(self.connection.receive_msg()) self.connection.confirm_msg(InstrMsg('B').create_msg(), [b'\x1f'], resend)
except VisualiseException:
raise
# Creates instruction message and sends it to the Arduino, # Creates instruction message and sends it to the Arduino,
# to pause a playing animation. # to pause a playing animation.
def pause_playback(self): def pause_playback(self):
self.connection.send_msg(InstrMsg('C').create_arduino_msg()) try:
Arduino.print_response(self.connection.receive_msg()) self.connection.confirm_msg(InstrMsg('C').create_msg(), [b'\x5f'], resend)
except VisualiseException:
raise
# Creates instruction message and sends it to the Arduino, # Creates instruction message and sends it to the Arduino,
# to resume playback of a loaded animation. # to resume playback of a loaded animation.
def resume_playback(self): def resume_playback(self):
self.connection.send_msg(InstrMsg('D').create_arduino_msg()) try:
Arduino.print_response(self.connection.receive_msg()) self.connection.confirm_msg(InstrMsg('D').create_msg(), [b'\x6f'], resend)
except VisualiseException:
raise
# Streams an animation to the Arduino. For this purpose, two frames of # Streams an animation to the Arduino. For this purpose, two frames of
# the animation are repeatedly sent to the Arduino. The Arduino plays # the animation are repeatedly sent to the Arduino. The Arduino plays
......
...@@ -3,33 +3,104 @@ ...@@ -3,33 +3,104 @@
import time import time
import serial import serial
from visualising.exception import ConnectionException from visualising.exception import VisualiseException
from serial.serialutil import SerialException from serial.serialutil import SerialException
class Connection: class Connection:
source = "connection.py" # Name of the file that contains the Connection class definition.
def __init__(self, port, baud): def __init__(self, port, baud):
self.connection = serial.Serial() self.connection = serial.Serial()
self.connection.port = port self.connection.port = port
self.connection.baudrate = baud self.connection.baudrate = baud
self.connection.timeout = 1 self.connection.timeout = 1
try: try:
self.connection.open() # Establishes a serial connection. self.connection.open() # Establishes a serial connection.
except SerialException: except SerialException:
raise ConnectionException("P: -- ERROR -- No serial connection established!") raise VisualiseException(source, "No serial connection established!")
except FileNotFoundError: except FileNotFoundError:
raise ConnectionException("P: -- ERROR -- The specified port does not exist!") raise VisualiseException(source, "The specified port does not exist!")
time.sleep(2) # Prevents errors regarding pyserial. time.sleep(2) # Prevents errors regarding pyserial.
# Send a message. # Send a message to the Arduino.
def send_msg(self, msg): def send_msg(self, msg):
for byte in msg: for byte in msg:
self.connection.write(byte) self.connection.write(byte)
self.connection.reset_output_buffer() # Clear serial output buffer. self.connection.reset_output_buffer() # Clear serial output buffer.
# Receive a message. # Receive a message from the Arduino.
def receive_msg(self): 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) 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. self.connection.reset_input_buffer() # Clear serial input buffer.
return msg 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, resend):
def evaluate():
bool = resend > 0
for element in expected_responses:
bool = bool and (bytes(element) != bytes(response))
return bool
if not isinstance(expected_responses, list):
raise VisualiseException(source, "Expected responses must be given as a list!")
elif not expected_responses:
raise VisualiseException(source, "There must be at least one expected response!")
elif not resend > 0:
raise VisualiseException(source, "Number of resends must be greater than zero!")
else:
self.connection.send_msg(msg)
response = self.connection.receive_msg()
condition = evaluate()
while condition:
resend = resend - 1
self.connection.send_msg(msg)
response = self.connection.receive_msg()
condition = evaluate()
if not resend > 0:
raise VisualiseException("Arduino", Connection.translate_msg(response))
...@@ -4,16 +4,7 @@ class Error(Exception): ...@@ -4,16 +4,7 @@ class Error(Exception):
pass pass
class AnimationException(Error): class VisualiseException(Error):
def __init__(self, message): def __init__(self, source, description):
self.message = message self.source = source
self.description = description
class ConnectionException(Error):
def __init__(self, message):
self.message = message
class ArduinoException(Error):
def __init__(self, message):
self.message = message
...@@ -6,7 +6,7 @@ class FrameMsg: ...@@ -6,7 +6,7 @@ class FrameMsg:
# Creates a 50-bit data Message. The first and last bit are intended for authentication # 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. # as a data message. The remaining 48 bits store exactly one animation frame.
def create_arduino_msg(self): def create_msg(self):
msg = [bytes('{', 'ascii')] msg = [bytes('{', 'ascii')]
for color in self.frame: for color in self.frame:
...@@ -25,7 +25,7 @@ class InstrMsg: ...@@ -25,7 +25,7 @@ class InstrMsg:
# Creates a 50-bit instruction message. Since only 7 bits at most are required for the # 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 # actual message, most of the message consists only of zeros. The first and last bit
# are intended for authentication as an instruction message. # are intended for authentication as an instruction message.
def create_arduino_msg(self): def create_msg(self):
msg = [bytes('[', 'ascii'), bytes(self.instr, 'ascii')] msg = [bytes('[', 'ascii'), bytes(self.instr, 'ascii')]
if self.animation is not None: if self.animation is not None:
......
#!/usr/bin/env python #!/usr/bin/env python
import rospy import rospy
from visualising.msg import State from monitoring.watchdog import Watchdog
from visualising.msg import States from visualising.exception import *
from visualising.arduino import Arduino from visualising.arduino import Arduino
from visualising.connection import Connection from visualising.connection import Connection
from visualising.animation import Animation from visualising.animation import Animation
from visualising.exception import *
class Visualiser: class Visualiser:
def __init__(self, port="/dev/ttyUSB0", baud=57600): def __init__(self):
port = rospy.get_param("/arduino_port", "/dev/ttyUSB0")
baud = rospy.get_param("/arduino_baud", 57600)
self.visualisation_strategy = rospy.get_param("/visualisation_strategy", 1)
self.arduino = Arduino(Connection(port, baud)) self.arduino = Arduino(Connection(port, baud))
self.robot_state = 0 # Robot starts in a happy state. self.watchdog = Watchdog()
self.happy = Animation(Animation.read_frames_from_file("animation_happy.txt"), 100, 1) def visualise(self):
self.ok = Animation(Animation.read_frames_from_file("animation_ok.txt"), 100, 1) if self.visualisation_strategy == 1:
self.sad = Animation(Animation.read_frames_from_file("animation_sad.txt"), 100, 1) self.mood_mode()
elif self.visualisation_strategy == 2:
self.info_mode()
else:
rospy.logwarn("Visualiser: The specified strategy is not recognised by the visualiser!")
rospy.Subscriber("states", States, self.evaluate) # TODO: Add all basic emotions!
def mood_mode(self):
happy = Animation(Animation.read_frames_from_file("animation_happy.txt"), 100, 1)
ok = Animation(Animation.read_frames_from_file("animation_ok.txt"), 100, 1)
sad = Animation(Animation.read_frames_from_file("animation_sad.txt"), 100, 1)
animation_duration = 1 while not rospy.is_shutdown():
self.timer = rospy.Timer(rospy.Duration(animation_duration), self.update_mood) 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)
# TODO: Add different modes!
def evaluate(self, event):
number = 0
value = 0
for state in event.states:
value = value + state.critical
number = number + 1
self.robot_state = value / number
# TODO: Add different modes! def info_mode(self):
def update_mood(self, event): print("Hallo!")
print(self.robot_state)
if self.robot_state > 0.7:
self.stream_animation(self.sad)
else:
if self.robot_state > 0.4:
self.stream_animation(self.ok)
else:
self.stream_animation(self.happy)
def stream_animation(self, animation): def stream_animation(self, animation):
self.arduino.stream_animation(animation) self.arduino.stream_animation(animation)
\ 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