/// <summary> /// Deserializes a byte stream into a <see cref="WearableDeviceConfig"/>. Allocates the return object if one /// is not provided. /// </summary> /// <param name="buffer"></param> /// <param name="index"></param> /// <param name="config">The config to fill into, or null to allocate a new one.</param> /// <returns></returns> protected static WearableDeviceConfig DeserializeDeviceConfig(byte[] buffer, ref int index, WearableDeviceConfig config = null) { if (config == null) { config = new WearableDeviceConfig(); } // Decode header DeviceConfigPacketHeader header = DeserializeGenericPacket <DeviceConfigPacketHeader>(buffer, ref index); config.updateInterval = header.updateInterval; // Decode sensor configs for (int i = 0; i < header.sensorCount; i++) { SensorConfigPacket sensorConfig = DeserializeGenericPacket <SensorConfigPacket>(buffer, ref index); config.GetSensorConfig(sensorConfig.sensorId).isEnabled = sensorConfig.enabled != 0; } // Decode gesture configs for (int i = 0; i < header.gestureCount; i++) { GestureConfigPacket gestureConfig = DeserializeGenericPacket <GestureConfigPacket>(buffer, ref index); config.GetGestureConfig(gestureConfig.gestureId).isEnabled = gestureConfig.enabled != 0; } return(config); }
internal WearableProxyProvider() { _config = null; ResetDeviceStatus(); _networkTimeout = 1.0f; _server = new TcpClient(); _protocol = new WearableProxyClientProtocol(); _protocol.ConnectionStatus += OnConnectionStatus; _protocol.DeviceList += OnDeviceList; _protocol.KeepAlive += OnKeepAlive; _protocol.ConfigStatus += OnConfigStatus; _protocol.NewSensorFrame += OnNewSensorFrame; _protocol.PingQuery += OnPingQuery; _portNumber = 0; _hostname = string.Empty; _receiveIndex = 0; _receiveBuffer = new byte[WearableProxyProtocolBase.SuggestedServerToClientBufferSize]; _transmitIndex = 0; _transmitBuffer = new byte[WearableProxyProtocolBase.SuggestedClientToServerBufferSize]; _issuedWarningLastPacket = false; }
/// <summary> /// Called when the server sends an update to the device config information. Copies this config into local state. /// </summary> /// <param name="config"></param> private void OnConfigStatus(WearableDeviceConfig config) { _config = config.Clone(); // It's fine to invoke this every time since the provider base will determine if the call was in response to // a request. OnReceivedDeviceConfiguration(config); }
internal override void SetDeviceConfiguration(WearableDeviceConfig config) { _transmitIndex = 0; WearableProxyClientProtocol.EncodeSetNewConfig(_transmitBuffer, ref _transmitIndex, config); SendTransmitBuffer(); // Since failed attempts, retries, etc are handled on the <i>server</i> side, all configuration attempts // should be considered a success as long as the packet was sent out. _sendConfigSuccessNextFrame = true; }
/// <summary> /// Encode a Set New Config packet into the specified buffer /// </summary> /// <param name="buffer"></param> /// <param name="index"></param> /// <param name="config"></param> public static void EncodeSetNewConfig(byte[] buffer, ref int index, WearableDeviceConfig config) { // Encode header PacketHeader header = new PacketHeader(PacketTypeCode.SetNewConfig); SerializeGenericPacket(buffer, ref index, header); // Payload SerializeDeviceConfig(buffer, ref index, config); // Encode footer SerializeGenericPacket(buffer, ref index, _footer); }
private void OnSetConfigPacket(WearableDeviceConfig config) { _deviceProvider.SetDeviceConfiguration(config); _transmitIndex = 0; WearableProxyServerProtocol.EncodeConfigStatus( _transmitBuffer, ref _transmitIndex, _deviceProvider.GetCachedDeviceConfiguration()); SendTransmitBuffer(); }
/// <summary> /// Serialize a <see cref="WearableDeviceConfig"/> into a byte buffer. /// </summary> /// <param name="buffer"></param> /// <param name="index"></param> /// <param name="config"></param> protected static void SerializeDeviceConfig(byte[] buffer, ref int index, WearableDeviceConfig config) { // Encode sub-header SerializeGenericPacket(buffer, ref index, new DeviceConfigPacketHeader { sensorCount = WearableConstants.SensorIds.Length, gestureCount = WearableConstants.GestureIds.Length - 1, // Subtract 1 for "None" updateInterval = config.updateInterval }); // Encode payload for (int i = 0; i < WearableConstants.SensorIds.Length; i++) { SensorId sensorId = WearableConstants.SensorIds[i]; var sensorConfigPacket = new SensorConfigPacket { sensorId = sensorId, enabled = (byte)(config.GetSensorConfig(sensorId).isEnabled ? 1 : 0) }; SerializeGenericPacket(buffer, ref index, sensorConfigPacket); } for (int i = 0; i < WearableConstants.GestureIds.Length; i++) { GestureId gestureId = WearableConstants.GestureIds[i]; if (gestureId == GestureId.None) { continue; } var gestureConfigPacket = new GestureConfigPacket { gestureId = gestureId, enabled = (byte)(config.GetGestureConfig(gestureId).isEnabled ? 1 : 0) }; SerializeGenericPacket(buffer, ref index, gestureConfigPacket); } }
public void TestClientToServer() { WearableProxyServerProtocol serverProtocol = new WearableProxyServerProtocol(); int serverIndex = 0; byte[] serverBuffer = new byte[1024]; // Register callbacks for packet processing serverProtocol.KeepAlive += () => { _lastPacketName = "KeepAlive"; }; serverProtocol.PingQuery += () => { _lastPacketName = "PingQuery"; }; serverProtocol.PingResponse += () => { _lastPacketName = "PingResponse"; }; serverProtocol.SetNewConfig += deviceConfig => { _lastPacketName = "SetConfig"; Assert.AreEqual(SensorUpdateInterval.ThreeHundredTwentyMs, deviceConfig.updateInterval); Assert.AreEqual(false, deviceConfig.rotationSixDof.isEnabled); Assert.AreEqual(true, deviceConfig.gyroscope.isEnabled); Assert.AreEqual(false, deviceConfig.accelerometer.isEnabled); Assert.AreEqual(false, deviceConfig.headNodGesture.isEnabled); Assert.AreEqual(true, deviceConfig.doubleTapGesture.isEnabled); }; serverProtocol.QueryConfigStatus += () => { _lastPacketName = "QueryConfig"; }; serverProtocol.RSSIFilterValueChange += value => { _lastPacketName = "SetRSSI"; Assert.AreEqual(-40, value); }; serverProtocol.InitiateDeviceSearch += () => { _lastPacketName = "StartSearch"; }; serverProtocol.StopDeviceSearch += () => { _lastPacketName = "StopSearch"; }; serverProtocol.ConnectToDevice += uid => { _lastPacketName = "ConnectToDevice"; Assert.AreEqual("00000000-0000-0000-0000-000000000000", uid); }; serverProtocol.DisconnectFromDevice += () => { _lastPacketName = "DisconnectFromDevice"; }; serverProtocol.QueryConnectionStatus += () => { _lastPacketName = "QueryConnection"; }; // Encode WearableProxyProtocolBase.EncodeKeepAlive(serverBuffer, ref serverIndex); WearableProxyProtocolBase.EncodePingQuery(serverBuffer, ref serverIndex); WearableProxyProtocolBase.EncodePingResponse(serverBuffer, ref serverIndex); WearableDeviceConfig config = new WearableDeviceConfig { updateInterval = SensorUpdateInterval.ThreeHundredTwentyMs, gyroscope = { isEnabled = true }, doubleTapGesture = { isEnabled = true } }; WearableProxyClientProtocol.EncodeSetNewConfig(serverBuffer, ref serverIndex, config); WearableProxyClientProtocol.EncodeQueryConfig(serverBuffer, ref serverIndex); WearableProxyClientProtocol.EncodeRSSIFilterControl(serverBuffer, ref serverIndex, -40); WearableProxyClientProtocol.EncodeInitiateDeviceSearch(serverBuffer, ref serverIndex); WearableProxyClientProtocol.EncodeStopDeviceSearch(serverBuffer, ref serverIndex); WearableProxyClientProtocol.EncodeConnectToDevice(serverBuffer, ref serverIndex, "00000000-0000-0000-0000-000000000000"); WearableProxyClientProtocol.EncodeDisconnectFromDevice(serverBuffer, ref serverIndex); WearableProxyClientProtocol.EncodeQueryConnectionStatus(serverBuffer, ref serverIndex); WearableProxyProtocolBase.EncodeKeepAlive(serverBuffer, ref serverIndex); // Decode serverIndex = 0; serverProtocol.ProcessPacket(serverBuffer, ref serverIndex); Assert.AreEqual("KeepAlive", _lastPacketName); serverProtocol.ProcessPacket(serverBuffer, ref serverIndex); Assert.AreEqual("PingQuery", _lastPacketName); serverProtocol.ProcessPacket(serverBuffer, ref serverIndex); Assert.AreEqual("PingResponse", _lastPacketName); serverProtocol.ProcessPacket(serverBuffer, ref serverIndex); Assert.AreEqual("SetConfig", _lastPacketName); serverProtocol.ProcessPacket(serverBuffer, ref serverIndex); Assert.AreEqual("QueryConfig", _lastPacketName); serverProtocol.ProcessPacket(serverBuffer, ref serverIndex); Assert.AreEqual("SetRSSI", _lastPacketName); serverProtocol.ProcessPacket(serverBuffer, ref serverIndex); Assert.AreEqual("StartSearch", _lastPacketName); serverProtocol.ProcessPacket(serverBuffer, ref serverIndex); Assert.AreEqual("StopSearch", _lastPacketName); serverProtocol.ProcessPacket(serverBuffer, ref serverIndex); Assert.AreEqual("ConnectToDevice", _lastPacketName); serverProtocol.ProcessPacket(serverBuffer, ref serverIndex); Assert.AreEqual("DisconnectFromDevice", _lastPacketName); serverProtocol.ProcessPacket(serverBuffer, ref serverIndex); Assert.AreEqual("QueryConnection", _lastPacketName); serverProtocol.ProcessPacket(serverBuffer, ref serverIndex); Assert.AreEqual("KeepAlive", _lastPacketName); }
public void TestServerToClient() { WearableProxyClientProtocol clientProtocol = new WearableProxyClientProtocol(); int clientIndex = 0; byte[] clientBuffer = new byte[1024]; // Register callbacks for packet processing clientProtocol.KeepAlive += () => { _lastPacketName = "KeepAlive"; }; clientProtocol.NewSensorFrame += frame => { _lastPacketName = "SensorFrame"; Assert.AreEqual(123.45f, frame.timestamp); Assert.AreEqual(0.1f, frame.deltaTime); Assert.AreEqual(Vector3.right, frame.acceleration.value); Assert.AreEqual(SensorAccuracy.Low, frame.acceleration.accuracy); Assert.AreEqual(Vector3.up, frame.angularVelocity.value); Assert.AreEqual(SensorAccuracy.High, frame.angularVelocity.accuracy); Assert.AreEqual(new Quaternion(1.0f, 2.0f, 3.0f, 4.0f), frame.rotationSixDof.value); Assert.AreEqual(15.0, frame.rotationSixDof.measurementUncertainty); Assert.AreEqual(GestureId.DoubleTap, frame.gestureId); }; clientProtocol.DeviceList += devices => { _lastPacketName = "DeviceList"; Assert.IsTrue(devices.Length == 3); Assert.IsTrue(devices[0].name == "Product Name"); Assert.IsTrue(devices[0].productId == ProductId.Undefined); Assert.IsTrue(devices[0].firmwareVersion == "0.13.2f"); Assert.IsTrue(devices[1].name == "Corey's Device"); Assert.IsTrue(devices[1].productId == ProductId.Frames); Assert.IsTrue(devices[1].variantId == (byte)FramesVariantId.Alto); Assert.IsTrue(devices[2].name == "Michael's Headphones"); Assert.IsTrue(devices[2].productId == ProductId.Frames); Assert.IsTrue(devices[2].variantId == (byte)FramesVariantId.Rondo); }; clientProtocol.ConnectionStatus += (state, device) => { _lastPacketName = "ConnectionStatus"; Assert.IsTrue(state == WearableProxyProtocolBase.ConnectionState.Connected); Assert.IsTrue(device != null); Assert.IsTrue(device.Value.name == "Product Name"); Assert.AreEqual(ProductId.Frames, device.Value.productId); Assert.AreEqual((byte)FramesVariantId.Alto, device.Value.variantId); Assert.IsTrue(device.Value.uid == "00000000-0000-0000-0000-000000000000"); }; clientProtocol.ConfigStatus += deviceConfig => { _lastPacketName = "ConfigStatus"; Assert.AreEqual(SensorUpdateInterval.ThreeHundredTwentyMs, deviceConfig.updateInterval); Assert.AreEqual(false, deviceConfig.rotationSixDof.isEnabled); Assert.AreEqual(true, deviceConfig.gyroscope.isEnabled); Assert.AreEqual(false, deviceConfig.accelerometer.isEnabled); Assert.AreEqual(false, deviceConfig.headNodGesture.isEnabled); Assert.AreEqual(true, deviceConfig.doubleTapGesture.isEnabled); }; WearableProxyProtocolBase.EncodeKeepAlive(clientBuffer, ref clientIndex); WearableProxyServerProtocol.EncodeSensorFrame( clientBuffer, ref clientIndex, new SensorFrame { timestamp = 123.45f, deltaTime = 0.1f, acceleration = new SensorVector3 { value = Vector3.right, accuracy = SensorAccuracy.Low }, angularVelocity = new SensorVector3 { value = Vector3.up, accuracy = SensorAccuracy.High }, rotationSixDof = new SensorQuaternion { value = new Quaternion(1.0f, 2.0f, 3.0f, 4.0f), measurementUncertainty = 15.0f }, gestureId = GestureId.DoubleTap }); WearableProxyServerProtocol.EncodeDeviceList( clientBuffer, ref clientIndex, new[] { new Device { isConnected = false, name = "Product Name", firmwareVersion = "0.13.2f", productId = ProductId.Undefined, variantId = (byte)FramesVariantId.Undefined, rssi = -30, uid = "00000000-0000-0000-0000-000000000000" }, new Device { isConnected = false, name = "Corey's Device", productId = ProductId.Frames, variantId = (byte)FramesVariantId.Alto, rssi = -40, uid = "00000000-0000-0000-0000-000000000000" }, new Device { isConnected = false, name = "Michael's Headphones", productId = ProductId.Frames, variantId = (byte)FramesVariantId.Rondo, rssi = -55, uid = "00000000-0000-0000-0000-000000000000" } }); WearableProxyServerProtocol.EncodeConnectionStatus( clientBuffer, ref clientIndex, WearableProxyProtocolBase.ConnectionState.Connected, new Device { isConnected = true, name = "Product Name", productId = ProductId.Frames, variantId = (byte)FramesVariantId.Alto, rssi = -30, uid = "00000000-0000-0000-0000-000000000000" }); WearableDeviceConfig config = new WearableDeviceConfig { updateInterval = SensorUpdateInterval.ThreeHundredTwentyMs, gyroscope = { isEnabled = true }, doubleTapGesture = { isEnabled = true } }; WearableProxyServerProtocol.EncodeConfigStatus(clientBuffer, ref clientIndex, config); WearableProxyProtocolBase.EncodeKeepAlive(clientBuffer, ref clientIndex); // Decode clientIndex = 0; clientProtocol.ProcessPacket(clientBuffer, ref clientIndex); Assert.AreEqual(_lastPacketName, "KeepAlive"); clientProtocol.ProcessPacket(clientBuffer, ref clientIndex); Assert.AreEqual(_lastPacketName, "SensorFrame"); clientProtocol.ProcessPacket(clientBuffer, ref clientIndex); Assert.AreEqual(_lastPacketName, "DeviceList"); clientProtocol.ProcessPacket(clientBuffer, ref clientIndex); Assert.AreEqual(_lastPacketName, "ConnectionStatus"); clientProtocol.ProcessPacket(clientBuffer, ref clientIndex); Assert.AreEqual(_lastPacketName, "ConfigStatus"); clientProtocol.ProcessPacket(clientBuffer, ref clientIndex); Assert.AreEqual(_lastPacketName, "KeepAlive"); }
/// <summary> /// Consume a packet from the buffer if possible, then advance the buffer index. /// </summary> /// <param name="buffer">Byte buffer to decode</param> /// <param name="index">(Ref) Index to read into buffer</param> /// <exception cref="WearableProxyProtocolException">Thrown when a packet cannot be decoded and the buffer /// must be discarded.</exception> /// <exception cref="IndexOutOfRangeException">Thrown when a packet was partially consumed but ran out of /// buffer contents.</exception> public override void ProcessPacket(byte[] buffer, ref int index) { PacketTypeCode packetType = DecodePacketType(buffer, ref index); switch (packetType) { case PacketTypeCode.KeepAlive: { CheckFooter(buffer, ref index); if (KeepAlive != null) { KeepAlive.Invoke(); } break; } case PacketTypeCode.PingQuery: { CheckFooter(buffer, ref index); if (PingQuery != null) { PingQuery.Invoke(); } break; } case PacketTypeCode.PingResponse: { CheckFooter(buffer, ref index); if (PingResponse != null) { PingResponse.Invoke(); } break; } case PacketTypeCode.SensorFrame: { SensorFrame frame = DecodeSensorFrame(buffer, ref index); CheckFooter(buffer, ref index); if (NewSensorFrame != null) { NewSensorFrame.Invoke(frame); } break; } case PacketTypeCode.DeviceList: { Device[] devices = DecodeDeviceList(buffer, ref index); CheckFooter(buffer, ref index); if (DeviceList != null) { DeviceList.Invoke(devices); } break; } case PacketTypeCode.ConnectionStatus: { Device? device; ConnectionState status = DecodeConnectionStatus(buffer, ref index, out device); CheckFooter(buffer, ref index); if (ConnectionStatus != null) { ConnectionStatus.Invoke(status, device); } break; } case PacketTypeCode.ConfigStatus: { WearableDeviceConfig config = DeserializeDeviceConfig(buffer, ref index); CheckFooter(buffer, ref index); if (ConfigStatus != null) { ConfigStatus.Invoke(config); } break; } case PacketTypeCode.SetRssiFilter: case PacketTypeCode.InitiateDeviceSearch: case PacketTypeCode.StopDeviceSearch: case PacketTypeCode.ConnectToDevice: case PacketTypeCode.DisconnectFromDevice: case PacketTypeCode.QueryConnectionStatus: case PacketTypeCode.SetNewConfig: case PacketTypeCode.QueryConfig: // This is a known, but contextually-invalid packet type throw new WearableProxyProtocolException(WearableConstants.ProxyProviderInvalidPacketError); default: // This is an unknown or invalid packet type throw new WearableProxyProtocolException(WearableConstants.ProxyProviderInvalidPacketError); } }
/// <summary> /// Consume a packet from the buffer if possible, then advance the buffer index. /// </summary> /// <param name="buffer">Byte buffer to decode</param> /// <param name="index">(Ref) Index to read into buffer</param> /// <exception cref="WearableProxyProtocolException">Thrown when a packet cannot be decoded and the buffer /// must be discarded.</exception> /// <exception cref="IndexOutOfRangeException">Thrown when a packet was partially consumed but ran out of /// buffer contents.</exception> public override void ProcessPacket(byte[] buffer, ref int index) { PacketTypeCode packetType = DecodePacketType(buffer, ref index); switch (packetType) { case PacketTypeCode.KeepAlive: { CheckFooter(buffer, ref index); if (KeepAlive != null) { KeepAlive.Invoke(); } break; } case PacketTypeCode.PingQuery: { CheckFooter(buffer, ref index); if (PingQuery != null) { PingQuery.Invoke(); } break; } case PacketTypeCode.PingResponse: { CheckFooter(buffer, ref index); if (PingResponse != null) { PingResponse.Invoke(); } break; } case PacketTypeCode.SetRssiFilter: { int value = DecodeRSSIFilterControlPacket(buffer, ref index); CheckFooter(buffer, ref index); if (RSSIFilterValueChange != null) { RSSIFilterValueChange.Invoke(value); } break; } case PacketTypeCode.InitiateDeviceSearch: { CheckFooter(buffer, ref index); if (InitiateDeviceSearch != null) { InitiateDeviceSearch.Invoke(); } break; } case PacketTypeCode.StopDeviceSearch: { CheckFooter(buffer, ref index); if (StopDeviceSearch != null) { StopDeviceSearch.Invoke(); } break; } case PacketTypeCode.ConnectToDevice: { string uid = DecodeDeviceConnectPacket(buffer, ref index); CheckFooter(buffer, ref index); if (ConnectToDevice != null) { ConnectToDevice.Invoke(uid); } break; } case PacketTypeCode.DisconnectFromDevice: { CheckFooter(buffer, ref index); if (DisconnectFromDevice != null) { DisconnectFromDevice.Invoke(); } break; } case PacketTypeCode.QueryConnectionStatus: { CheckFooter(buffer, ref index); if (QueryConnectionStatus != null) { QueryConnectionStatus.Invoke(); } break; } case PacketTypeCode.QueryConfig: { CheckFooter(buffer, ref index); if (QueryConfigStatus != null) { QueryConfigStatus.Invoke(); } break; } case PacketTypeCode.SetNewConfig: { // N.B. This generates a tiny bit of garbage, but avoids race conditions by allocating for every packet WearableDeviceConfig config = DeserializeDeviceConfig(buffer, ref index); CheckFooter(buffer, ref index); if (SetNewConfig != null) { SetNewConfig.Invoke(config); } break; } case PacketTypeCode.ConfigStatus: case PacketTypeCode.SensorFrame: case PacketTypeCode.DeviceList: case PacketTypeCode.ConnectionStatus: // Known, but contextually-invalid packet throw new WearableProxyProtocolException(WearableConstants.ProxyProviderInvalidPacketError); default: // Unknown or corrupt packet throw new WearableProxyProtocolException(WearableConstants.ProxyProviderInvalidPacketError); } }
/// <summary> /// Call when we don't know the status of the device. /// </summary> private void ResetDeviceStatus() { _config = null; }