diff --git a/libs/lib/xaal/lib/messages.py b/libs/lib/xaal/lib/messages.py index 6280144f566429b82a1a45e90aba5763ee3cfb83..c3e9cde0105b84bcee828014040cf5b04346f39b 100644 --- a/libs/lib/xaal/lib/messages.py +++ b/libs/lib/xaal/lib/messages.py @@ -18,28 +18,27 @@ # along with xAAL. If not, see <http://www.gnu.org/licenses/>. # -from xaal.lib.types import DeviceT -from . import tools -from . import config -from .bindings import UUID -from .exceptions import MessageError, MessageParserError -from . import cbor - -from enum import Enum -from tabulate import tabulate -import pprint import datetime -import pysodium +import pprint import struct import sys +from enum import Enum +import typing +from typing import Any, Optional + +import pysodium +from tabulate import tabulate -from typing import Any, Optional, TYPE_CHECKING +from . import cbor, config, tools +from .bindings import UUID +from .exceptions import MessageError, MessageParserError -if TYPE_CHECKING: +if typing.TYPE_CHECKING: from .devices import Device import logging + logger = logging.getLogger(__name__) ALIVE_ADDR = UUID("00000000-0000-0000-0000-000000000000") @@ -58,6 +57,99 @@ class MessageAction(Enum): GET_ATTRIBUTES = "get_attributes" GET_DESCRIPTION = "get_description" +class Message(object): + """Message object used for incomming & outgoint message""" + + __slots__ = ['version', 'timestamp', 'source', 'dev_type', 'msg_type', 'action', 'body', '__targets'] + + def __init__(self): + self.version = config.STACK_VERSION # message API version + self.__targets = [] # target property + self.timestamp = None # message timestamp + self.source = None # message source + self.dev_type = None # message dev_type + self.msg_type = None # message type + self.action = None # message action + self.body = {} # message body + + @property + def targets(self) -> list: + return self.__targets + + @targets.setter + def targets(self, values: list): + if not isinstance(values, list): + raise MessageError("Expected a list for targetsList, got %s" % (type(values),)) + for uid in values: + if not tools.is_valid_address(uid): + raise MessageError("Bad target addr: %s" % uid) + self.__targets = values + + def targets_as_string(self) -> list: + return [str(k) for k in self.targets] + + def dump(self): + r = [] + r.append(["version", self.version]) + r.append(["targets", str(self.targets)]) + r.append(["timestamp", str(self.timestamp)]) + r.append(["source", self.source]) + r.append(["dev_type", self.dev_type]) + r.append(["msg_type", MessageType(self.msg_type)]) + r.append(["action", self.action]) + if self.body: + tmp="" + for k,v in self.body.items(): + k = k + ':' + v = pprint.pformat(v,width=55) + tmp = tmp+"- %-12s %s\n" % (k,v) + #tmp = tmp.strip() + r.append(["body", tmp]) + print(tabulate(r, headers=["Fied", "Value"], tablefmt="psql")) + + def __repr__(self) -> str: + return f"<xaal.Message {id(self):x} {self.source} {self.dev_type} {self.msg_type} {self.action}>" + + def is_request(self) -> bool: + if MessageType(self.msg_type) == MessageType.REQUEST: + return True + return False + + def is_reply(self) -> bool: + if MessageType(self.msg_type) == MessageType.REPLY: + return True + return False + + def is_notify(self) -> bool: + if MessageType(self.msg_type) == MessageType.NOTIFY: + return True + return False + + def is_alive(self) -> bool: + if self.is_notify() and self.action == MessageAction.ALIVE.value: + return True + return False + + def is_request_isalive(self) -> bool: + if self.is_request() and self.action == MessageAction.IS_ALIVE.value: + return True + return False + + def is_attributes_change(self) -> bool: + if self.is_notify() and self.action == MessageAction.ATTRIBUTES_CHANGE.value: + return True + return False + + def is_get_attribute_reply(self) -> bool: + if self.is_reply() and self.action == MessageAction.GET_ATTRIBUTES.value: + return True + return False + + def is_get_description_reply(self) -> bool: + if self.is_reply() and self.action == MessageAction.GET_DESCRIPTION.value: + return True + return False + class MessageFactory(object): """Message Factory: @@ -66,9 +158,10 @@ class MessageFactory(object): - Serialize/Deserialize data in CBOR""" def __init__(self, cipher_key): - self.cipher_key = cipher_key # key encode / decode message built from passphrase + # key encode / decode message built from passphrase + self.cipher_key = cipher_key - def encode_msg(self, msg: 'Message'): + def encode_msg(self, msg: Message): """Apply security layer and return encode MSG in CBOR :param msg: xAAL msg instance :type msg: Message @@ -103,7 +196,7 @@ class MessageFactory(object): pkt = cbor.dumps(result) return pkt - def decode_msg(self, data: bytes, filter_func: Any =None) -> Optional['Message']: + def decode_msg(self, data: bytes, filter_func: Any = None) -> Optional[Message]: """Decode incoming CBOR data and De-Ciphering :param data: data received from the multicast bus :type data: cbor @@ -183,7 +276,7 @@ class MessageFactory(object): ##################################################### def build_msg( self, - dev: Optional[Device] = None, + dev: Optional['Device'] = None, targets: list = [], msg_type: Optional[MessageType] = None, action: Optional[str] = None, @@ -215,7 +308,7 @@ class MessageFactory(object): data = self.encode_msg(message) return data - def build_alive_for(self, dev: Device, timeout: int =0) -> bytes: + def build_alive_for(self, dev: 'Device', timeout: int =0) -> bytes: """Build Alive message for a given device timeout = 0 is the minimum value """ @@ -224,7 +317,7 @@ class MessageFactory(object): message = self.build_msg(dev=dev, targets=[], msg_type=MessageType.NOTIFY, action=MessageAction.ALIVE.value, body=body) return message - def build_error_msg(self, dev: Device, errcode:int , description: Optional[str] =None): + def build_error_msg(self, dev: 'Device', errcode:int , description: Optional[str] =None): """Build a Error message""" message = Message() body = {} @@ -236,100 +329,6 @@ class MessageFactory(object): -class Message(object): - """Message object used for incomming & outgoint message""" - - __slots__ = ['version', 'timestamp', 'source', 'dev_type', 'msg_type', 'action', 'body', '__targets'] - - def __init__(self): - self.version = config.STACK_VERSION # message API version - self.__targets = [] # target property - self.timestamp = None # message timestamp - self.source = None # message source - self.dev_type = None # message dev_type - self.msg_type = None # message type - self.action = None # message action - self.body = {} # message body - - @property - def targets(self) -> list: - return self.__targets - - @targets.setter - def targets(self, values: list): - if not isinstance(values, list): - raise MessageError("Expected a list for targetsList, got %s" % (type(values),)) - for uid in values: - if not tools.is_valid_address(uid): - raise MessageError("Bad target addr: %s" % uid) - self.__targets = values - - def targets_as_string(self) -> list: - return [str(k) for k in self.targets] - - def dump(self): - r = [] - r.append(["version", self.version]) - r.append(["targets", str(self.targets)]) - r.append(["timestamp", str(self.timestamp)]) - r.append(["source", self.source]) - r.append(["dev_type", self.dev_type]) - r.append(["msg_type", MessageType(self.msg_type)]) - r.append(["action", self.action]) - if self.body: - tmp="" - for k,v in self.body.items(): - k = k + ':' - v = pprint.pformat(v,width=55) - tmp = tmp+"- %-12s %s\n" % (k,v) - #tmp = tmp.strip() - r.append(["body", tmp]) - print(tabulate(r, headers=["Fied", "Value"], tablefmt="psql")) - - def __repr__(self) -> str: - return f"<xaal.Message {id(self):x} {self.source} {self.dev_type} {self.msg_type} {self.action}>" - - def is_request(self) -> bool: - if MessageType(self.msg_type) == MessageType.REQUEST: - return True - return False - - def is_reply(self) -> bool: - if MessageType(self.msg_type) == MessageType.REPLY: - return True - return False - - def is_notify(self) -> bool: - if MessageType(self.msg_type) == MessageType.NOTIFY: - return True - return False - - def is_alive(self) -> bool: - if self.is_notify() and self.action == MessageAction.ALIVE.value: - return True - return False - - def is_request_isalive(self) -> bool: - if self.is_request() and self.action == MessageAction.IS_ALIVE.value: - return True - return False - - def is_attributes_change(self) -> bool: - if self.is_notify() and self.action == MessageAction.ATTRIBUTES_CHANGE.value: - return True - return False - - def is_get_attribute_reply(self) -> bool: - if self.is_reply() and self.action == MessageAction.GET_ATTRIBUTES.value: - return True - return False - - def is_get_description_reply(self) -> bool: - if self.is_reply() and self.action == MessageAction.GET_DESCRIPTION.value: - return True - return False - - def build_nonce(data: tuple) -> bytes: """ Big-Endian, time in seconds and time in microseconds """ nonce = struct.pack('>QL', data[0], data[1])