One step to aligning with C++

This commit is contained in:
Pascal Serrarens 2025-03-09 15:00:34 +01:00
parent 9b632e5a39
commit dfbfc0818b
22 changed files with 504 additions and 456 deletions

4
.gitignore vendored
View File

@ -1,6 +1,4 @@
__pycache__/*
test/__pycache__/*
.vscode/* .vscode/*
.DS_Store .DS_Store
DoxyGen/DoxyWarnLogfile.txt DoxyGen/DoxyWarnLogfile.txt
**/__pycache__ **/__pycache__/*

203
LocalParticipant.py Normal file
View File

@ -0,0 +1,203 @@
import socket
import threading
from Participant import Participant
from Thing import Thing
from Messages.ParticipantMsg import ParticipantMsg
from Messages.NetworkIdMsg import NetworkIdMsg
from Messages.ThingMsg import ThingMsg
from Messages.NameMsg import NameMsg
from Messages.ModelUrlMsg import ModelUrlMsg
from Messages import *
import sys
micropython = 'micropython' in sys.modules
if micropython:
from MicroPython.uPythonParticipant import Bla
class LocalParticipant(Participant):
"""! A local participant is the local device which can communicate with other participants.
It manages all local things and communcation with other participants. Each application has a local participant which is usually explicit in the code.
An participant can be isolated. In that case it is standalong and does not communicate with other participants.
Currently, only UDP communication is supported
"""
publishInterval = 3000 # 3 seconds
buffer = None
## The network ID of the participant
network_id = None
nextPublishMe = 0
others = None
thread = None
name = "Participant"
def __init__(self, port=7681, ip_address = "0.0.0.0", remote=False, udp_socket = None):
super().__init__(ip_address, port)
self.ip_address = ip_address
self.port = port
self.others = []
self.network_id = 0
self.buffer = bytearray(256)
self.thing_msg_processors = {}
self.new_thing_handlers = []
if remote == False:
self.udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
self.udp_socket.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
self.udp_socket.bind(("0.0.0.0", port))
self.AddParticipant(self.ip_address, self.port)
self.thread = threading.Thread(target = self.Receiver)
self.thread.daemon = True
self.thread.start()
else:
self.udp_socket = udp_socket
#region Update
def GetParticipant(self, ip_address, port):
# print(f'{self.name} Get participant {ip_address} {port}')
# for item in self.others:
# print(f'- {item.ip_address} {item.port}')
found_participants = (item for item in self.others
if item.ip_address == ip_address and item.port == port)
participant = next(found_participants, None)
return participant
def AddParticipant(self, ip_address, port):
# print(f'{self.name} Add participant {ip_address} {port}')
remote_participant = LocalParticipant(ip_address, port, remote=True, udp_socket=self.udp_socket)
remote_participant.network_id = len(self.others)
self.others.append(remote_participant)
return remote_participant
def Update(self, currentTimeMs = None):
if currentTimeMs is None:
currentTimeMs = time.time() * 1000
if self.publishInterval > 0 and currentTimeMs > self.nextPublishMe:
self.Publish(ParticipantMsg(self.network_id))
print(f'Publish ClientMsg {self.network_id}')
self.nextPublishMe = currentTimeMs + self.publishInterval
super().Update(currentTimeMs)
#endregion
#region Send
def SendThingInfo(self, thing):
self.Send(ThingMsg(self.network_id, thing))
self.Send(NameMsg(self.network_id, thing))
self.Send(ModelUrlMsg(self.network_id, thing))
def Send(self, msg):
buffer_size = msg.Serialize([self.buffer])
if buffer_size <= 0:
return True
# print(f'{self.name} send {self.buffer[0]} to {self.ip_address} {self.port}')
self.udp_socket.sendto(self.buffer[:buffer_size], (self.ip_address, self.port))
return True
def Publish(self, msg):
buffer_size = msg.Serialize([self.buffer])
if buffer_size <= 0:
return True
# print(f'publish {self.buffer[0]} to {self.port}')
self.udp_socket.sendto(self.buffer[:buffer_size], ('<broadcast>', self.port))
return True
#endregion
#region Receive
def Receiver(self):
while True:
data, addr = self.udp_socket.recvfrom(1024)
remote_ip_address = addr[0]
remote_port = addr[1]
# print(f'msg received from {remote_ip_address}:{remote_port}')
remote_participant = self.GetParticipant(remote_ip_address, remote_port)
if remote_participant is None:
# print(f'new participant')
remote_participant = self.AddParticipant(remote_ip_address, remote_port)
self.ReceiveData(data, remote_participant)
def ReceiveData(self, data, remote_participant):
msgId = data[0]
# print(f'msg {msgId} ')
match msgId:
case ParticipantMsg.id:
self.ProcessClientMsg(remote_participant, ParticipantMsg(data))
case NetworkIdMsg.id:
self.ProcessNetworkIdMsg(remote_participant, NetworkIdMsg(data))
# case InvestigateMsg.id:
# self.ProcessInvestigateMsg(InvestigateMsg(data))
case ThingMsg.id:
self.ProcessThingMsg(ThingMsg(data))
case NameMsg.id:
self.ProcessNameMsg(NameMsg(data))
case ModelUrlMsg.id:
self.ProcessModelUrlMsg(ModelUrlMsg(data))
# case Messages.BinaryMsg.id:
# msg = Messages.BinaryMsg(data)
# msg.thing.ProcessBinary(msg.data)
def ProcessClientMsg(self, sender, msg: ParticipantMsg):
pass
def ProcessNetworkIdMsg(self, sender, msg: NetworkIdMsg):
print(f'{self.name} receive network id {self.network_id} -> {msg.network_id}')
if self.network_id != msg.network_id:
self.network_id = msg.network_id
# print(f'sending all things {len(Thing.allThings)}')
for thing in self.things:
sender.SendThingInfo(thing)
# self.Send(NameMsg(self.network_id, thing))
def ProcessInvestigateMsg(self, data: bytearray):
pass
def ProcessThingMsg(self, msg: ThingMsg):
print(f'received thing {msg.network_id} {msg.thing_id}')
if msg.thing_type in self.thing_msg_processors:
constructor = self.thing_msg_processors[msg.thing_type]
constructor(msg.network_id, msg.thing_id)
# not really 'new' thing, but it is a start
thing = Thing.Get(msg.network_id, msg.thing_id)
if thing is not None:
for handler in self.new_thing_handlers:
handler(thing)
def ProcessNameMsg(self, msg: NameMsg):
print(f'received name {msg.name}')
def ProcessModelUrlMsg(self, msg: ModelUrlMsg):
print(f'received model url: {msg.url}')
def ProcessBinary(self, msg: BinaryMsg):
print('received binary data')
if msg.thing != None:
msg.thing.ProcessBinary(msg.data)
def Register(self, constructor, thing_type):
self.thing_msg_processors[thing_type] = constructor
def OnNewThing(self, event_handler):
self.new_thing_handlers.append(event_handler)
def OnNewThingType(self, thing_type, event_handler):
def ConditionalHandler(thing):
if thing.type == thing_type:
event_handler(thing)
self.new_thing_handlers.append(ConditionalHandler)
#endregion

View File

@ -1,164 +0,0 @@
import Messages.LowLevelMessages as LowLevelMessages
from Thing import Thing
class IMessage:
id = 0x00
## Serialize the message into the given buffer
#
## @returns: the length of the message
def Serialize(buffer):
return 0
def SendTo(self, participant):
buffer_size = self.Serialize([participant.buffer])
if buffer_size == 0:
return False
return participant.SendBuffer(buffer_size)
def Publish(self, participant):
bufferSize = self.Serialize([participant.buffer])
if bufferSize == 0:
return False
return participant.PublishBuffer(bufferSize)
class ParticipantMsg(IMessage):
"""! A participant messages notifies other participants of its presence.
When received by another participant, it can be followed by a NetworkIdMsg
to announce that participant to this client such that it can join privately
"""
## The message ID
id: int = 0xA0
## The length of the message
length: int = 2
## The network ID known by the participant.
network_id: int = None
def __init__(self, network_id: int):
"""! Create a new message for sending
@param network_id The network ID known by the participant. Use 0 if it is unknown
"""
if isinstance(network_id, int):
self.network_id = network_id
elif isinstance(network_id, bytes):
self.network_id = network_id[1]
def Serialize(self, buffer_ref):
"""! Serialize the message into a byte array.
@param buffer_ref The buffer to serialize into
@return The length of the message in the buffer
"""
if buffer_ref is None or self.network_id is None:
return 0
buffer: bytearray = buffer_ref[0]
buffer[0:ParticipantMsg.length] = [
ParticipantMsg.id,
self.network_id
]
return ParticipantMsg.length
## A network id message invites another participant to a site
#
## This can be sent in response to a ClientMsg
class NetworkIdMsg(IMessage):
id = 0xA1
length = 2
network_id = None
## Create a network id message
#
# @param network_id The network id assigned to the remote participant.
def __init__(self, network_id):
self.network_id = None
if isinstance(network_id, int):
self.network_id = network_id
elif isinstance(network_id, bytes):
self.network_id = network_id[1]
## Serialize the message into the given buffer
#
## @param buffer_ref A reference to the buffer to use. This should be a list with the buffer as its first and only element
## @returns the length of the message
def Serialize(self, buffer_ref):
if self.network_id is None:
return 0
buffer: bytearray = buffer_ref[0]
buffer[0:NetworkIdMsg.length] = [
NetworkIdMsg.id,
self.network_id
]
return NetworkIdMsg.length
class InvestigateMsg():
id = 0x81
length = 3
def __init__(self, buffer):
self.network_id = buffer[1]
self.thing_id = buffer[2]
class PoseMsg(IMessage):
"""! Message to communicate the pose of the thing
The pose is in local space relative to the parent.
If there is not parent (the thing is a root thing), the pose will be in world space.
"""
## The message ID
id = 0x10
## The length of the message
length = 4
def __init__(self, network_id, thing):
self.network_id = network_id
self.thing = thing
def Serialize(self, buffer_ref):
if (self.network_id is None) or (self.thing is None):
return 0
buffer: bytearray = buffer_ref[0]
buffer[0:PoseMsg.length] = [
PoseMsg.id,
self.network_id,
self.thing.id,
self.thing.pose_updated
]
ix = [4]
if self.thing.pose_updated & Thing.Position:
Messages.LowLevelMessages.SendSpherical(buffer, ix, self.thing.position)
if self.thing.pose_updated & Thing.Orientation:
Messages.LowLevelMessages.SendQuat32(buffer, ix, self.thing.orientation)
if self.thing.pose_updated & Thing.LinearVelocity:
Messages.LowLevelMessages.SendSpherical(buffer, ix, self.thing.linearVelocity)
if self.thing.pose_updated & Thing.AngularVelocity:
Messages.LowLevelMessages.SendSpherical(buffer, ix, self.thing.angularVelocity)
return ix[0]
class BinaryMsg():
id = 0xB1
def __init__(self, buffer):
self.network_id = buffer[1]
self.thing_id = buffer[2]
self.thing: Thing = Thing.Get(self.network_id, self.thing_id)
self.data = buffer[3:]
def SendTo(participant, thing, data: bytearray):
length = 3
if thing.network_id is None or thing is None or data is None:
return False
participant.buffer[0:length] = [
BinaryMsg.id,
participant.network_id,
thing.id
]
full_length = length + len(data)
participant.buffer[length:full_length] = data
participant.SendBuffer(full_length)
return True

164
Messages/Messages.py Normal file
View File

@ -0,0 +1,164 @@
import Messages.LowLevelMessages as LowLevelMessages
from Thing import Thing
class IMessage:
id = 0x00
## Serialize the message into the given buffer
#
## @returns: the length of the message
def Serialize(buffer):
return 0
def SendTo(self, participant):
buffer_size = self.Serialize([participant.buffer])
if buffer_size == 0:
return False
return participant.SendBuffer(buffer_size)
def Publish(self, participant):
bufferSize = self.Serialize([participant.buffer])
if bufferSize == 0:
return False
return participant.PublishBuffer(bufferSize)
# class ParticipantMsg(IMessage):
# """! A participant messages notifies other participants of its presence.
# When received by another participant, it can be followed by a NetworkIdMsg
# to announce that participant to this client such that it can join privately
# """
# ## The message ID
# id: int = 0xA0
# ## The length of the message
# length: int = 2
# ## The network ID known by the participant.
# network_id: int = None
# def __init__(self, network_id: int):
# """! Create a new message for sending
# @param network_id The network ID known by the participant. Use 0 if it is unknown
# """
# if isinstance(network_id, int):
# self.network_id = network_id
# elif isinstance(network_id, bytes):
# self.network_id = network_id[1]
# def Serialize(self, buffer_ref):
# """! Serialize the message into a byte array.
# @param buffer_ref The buffer to serialize into
# @return The length of the message in the buffer
# """
# if buffer_ref is None or self.network_id is None:
# return 0
# buffer: bytearray = buffer_ref[0]
# buffer[0:ParticipantMsg.length] = [
# ParticipantMsg.id,
# self.network_id
# ]
# return ParticipantMsg.length
# ## A network id message invites another participant to a site
# #
# ## This can be sent in response to a ClientMsg
# class NetworkIdMsg(IMessage):
# id = 0xA1
# length = 2
# network_id = None
# ## Create a network id message
# #
# # @param network_id The network id assigned to the remote participant.
# def __init__(self, network_id):
# self.network_id = None
# if isinstance(network_id, int):
# self.network_id = network_id
# elif isinstance(network_id, bytes):
# self.network_id = network_id[1]
# ## Serialize the message into the given buffer
# #
# ## @param buffer_ref A reference to the buffer to use. This should be a list with the buffer as its first and only element
# ## @returns the length of the message
# def Serialize(self, buffer_ref):
# if self.network_id is None:
# return 0
# buffer: bytearray = buffer_ref[0]
# buffer[0:NetworkIdMsg.length] = [
# NetworkIdMsg.id,
# self.network_id
# ]
# return NetworkIdMsg.length
class InvestigateMsg():
id = 0x81
length = 3
def __init__(self, buffer):
self.network_id = buffer[1]
self.thing_id = buffer[2]
class PoseMsg(IMessage):
"""! Message to communicate the pose of the thing
The pose is in local space relative to the parent.
If there is not parent (the thing is a root thing), the pose will be in world space.
"""
## The message ID
id = 0x10
## The length of the message
length = 4
def __init__(self, network_id, thing):
self.network_id = network_id
self.thing = thing
def Serialize(self, buffer_ref):
if (self.network_id is None) or (self.thing is None):
return 0
buffer: bytearray = buffer_ref[0]
buffer[0:PoseMsg.length] = [
PoseMsg.id,
self.network_id,
self.thing.id,
self.thing.pose_updated
]
ix = [4]
if self.thing.pose_updated & Thing.Position:
LowLevelMessages.SendSpherical(buffer, ix, self.thing.position)
if self.thing.pose_updated & Thing.Orientation:
LowLevelMessages.SendQuat32(buffer, ix, self.thing.orientation)
if self.thing.pose_updated & Thing.LinearVelocity:
LowLevelMessages.SendSpherical(buffer, ix, self.thing.linearVelocity)
if self.thing.pose_updated & Thing.AngularVelocity:
LowLevelMessages.SendSpherical(buffer, ix, self.thing.angularVelocity)
return ix[0]
class BinaryMsg():
id = 0xB1
def __init__(self, buffer):
self.network_id = buffer[1]
self.thing_id = buffer[2]
self.thing = Thing.Get(self.network_id, self.thing_id)
self.data = buffer[3:]
def SendTo(participant, thing, data: bytearray):
length = 3
if thing.network_id is None or thing is None or data is None:
return False
participant.buffer[0:length] = [
BinaryMsg.id,
participant.network_id,
thing.id
]
full_length = length + len(data)
participant.buffer[length:full_length] = data
participant.SendBuffer(full_length)
return True

View File

@ -1,13 +1,36 @@
# from Messages.Messages import IMessage from Messages.Messages import IMessage
## A network id message invites another participant to a site
#
## This can be sent in response to a ClientMsg
class NetworkIdMsg(IMessage):
id = 0xA1
length = 2
# def SendTo(participant, network_id): network_id = None
# if network_id is None:
# return 0 ## Create a network id message
#
# @param network_id The network id assigned to the remote participant.
def __init__(self, network_id):
self.network_id = None
if isinstance(network_id, int):
self.network_id = network_id
elif isinstance(network_id, bytes):
self.network_id = network_id[1]
## Serialize the message into the given buffer
#
## @param buffer_ref A reference to the buffer to use. This should be a list with the buffer as its first and only element
## @returns the length of the message
def Serialize(self, buffer_ref):
if self.network_id is None:
return 0
# participant.buffer[0:2] = [ buffer: bytearray = buffer_ref[0]
# NetworkIdMsg.id, buffer[0:NetworkIdMsg.length] = [
# network_id NetworkIdMsg.id,
# ] self.network_id
# return NetworkIdMsg.length ]
return NetworkIdMsg.length

View File

@ -1,72 +1,72 @@
# from Messages.Messages import IMessage from Messages.Messages import IMessage
# class ParticipantMsg(IMessage): class ParticipantMsg(IMessage):
# """! A participant messages notifies other participants of its presence. """! A participant messages notifies other participants of its presence.
# When received by another participant, it can be followed by a NetworkIdMsg When received by another participant, it can be followed by a NetworkIdMsg
# to announce that participant to this client such that it can join privately to announce that participant to this client such that it can join privately
# """ """
# ## The message ID ## The message ID
# id: int = 0xA0 id: int = 0xA0
# ## The length of the message ## The length of the message
# length: int = 2 length: int = 2
# ## The network ID known by the participant. ## The network ID known by the participant.
# network_id: int = None network_id: int = None
# def __init__(self, network_id: int): def __init__(self, network_id: int):
# """! Create a new message for sending """! Create a new message for sending
# @param network_id The network ID known by the participant. Use 0 if it is unknown @param network_id The network ID known by the participant. Use 0 if it is unknown
# """ """
# if isinstance(network_id, int): if isinstance(network_id, int):
# self.network_id = network_id self.network_id = network_id
# elif isinstance(network_id, bytes): elif isinstance(network_id, bytes):
# self.network_id = network_id[1] self.network_id = network_id[1]
# def Serialize(self, buffer_ref): def Serialize(self, buffer_ref):
# """! Serialize the message into a byte array. """! Serialize the message into a byte array.
# @param buffer_ref The buffer to serialize into @param buffer_ref The buffer to serialize into
# @return The length of the message in the buffer @return The length of the message in the buffer
# """ """
# if buffer_ref is None or self.network_id is None: if buffer_ref is None or self.network_id is None:
# return 0 return 0
# buffer: bytearray = buffer_ref[0] buffer: bytearray = buffer_ref[0]
# buffer[0:ParticipantMsg.length] = [ buffer[0:ParticipantMsg.length] = [
# ParticipantMsg.id, ParticipantMsg.id,
# self.network_id self.network_id
# ] ]
# return ParticipantMsg.length return ParticipantMsg.length
# @staticmethod @staticmethod
# def Serialized(buffer_ref, network_id): def Serialized(buffer_ref, network_id):
# if buffer_ref is None or network_id is None: if buffer_ref is None or network_id is None:
# return 0 return 0
# buffer: bytearray = buffer_ref[0] buffer: bytearray = buffer_ref[0]
# buffer[0:ClientMsg.length] = [ buffer[0:ParticipantMsg.length] = [
# ClientMsg.id, ParticipantMsg.id,
# network_id network_id
# ] ]
# return ClientMsg.length return ParticipantMsg.length
## Send the network_id to the participant # Send the network_id to the participant
# def SendTo(participant, network_id): def SendTo(participant, network_id):
# if participant is None or network_id is None: if participant is None or network_id is None:
# return False return False
# participant.buffer[0:2] = [ participant.buffer[0:2] = [
# ClientMsg.id, ParticipantMsg.id,
# network_id network_id
# ] ]
# participant.SendBuffer(ClientMsg.length) participant.SendBuffer(ParticipantMsg.length)
## Publish the network_id # Publish the network_id
# def Publish(participant, network_id): def Publish(participant, network_id):
# if participant is None or network_id is None: if participant is None or network_id is None:
# return False return False
# participant.buffer[0:2] = [ participant.buffer[0:2] = [
# ClientMsg.id, ParticipantMsg.id,
# network_id network_id
# ] ]
# participant.PublishBuffer(ClientMsg.length) participant.PublishBuffer(ParticipantMsg.length)

View File

@ -1,198 +1,22 @@
import socket
import threading
from Messages.ParticipantMsg import ParticipantMsg
from Messages.NetworkIdMsg import NetworkIdMsg
from Messages.ThingMsg import ThingMsg
from Messages.NameMsg import NameMsg
from Messages.ModelUrlMsg import ModelUrlMsg
from Messages import *
from Thing import Thing
import sys
micropython = 'micropython' in sys.modules
if micropython:
from MicroPython.uPythonParticipant import Bla
class Participant: class Participant:
"""! A participant is used for communcation between things. def __init__(self, ip_address, port):
self.ip_addres = ip_address
Currently, only UDP communication is supported
"""
publishInterval = 3000 # 3 seconds
buffer = None
## The network ID of the participant
network_id = None
nextPublishMe = 0
others = None
thread = None
name = "Participant"
def __init__(self, ipAddress = "0.0.0.0", port=7681, remote=False, udp_socket =None):
self.ip_address = ipAddress
self.port = port self.port = port
self.others = [] self.things = set({ None })
self.network_id = 0
self.buffer = bytearray(256)
self.thing_msg_processors = {}
self.new_thing_handlers = []
if remote == False: def Add(self, thing):
self.udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) thing.id = len(self.things)
self.udp_socket.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1) self.things.add(thing)
self.udp_socket.bind(("0.0.0.0", port))
self.AddParticipant(self.ip_address, self.port)
self.thread = threading.Thread(target = self.Receiver)
self.thread.daemon = True
self.thread.start()
else:
self.udp_socket = udp_socket
#region Update
def GetParticipant(self, ip_address, port):
# print(f'{self.name} Get participant {ip_address} {port}')
# for item in self.others:
# print(f'- {item.ip_address} {item.port}')
found_participants = (item for item in self.others
if item.ip_address == ip_address and item.port == port)
participant = next(found_participants, None)
return participant
def AddParticipant(self, ip_address, port):
# print(f'{self.name} Add participant {ip_address} {port}')
remote_participant = Participant(ip_address, port, remote=True, udp_socket=self.udp_socket)
remote_participant.network_id = len(self.others)
self.others.append(remote_participant)
return remote_participant
def Update(self, currentTimeMs = None):
if currentTimeMs is None:
currentTimeMs = time.time() * 1000
if self.publishInterval > 0 and currentTimeMs > self.nextPublishMe:
self.Publish(ParticipantMsg(self.network_id))
print(f'Publish ClientMsg {self.network_id}')
self.nextPublishMe = currentTimeMs + self.publishInterval
Thing.UpdateAll(currentTimeMs)
#endregion
#region Send
def SendThingInfo(self, thing):
self.Send(ThingMsg(self.network_id, thing))
self.Send(NameMsg(self.network_id, thing))
self.Send(ModelUrlMsg(self.network_id, thing))
def Send(self, msg):
buffer_size = msg.Serialize([self.buffer])
if buffer_size <= 0:
return True
# print(f'{self.name} send {self.buffer[0]} to {self.ip_address} {self.port}')
self.udp_socket.sendto(self.buffer[:buffer_size], (self.ip_address, self.port))
return True
def Publish(self, msg):
buffer_size = msg.Serialize([self.buffer])
if buffer_size <= 0:
return True
# print(f'publish {self.buffer[0]} to {self.port}')
self.udp_socket.sendto(self.buffer[:buffer_size], ('<broadcast>', self.port))
return True
#endregion
#region Receive
def Receiver(self): def Get(self, network_id, thing_id):
while True: for thing in self.things:
data, addr = self.udp_socket.recvfrom(1024) if thing is not None:
remote_ip_address = addr[0] if thing.network_id == network_id and thing.id == thing_id:
remote_port = addr[1] return thing
# print(f'msg received from {remote_ip_address}:{remote_port}') return None
remote_participant = self.GetParticipant(remote_ip_address, remote_port)
if remote_participant is None:
# print(f'new participant')
remote_participant = self.AddParticipant(remote_ip_address, remote_port)
self.ReceiveData(data, remote_participant)
def ReceiveData(self, data, remote_participant): ## Update all things
msgId = data[0] def Update(self, currentTimeMs):
# print(f'msg {msgId} ') for thing in list(self.things):
match msgId: if thing is not None:
case ParticipantMsg.id: thing.update(currentTimeMs)
self.ProcessClientMsg(remote_participant, ParticipantMsg(data))
case NetworkIdMsg.id:
self.ProcessNetworkIdMsg(remote_participant, NetworkIdMsg(data))
# case InvestigateMsg.id:
# self.ProcessInvestigateMsg(InvestigateMsg(data))
case ThingMsg.id:
self.ProcessThingMsg(ThingMsg(data))
case NameMsg.id:
self.ProcessNameMsg(NameMsg(data))
case ModelUrlMsg.id:
self.ProcessModelUrlMsg(ModelUrlMsg(data))
# case Messages.BinaryMsg.id:
# msg = Messages.BinaryMsg(data)
# msg.thing.ProcessBinary(msg.data)
def ProcessClientMsg(self, sender, msg: ParticipantMsg):
pass
def ProcessNetworkIdMsg(self, sender, msg: NetworkIdMsg):
print(f'{self.name} receive network id {self.network_id} -> {msg.network_id}')
if self.network_id != msg.network_id:
self.network_id = msg.network_id
# print(f'sending all things {len(Thing.allThings)}')
for thing in Thing.allThings:
sender.SendThingInfo(thing)
# self.Send(NameMsg(self.network_id, thing))
def ProcessInvestigateMsg(self, data: bytearray):
pass
def ProcessThingMsg(self, msg: ThingMsg):
print(f'received thing {msg.network_id} {msg.thing_id}')
if msg.thing_type in self.thing_msg_processors:
constructor = self.thing_msg_processors[msg.thing_type]
constructor(msg.network_id, msg.thing_id)
# not really 'new' thing, but it is a start
thing = Thing.Get(msg.network_id, msg.thing_id)
if thing is not None:
for handler in self.new_thing_handlers:
handler(thing)
def ProcessNameMsg(self, msg: NameMsg):
print(f'received name {msg.name}')
def ProcessModelUrlMsg(self, msg: ModelUrlMsg):
print(f'received model url: {msg.url}')
def ProcessBinary(self, msg: BinaryMsg):
print('received binary data')
if msg.thing != None:
msg.thing.ProcessBinary(msg.data)
def Register(self, constructor, thing_type):
self.thing_msg_processors[thing_type] = constructor
def OnNewThing(self, event_handler):
self.new_thing_handlers.append(event_handler)
def OnNewThingType(self, thing_type, event_handler):
def ConditionalHandler(thing):
if thing.type == thing_type:
event_handler(thing)
self.new_thing_handlers.append(ConditionalHandler)
#endregion

View File

@ -1,4 +1,4 @@
from Participant import Participant from LocalParticipant import LocalParticipant
from Messages.ParticipantMsg import ParticipantMsg from Messages.ParticipantMsg import ParticipantMsg
from Messages.NetworkIdMsg import NetworkIdMsg from Messages.NetworkIdMsg import NetworkIdMsg
from Sensors.TemperatureSensor import TemperatureSensor from Sensors.TemperatureSensor import TemperatureSensor
@ -7,7 +7,7 @@ from Thing import Thing
import socket import socket
import threading import threading
class SiteServer(Participant): class SiteServer(LocalParticipant):
name = "Site Server" name = "Site Server"
def __init__(self, ip_address="0.0.0.0", port=7681, remote=False, udp_socket = None): def __init__(self, ip_address="0.0.0.0", port=7681, remote=False, udp_socket = None):

View File

@ -47,24 +47,24 @@ class Thing:
print('default binary processor') print('default binary processor')
pass pass
allThings = set({ None }) # allThings = set({ None })
@staticmethod # @staticmethod
def Add(thing): # def Add(thing):
thing.id = len(Thing.allThings) # thing.id = len(Thing.allThings)
Thing.allThings.add(thing) # Thing.allThings.add(thing)
@staticmethod # @staticmethod
def Get(network_id, thing_id): # def Get(network_id, thing_id):
for thing in Thing.allThings: # for thing in Thing.allThings:
if thing is not None: # if thing is not None:
if thing.network_id == network_id and thing.id == thing_id: # if thing.network_id == network_id and thing.id == thing_id:
return thing # return thing
return None # return None
## Update all things # ## Update all things
@staticmethod # @staticmethod
def UpdateAll(currentTime): # def UpdateAll(currentTime):
for thing in list(Thing.allThings): # for thing in list(Thing.allThings):
if thing is not None: # if thing is not None:
thing.update(currentTime) # thing.update(currentTime)

View File

@ -1,9 +1,9 @@
__all__ = ['Thing', __all__ = ['Thing',
'Participant', 'LocalParticipant',
'SiteServer'] 'SiteServer']
from .LinearAlgebra.Direction import Direction from .LinearAlgebra.Direction import Direction
from .Participant import Participant from .LocalParticipant import LocalParticipant
from .Thing import Thing from .Thing import Thing
from .LinearAlgebra.Spherical import Spherical from .LinearAlgebra.Spherical import Spherical
from .LinearAlgebra.SwingTwist import SwingTwist from .LinearAlgebra.SwingTwist import SwingTwist

View File

@ -8,13 +8,13 @@ sys.path.append(str(Path(__file__).resolve().parent.parent))
import unittest import unittest
from Thing import Thing from Thing import Thing
from Participant import Participant from LocalParticipant import LocalParticipant
from SiteServer import SiteServer from SiteServer import SiteServer
class ThingTest(unittest.TestCase): class ThingTest(unittest.TestCase):
def test_participant(self): def test_participant(self):
participant: Participant = Participant(ipAddress="127.0.0.1", port=7681) participant: LocalParticipant = LocalParticipant()
milliseconds = time.time() * 1000 milliseconds = time.time() * 1000
start_time = milliseconds start_time = milliseconds
@ -33,7 +33,7 @@ class ThingTest(unittest.TestCase):
def test_site_participant(self): def test_site_participant(self):
site = SiteServer(7681) site = SiteServer(7681)
participant = Participant("127.0.0.1", 7681) participant = LocalParticipant("127.0.0.1", 7681)
milliseconds = time.time() * 1000 milliseconds = time.time() * 1000
start_time = milliseconds start_time = milliseconds
@ -46,7 +46,7 @@ class ThingTest(unittest.TestCase):
def test_thing_msg(self): def test_thing_msg(self):
site = SiteServer() site = SiteServer()
participant = Participant("127.0.0.1") participant = LocalParticipant("127.0.0.1")
thing = Thing() thing = Thing()
thing.name = "First thing" thing.name = "First thing"
thing.model_url = "https://passer.life/extras/ant.jpg" thing.model_url = "https://passer.life/extras/ant.jpg"