From 0bd4baa466cb6c808716a8de69134d3724b856df Mon Sep 17 00:00:00 2001 From: Marco Bacis Date: Thu, 29 Jun 2023 15:24:02 +0200 Subject: [PATCH] feat: add middleware and devices status update feature --- WeArtClient.cs | 116 +++++++++++++++++++++++++++++++++++++++++++++-- WeArtMessages.cs | 88 +++++++++++++++++++++++++++++++++++ 2 files changed, 201 insertions(+), 3 deletions(-) diff --git a/WeArtClient.cs b/WeArtClient.cs index 84b0a91..d7f9cee 100644 --- a/WeArtClient.cs +++ b/WeArtClient.cs @@ -95,6 +95,16 @@ public enum ErrorType /// public event Action OnCalibrationResultFail; + /// + /// Called when a status update is received from the middleware + /// + public event Action OnMiddlewareStatusMessage; + + /// + /// Called when a status update about the connected devices is received + /// + public event Action OnDevicesStatusMessage; + /// /// True if a connection to the middleware has been established /// @@ -125,7 +135,7 @@ public int Port public Task Start(TrackingType trackingType = TrackingType.WEART_HAND) { _cancellation = new CancellationTokenSource(); - return Task.Run(() => + return Task.Run(async () => { // Connection loop while (!_cancellation.IsCancellationRequested) @@ -143,6 +153,10 @@ public Task Start(TrackingType trackingType = TrackingType.WEART_HAND) // Send the request to start SendMessage(new StartFromClientMessage { TrackingType = trackingType }); + + // Wait for the middleware to start the session (or throw TimeoutException otherwise) + SendMessage(new GetMiddlewareStatusMessage()); + await WaitForMessage((MiddlewareStatusMessage message) => message.Status == MiddlewareStatus.RUNNING, 5000); } catch (Exception e) { @@ -170,10 +184,25 @@ public Task Start(TrackingType trackingType = TrackingType.WEART_HAND) /// /// Stops the middleware and the established connection /// - public void Stop() + /// True if the middleware was stopped correctly and the connection closed, false otherwise + public async Task Stop() { SendMessage(new StopFromClientMessage()); + + try + { + await WaitForMessage((MiddlewareStatusMessage msg) => + msg.Status != MiddlewareStatus.RUNNING && msg.Status != MiddlewareStatus.STOPPING, + 5000 + ); + } + catch (TimeoutException) + { + return false; + } + StopConnection(); + return true; } /// @@ -192,16 +221,97 @@ public void StopCalibration() SendMessage(new StopCalibrationMessage()); } + /// + /// Asks the middleware to start sending raw data events to the sdk + /// public void StartRawData() { SendMessage(new RawDataOnMessage()); } + /// + /// Tells the middleware to stop sending raw data events + /// public void StopRawData() { SendMessage(new RawDataOffMessage()); } + /// + /// Asks the middleware to send an updated status message + /// + public void AskStatusUpdate() + { + SendMessage(new GetMiddlewareStatusMessage()); + } + + /// + /// Asks the middleware to send an updated message on the status of connected devices + /// + public void AskDevicesStatusUpdate() + { + SendMessage(new GetDevicesStatusMessage()); + } + + /// + /// Waits for any message with the given type for the given timeout + /// + /// Type of the message to wait + /// Timeout to wait for the correct message + /// The received message + /// Thrown when the correct message is not received withih the given timeout + public async Task WaitForMessage(int timeoutMs) + { + return await WaitForMessage((T msg) => true, timeoutMs); + } + + /// + /// Waits for any message with the given type and condition for the given timeout + /// + /// Type of the message to wait + /// Condition that the message must fullfill to be considered ok + /// Timeout to wait for the correct message + /// The received message + /// Thrown when the correct message is not received withih the given timeout + public async Task WaitForMessage(Func predicate, int timeoutMs) + { + // Write Pack and wait for responses (of a certain type) with a given timeout + CancellationTokenSource source = new CancellationTokenSource(); + + T receivedMessage = default(T); + Action onMessageReceived = (MessageType type, IWeArtMessage message) => + { + if (type != MessageType.MessageReceived) return; + if (message is T castedMessage) + { + if (predicate(castedMessage)) + { + receivedMessage = castedMessage; + source.Cancel(); + } + } + }; + + OnMessage += onMessageReceived; + + // Wait to receive message + try + { + await Task.Delay(timeoutMs, source.Token); + throw new TimeoutException(); + } + catch (OperationCanceledException) + { + // Canceled operation means we received the correct message + } + finally + { + OnMessage -= onMessageReceived; + } + + return receivedMessage; + } + /// /// Sends a message to the middleware /// @@ -257,7 +367,7 @@ private bool ReceiveMessages(out IWeArtMessage[] messages) messages = new IWeArtMessage[split.Length]; for (int i = 0; i < messages.Length; i++) { - if(_messageSerializer.Deserialize(split[i], out messages[i])) + if (_messageSerializer.Deserialize(split[i], out messages[i])) ForwardMessage(split[i], messages[i]); } return true; diff --git a/WeArtMessages.cs b/WeArtMessages.cs index 62da4ed..ca3b7b5 100644 --- a/WeArtMessages.cs +++ b/WeArtMessages.cs @@ -4,6 +4,7 @@ */ using Newtonsoft.Json; +using Newtonsoft.Json.Converters; using System; using System.Collections.Generic; using WeArt.Core; @@ -370,4 +371,91 @@ public class RawDataMessage : WeArtJsonMessage public SensorData Middle { get; set; } public SensorData Palm { get; set; } } + + [WeArtMiddlewareMessageID("MW_GET_STATUS")] + public class GetMiddlewareStatusMessage : WeArtJsonMessage { } + + + /// + /// Defines the STATUS. + /// + public enum MiddlewareStatus + { + DISCONNECTED, + IDLE, + STARTING, + RUNNING, + STOPPING, + UPLOADING_TEXTURES, + CONNECTING_DEVICE, + CALIBRATION, + }; + + [WeArtMiddlewareMessageID("MW_STATUS")] + public class MiddlewareStatusMessage : WeArtJsonMessage + { + [JsonConverter(typeof(StringEnumConverter))] + public MiddlewareStatus Status { get; set; } + + public string Version { get; set; } + + public int StatusCode { get; set; } + + public string ErrorDesc { get; set; } + + public bool ActuationsEnabled { get; set; } + + public List ConnectedDevices { get; set; } + + public override string ToString() + { + return JsonConvert.SerializeObject(this); + } + } + + public struct MiddlewareConnectedDevice + { + public string MacAddress { get; set; } + + [JsonConverter(typeof(StringEnumConverter))] + public HandSide HandSide { get; set; } + } + + [WeArtMiddlewareMessageID("DEVICES_GET_STATUS")] + public class GetDevicesStatusMessage : WeArtJsonMessage { } + + [WeArtMiddlewareMessageID("DEVICES_STATUS")] + public class DevicesStatusMessage : WeArtJsonMessage + { + public List Devices { get; set; } + + public override string ToString() + { + return JsonConvert.SerializeObject(this); + } + } + + public struct DeviceStatus + { + public string MacAddress { get; set; } + + [JsonConverter(typeof(StringEnumConverter))] + public HandSide HandSide { get; set; } + + public int BatteryLevel { get; set; } + + public List Thimbles { get; set; } + } + + public struct ThimbleStatus + { + [JsonConverter(typeof(StringEnumConverter))] + public ActuationPoint Id { get; set; } + + public bool Connected { get; set; } + + public int StatusCode { get; set; } + + public string ErrorDesc { get; set; } + } } \ No newline at end of file