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/*
.DS_Store
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):
# if network_id is None:
# return 0
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
# participant.buffer[0:2] = [
# NetworkIdMsg.id,
# network_id
# ]
# return NetworkIdMsg.length
buffer: bytearray = buffer_ref[0]
buffer[0:NetworkIdMsg.length] = [
NetworkIdMsg.id,
self.network_id
]
return NetworkIdMsg.length

View File

@ -1,72 +1,72 @@
# from Messages.Messages import IMessage
from Messages.Messages import IMessage
# class ParticipantMsg(IMessage):
# """! A participant messages notifies other participants of its presence.
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
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 __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
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
buffer: bytearray = buffer_ref[0]
buffer[0:ParticipantMsg.length] = [
ParticipantMsg.id,
self.network_id
]
return ParticipantMsg.length
# @staticmethod
# def Serialized(buffer_ref, network_id):
# if buffer_ref is None or network_id is None:
# return 0
@staticmethod
def Serialized(buffer_ref, network_id):
if buffer_ref is None or network_id is None:
return 0
# buffer: bytearray = buffer_ref[0]
# buffer[0:ClientMsg.length] = [
# ClientMsg.id,
# network_id
# ]
# return ClientMsg.length
buffer: bytearray = buffer_ref[0]
buffer[0:ParticipantMsg.length] = [
ParticipantMsg.id,
network_id
]
return ParticipantMsg.length
## Send the network_id to the participant
# def SendTo(participant, network_id):
# if participant is None or network_id is None:
# return False
# Send the network_id to the participant
def SendTo(participant, network_id):
if participant is None or network_id is None:
return False
# participant.buffer[0:2] = [
# ClientMsg.id,
# network_id
# ]
# participant.SendBuffer(ClientMsg.length)
participant.buffer[0:2] = [
ParticipantMsg.id,
network_id
]
participant.SendBuffer(ParticipantMsg.length)
## Publish the network_id
# def Publish(participant, network_id):
# if participant is None or network_id is None:
# return False
# Publish the network_id
def Publish(participant, network_id):
if participant is None or network_id is None:
return False
# participant.buffer[0:2] = [
# ClientMsg.id,
# network_id
# ]
# participant.PublishBuffer(ClientMsg.length)
participant.buffer[0:2] = [
ParticipantMsg.id,
network_id
]
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:
"""! A participant is used for communcation between things.
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
def __init__(self, ip_address, port):
self.ip_addres = ip_address
self.port = port
self.others = []
self.network_id = 0
self.buffer = bytearray(256)
self.thing_msg_processors = {}
self.new_thing_handlers = []
self.things = set({ None })
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 = 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 Add(self, thing):
thing.id = len(self.things)
self.things.add(thing)
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 Get(self, network_id, thing_id):
for thing in self.things:
if thing is not None:
if thing.network_id == network_id and thing.id == thing_id:
return thing
return None
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 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
## Update all things
def Update(self, currentTimeMs):
for thing in list(self.things):
if thing is not None:
thing.update(currentTimeMs)

View File

@ -1,4 +1,4 @@
from Participant import Participant
from LocalParticipant import LocalParticipant
from Messages.ParticipantMsg import ParticipantMsg
from Messages.NetworkIdMsg import NetworkIdMsg
from Sensors.TemperatureSensor import TemperatureSensor
@ -7,7 +7,7 @@ from Thing import Thing
import socket
import threading
class SiteServer(Participant):
class SiteServer(LocalParticipant):
name = "Site Server"
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')
pass
allThings = set({ None })
# allThings = set({ None })
@staticmethod
def Add(thing):
thing.id = len(Thing.allThings)
Thing.allThings.add(thing)
# @staticmethod
# def Add(thing):
# thing.id = len(Thing.allThings)
# Thing.allThings.add(thing)
@staticmethod
def Get(network_id, thing_id):
for thing in Thing.allThings:
if thing is not None:
if thing.network_id == network_id and thing.id == thing_id:
return thing
return None
# @staticmethod
# def Get(network_id, thing_id):
# for thing in Thing.allThings:
# if thing is not None:
# if thing.network_id == network_id and thing.id == thing_id:
# return thing
# return None
## Update all things
@staticmethod
def UpdateAll(currentTime):
for thing in list(Thing.allThings):
if thing is not None:
thing.update(currentTime)
# ## Update all things
# @staticmethod
# def UpdateAll(currentTime):
# for thing in list(Thing.allThings):
# if thing is not None:
# thing.update(currentTime)

View File

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

View File

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