Event handling

This commit is contained in:
Pascal Serrarens 2025-01-20 19:26:30 +01:00
parent d9108a418b
commit e218e0ea51
16 changed files with 127 additions and 61 deletions

1
.gitattributes vendored Normal file
View File

@ -0,0 +1 @@
* text=auto

3
.gitignore vendored
View File

@ -1,2 +1,5 @@
__pycache__/*
test/__pycache__/*
.vscode/*
DoxyGen/DoxyWarnLogfile.txt
**/__pycache__

27
BinaryMsg.py Normal file
View File

@ -0,0 +1,27 @@
from .Messages import IMessage
from .Thing import Thing
class BinaryMsg(IMessage):
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

View File

@ -1,4 +1,4 @@
from Messages import IMessage
from .Messages import IMessage
## A client message announces the presence of a participant
#

View File

@ -1,5 +1,5 @@
import numpy as np
from SwingTwist import SwingTwist
from .SwingTwist import SwingTwist
def SendAngle8(buffer, ix_ref, angle):
# Normalize angle
@ -24,7 +24,10 @@ def SendFloat16(buffer, ix_ref, value):
def ReceiveFloat16(buffer, ix_ref) -> float:
ix = ix_ref[0]
# if ix < len(buffer) - 1:
binary = (buffer[ix] << 8) + buffer[ix+1]
# else:
# binary = 0
value16 = np.uint16(binary).view(np.float16)
ix_ref[0] += 2
return float(value16)

View File

@ -1,5 +1,5 @@
import LowLevelMessages
from Thing import Thing
from . import LowLevelMessages
from .Thing import Thing
class IMessage:
id = 0x00
@ -59,28 +59,3 @@ class PoseMsg(IMessage):
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 = 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,4 +1,4 @@
from Messages import IMessage
from .Messages import IMessage
class ModelUrlMsg(IMessage):
id = 0x90

View File

@ -1,4 +1,4 @@
from Messages import IMessage
from .Messages import IMessage
class NameMsg(IMessage):
id = 0x91

View File

@ -1,4 +1,4 @@
from Messages import IMessage
from .Messages import IMessage
## A network id message invites another participant to a site
#

View File

@ -1,12 +1,14 @@
import socket
import threading
import time
from ClientMsg import ClientMsg
from NetworkIdMsg import NetworkIdMsg
from ThingMsg import ThingMsg
from NameMsg import NameMsg
from ModelUrlMsg import ModelUrlMsg
from Thing import Thing
from .ClientMsg import ClientMsg
from .NetworkIdMsg import NetworkIdMsg
from .ThingMsg import ThingMsg
from .NameMsg import NameMsg
from .ModelUrlMsg import ModelUrlMsg
from .BinaryMsg import BinaryMsg
from .Thing import Thing
## A participant is device which can communicate with other participants
#
@ -27,11 +29,13 @@ class Participant:
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", 0))
self.udp_socket.bind(("0.0.0.0", port))
self.AddParticipant(self.ip_address, self.port)
self.thread = threading.Thread(target = self.Receiver)
@ -59,7 +63,10 @@ class Participant:
self.others.append(remote_participant)
return remote_participant
def Update(self, currentTimeMs):
def Update(self, currentTimeMs = None):
if currentTimeMs is None:
currentTimeMs = time.time() * 1000
if self.publishInterval > 0 and currentTimeMs > self.nextPublishMe:
self.Publish(ClientMsg(self.network_id))
print(f'Publish ClientMsg {self.network_id}')
@ -126,8 +133,10 @@ class Participant:
self.ProcessNameMsg(NameMsg(data))
case ModelUrlMsg.id:
self.ProcessModelUrlMsg(ModelUrlMsg(data))
# case Messages.BinaryMsg.id:
# msg = Messages.BinaryMsg(data)
case BinaryMsg.id:
self.ProcessBinary(BinaryMsg(data))
# msg = BinaryMsg(data)
# if msg.thing != None:
# msg.thing.ProcessBinary(msg.data)
def ProcessClientMsg(self, sender, msg: ClientMsg):
@ -146,7 +155,16 @@ class Participant:
pass
def ProcessThingMsg(self, msg: ThingMsg):
print(f'received thing {msg.thing_id}')
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}')
@ -154,4 +172,22 @@ class Participant:
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

@ -0,0 +1,19 @@
from ..Thing import Thing
from .. import LowLevelMessages
class TemperatureSensor(Thing):
def __init__(self, network_id, thing_id):
super().__init__(network_id, thing_id, type = Thing.Type.TemperatureSensor)
self.temp = 0
self._watchers = []
def OnUpdate(self, handler):
self._watchers.append(handler)
def CancelOnUpdate(self, handler):
self._watchers.remove(handler)
def ProcessBinary(self, data):
ix = 0
self.temp = LowLevelMessages.ReceiveFloat16(data, [ix])
for watcher in self._watchers:
watcher(self.temp)

View File

@ -1,6 +1,6 @@
from Participant import Participant
from ClientMsg import ClientMsg
from NetworkIdMsg import NetworkIdMsg
from .Participant import Participant
from .ClientMsg import ClientMsg
from .NetworkIdMsg import NetworkIdMsg
import socket
import threading
@ -19,7 +19,7 @@ class SiteServer(Participant):
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", 7681))
self.udp_socket.bind(("0.0.0.0", port))
self.AddParticipant(self.ip_address, self.port)
self.thread = threading.Thread(target = self.Receiver)

View File

@ -1,5 +1,5 @@
import math
from Direction import Direction
from .Direction import Direction
class Spherical:
def __init__(self, distance, direction):

View File

@ -1,5 +1,5 @@
from Direction import Direction
from Quaternion import Quaternion
from .Direction import Direction
from .Quaternion import Quaternion
class SwingTwist:
def __init__(self, swing: Direction, twist: float):

View File

@ -1,5 +1,5 @@
from Spherical import Spherical
from Quaternion import Quaternion
from .Spherical import Spherical
from .Quaternion import Quaternion
## A thing is the basic building block
#
@ -10,6 +10,7 @@ class Thing:
Switch = 0x01
DistanceSensor = 0x02
DirectionalSensor = 0x03
TemperatureSensor = 0x04
Animator = 0x40
Position = 0x01
@ -17,9 +18,9 @@ class Thing:
LinearVelocity = 0x04
AngularVelocity = 0x08
def __init__(self, type=Type.Undetermined, parent=None, name=None):
self.networkId = 0
self.id = 0
def __init__(self, network_id = 0, thing_id = 0, type=Type.Undetermined, parent=None, name=None):
self.network_id = network_id
self.id = thing_id
self.type = type
if parent is None:
self.parent_id = 0
@ -42,6 +43,7 @@ class Thing:
pass
def ProcessBinary(self, data):
print('default binary processor')
pass
allThings = set({ None })
@ -52,16 +54,16 @@ class Thing:
Thing.allThings.add(thing)
@staticmethod
def Get(networkId, thingId):
def Get(network_id, thing_id):
for thing in Thing.allThings:
if thing is not None:
if thing.networkId == networkId and thing.id == thingId:
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 Thing.allThings:
for thing in list(Thing.allThings):
if thing is not None:
thing.update(currentTime)

View File

@ -1,4 +1,4 @@
from Messages import IMessage
from .Messages import IMessage
class ThingMsg(IMessage):
id = 0x80