public async Task <ButtplugMessage> SendMessage([NotNull] ButtplugMessage aMsg) { _bpLogger.Trace($"Got Message {aMsg.Id} of type {aMsg.GetType().Name} to send"); var id = aMsg.Id; if (id == 0) { return(_bpLogger.LogWarnMsg(id, Error.ErrorClass.ERROR_MSG, "Message Id 0 is reserved for outgoing system messages. Please use another Id.")); } if (aMsg is IButtplugMessageOutgoingOnly) { return(_bpLogger.LogWarnMsg(id, Error.ErrorClass.ERROR_MSG, $"Message of type {aMsg.GetType().Name} cannot be sent to server")); } if (_pingTimedOut) { return(_bpLogger.LogErrorMsg(id, Error.ErrorClass.ERROR_PING, "Ping timed out.")); } // If we get a message that's not RequestServerInfo first, return an error. if (!_receivedRequestServerInfo && !(aMsg is RequestServerInfo)) { return(_bpLogger.LogErrorMsg(id, Error.ErrorClass.ERROR_INIT, "RequestServerInfo must be first message received by server!")); } switch (aMsg) { case RequestLog m: _bpLogger.Debug("Got RequestLog Message"); BpLogManager.Level = m.LogLevel; return(new Ok(id)); case Ping _: // Start the timer _pingTimer?.Change((int)_maxPingTime, (int)_maxPingTime); return(new Ok(id)); case RequestServerInfo rsi: _bpLogger.Debug("Got RequestServerInfo Message"); _receivedRequestServerInfo = true; _clientMessageVersion = rsi.MessageVersion; // Start the timer _pingTimer?.Change((int)_maxPingTime, (int)_maxPingTime); ClientConnected?.Invoke(this, new MessageReceivedEventArgs(rsi)); return(new ServerInfo(_serverName, 1, _maxPingTime, id)); case Test m: return(new Test(m.TestString, id)); } return(await _deviceManager.SendMessage(aMsg)); }
public async Task <ButtplugMessage> SendMessage(ButtplugMessage aMsg) { var id = aMsg.Id; switch (aMsg) { case StartScanning _: StartScanning(); return(new Ok(id)); case StopScanning _: StopScanning(); return(new Ok(id)); case StopAllDevices _: var isOk = true; var errorMsg = string.Empty; foreach (var d in _devices.ToList()) { var r = await d.Value.ParseMessage(new StopDeviceCmd(d.Key, aMsg.Id)); if (r is Ok) { continue; } isOk = false; errorMsg += $"{(r as Error).ErrorMessage}; "; } if (isOk) { return(new Ok(aMsg.Id)); } return(new Error(errorMsg, Error.ErrorClass.ERROR_DEVICE, aMsg.Id)); case RequestDeviceList _: var msgDevices = _devices .Select(aDevice => new DeviceMessageInfo(aDevice.Key, aDevice.Value.Name, GetAllowedMessageTypesAsStrings(aDevice.Value).ToArray())).ToList(); return(new DeviceList(msgDevices.ToArray(), id)); // If it's a device message, it's most likely not ours. case ButtplugDeviceMessage m: _bpLogger.Trace($"Sending {aMsg.GetType().Name} to device index {m.DeviceIndex}"); if (_devices.ContainsKey(m.DeviceIndex)) { return(await _devices[m.DeviceIndex].ParseMessage(m)); } return(_bpLogger.LogErrorMsg(id, Error.ErrorClass.ERROR_DEVICE, $"Dropping message for unknown device index {m.DeviceIndex}")); } return(_bpLogger.LogErrorMsg(id, Error.ErrorClass.ERROR_MSG, $"Message type {aMsg.GetType().Name} unhandled by this server.")); }
public async Task <ButtplugMessage> WriteValue(uint aMsgId, byte[] aValue, bool aWriteWithResponse = false) { if (_txChar == null) { return(_bpLogger.LogErrorMsg(aMsgId, Error.ErrorClass.ERROR_DEVICE, $"WriteValue using txChar called with no txChar available")); } return(await WriteValue(aMsgId, _txChar, aValue, aWriteWithResponse)); }
public async Task <ButtplugMessage> SendMessage([NotNull] ButtplugMessage aMsg) { _bpLogger.Trace($"Got Message {aMsg.Id} of type {aMsg.GetType().Name} to send"); var id = aMsg.Id; if (id == 0) { return(_bpLogger.LogWarnMsg(id, Error.ErrorClass.ERROR_MSG, "Message Id 0 is reserved for outgoing system messages. Please use another Id.")); } if (aMsg is IButtplugMessageOutgoingOnly) { return(_bpLogger.LogWarnMsg(id, Error.ErrorClass.ERROR_MSG, $"Message of type {aMsg.GetType().Name} cannot be sent to server")); } if (_pingTimedOut) { return(_bpLogger.LogErrorMsg(id, Error.ErrorClass.ERROR_PING, "Ping timed out.")); } // If we get a message that's not RequestServerInfo first, return an error. if (!_receivedRequestServerInfo && !(aMsg is RequestServerInfo)) { return(_bpLogger.LogErrorMsg(id, Error.ErrorClass.ERROR_INIT, "RequestServerInfo must be first message received by server!")); } switch (aMsg) { case RequestLog m: _bpLogManager.Level = m.LogLevel; return(new Ok(id)); case Ping _: if (_pingTimer != null) { _pingTimer.Stop(); _pingTimer.Start(); } return(new Ok(id)); case RequestServerInfo _: _receivedRequestServerInfo = true; _pingTimer?.Start(); return(new ServerInfo(_serverName, 1, _maxPingTime, id)); case Test m: return(new Test(m.TestString, id)); } return(await _deviceManager.SendMessage(aMsg)); }
public async Task <ButtplugMessage> WriteValue(uint aMsgId, Guid aCharacteristic, byte[] aValue, bool aWriteOption = false) { if (!(_currentTask is null)) { _currentTask.Cancel(); _bpLogger.Error("Cancelling device transfer in progress for new transfer."); } var chrs = from x in _gattCharacteristics where x.Uuid == aCharacteristic select x; var gattCharacteristics = chrs.ToArray(); if (!gattCharacteristics.Any()) { return(_bpLogger.LogErrorMsg(aMsgId, Error.ErrorClass.ERROR_DEVICE, $"Requested characteristic {aCharacteristic} not found")); } else if (gattCharacteristics.Length > 1) { _bpLogger.Warn($"Multiple gattCharacteristics for {aCharacteristic} found"); } var gattCharacteristic = gattCharacteristics[0]; _currentTask = gattCharacteristic.WriteValueAsync(aValue.AsBuffer(), aWriteOption ? GattWriteOption.WriteWithResponse : GattWriteOption.WriteWithoutResponse); try { var status = await _currentTask; _currentTask = null; if (status != GattCommunicationStatus.Success) { return(_bpLogger.LogErrorMsg(aMsgId, Error.ErrorClass.ERROR_DEVICE, $"GattCommunication Error: {status}")); } } catch (InvalidOperationException e) { // This exception will be thrown if the bluetooth device disconnects in the middle of a transfer. return(_bpLogger.LogErrorMsg(aMsgId, Error.ErrorClass.ERROR_DEVICE, $"GattCommunication Error: {e.Message}")); } return(new Ok(aMsgId)); }
/// <inheritdoc /> public async Task <ButtplugMessage> ParseMessage([NotNull] ButtplugDeviceMessage aMsg) { if (_isDisconnected) { return(BpLogger.LogErrorMsg(aMsg.Id, ErrorClass.ERROR_DEVICE, $"{Name} has disconnected and can no longer process messages.")); } if (!MsgFuncs.ContainsKey(aMsg.GetType())) { return(BpLogger.LogErrorMsg(aMsg.Id, ErrorClass.ERROR_DEVICE, $"{Name} cannot handle message of type {aMsg.GetType().Name}")); } // We just checked whether the key exists above, so we're ok. // ReSharper disable once PossibleNullReferenceException return(await MsgFuncs[aMsg.GetType()].Function.Invoke(aMsg)); }
public async Task <ButtplugMessage> WriteValue(uint aMsgId, uint aCharacteristicIndex, byte[] aValue, bool aWriteOption = false) { if (!(_currentTask is null)) { _currentTask.Cancel(); _bpLogger.Error("Cancelling device transfer in progress for new transfer."); } var gattCharacteristic = _gattCharacteristics[aCharacteristicIndex]; if (gattCharacteristic == null) { return(_bpLogger.LogErrorMsg(aMsgId, Error.ErrorClass.ERROR_DEVICE, $"Requested character {aCharacteristicIndex} out of range")); } _currentTask = gattCharacteristic.WriteValueAsync(aValue.AsBuffer(), aWriteOption ? GattWriteOption.WriteWithResponse : GattWriteOption.WriteWithoutResponse); try { var status = await _currentTask; _currentTask = null; if (status != GattCommunicationStatus.Success) { return(_bpLogger.LogErrorMsg(aMsgId, Error.ErrorClass.ERROR_DEVICE, $"GattCommunication Error: {status}")); } } catch (InvalidOperationException e) { // This exception will be thrown if the bluetooth device disconnects in the middle of a transfer. return(_bpLogger.LogErrorMsg(aMsgId, Error.ErrorClass.ERROR_DEVICE, $"GattCommunication Error: {e.Message}")); } return(new Ok(aMsgId)); }
private async Task HandleConnectionAsync(WebSocket ws, CancellationToken cancellation) { if (!_connections.IsEmpty) { try { ws.WriteString(new ButtplugJsonMessageParser(_logManager).Serialize(_logger.LogErrorMsg( ButtplugConsts.SystemMsgId, ErrorClass.ERROR_INIT, "WebSocketServer already in use!"), 0)); ws.Close(); } catch { // noop } finally { ws.Dispose(); } return; } var remoteId = ws.RemoteEndpoint.ToString(); _connections.AddOrUpdate(remoteId, ws, (oldWs, newWs) => newWs); ConnectionAccepted?.Invoke(this, new ConnectionEventArgs(remoteId)); var b******g = _factory.GetServer(); EventHandler <MessageReceivedEventArgs> msgReceived = (aObject, aEvent) => { var msg = b******g.Serialize(aEvent.Message); if (msg == null) { return; } try { if (ws != null && ws.IsConnected) { ws.WriteString(msg); } if (aEvent.Message is Error && (aEvent.Message as Error).ErrorCode == Error.ErrorClass.ERROR_PING && ws != null && ws.IsConnected) { ws.Close(); } } catch (WebSocketException e) { // Probably means we're repling to a message we recieved just before shutdown. _logger.Error(e.Message, true); } }; b******g.MessageReceived += msgReceived; EventHandler <MessageReceivedEventArgs> clientConnected = (aObject, aEvent) => { var msg = aEvent.Message as RequestServerInfo; var clientName = msg?.ClientName ?? "Unknown client"; ConnectionUpdated?.Invoke(this, new ConnectionEventArgs(remoteId, clientName)); }; b******g.ClientConnected += clientConnected; try { while (ws.IsConnected && !cancellation.IsCancellationRequested) { var msg = await ws.ReadStringAsync(cancellation).ConfigureAwait(false); if (msg != null) { var respMsgs = await b******g.SendMessage(msg); var respMsg = b******g.Serialize(respMsgs); if (respMsg == null) { continue; } await ws.WriteStringAsync(respMsg, cancellation); foreach (var m in respMsgs) { if (m is Error && (m as Error).ErrorCode == Error.ErrorClass.ERROR_PING && ws != null && ws.IsConnected) { ws.Close(); } } } } } catch (Exception e) { _logger.Error(e.Message, true); try { ws.Close(); } catch { // noop } } finally { b******g.MessageReceived -= msgReceived; await b******g.Shutdown(); b******g = null; ws.Dispose(); _connections.TryRemove(remoteId, out _); ConnectionClosed?.Invoke(this, new ConnectionEventArgs(remoteId)); } }
public ButtplugMessage[] Deserialize(string aJsonMsg) { _bpLogger?.Trace($"Got JSON Message: {aJsonMsg}"); var res = new List <ButtplugMessage>(); JArray msgArray; try { msgArray = JArray.Parse(aJsonMsg); } catch (JsonReaderException e) { var err = new Error($"Not valid JSON: {aJsonMsg} - {e.Message}", ErrorClass.ERROR_MSG, ButtplugConsts.SystemMsgId); _bpLogger?.LogErrorMsg(err); res.Add(err); return(res.ToArray()); } var errors = _schema.Validate(msgArray); if (errors.Any()) { var err = new Error("Message does not conform to schema: " + string.Join(", ", errors.Select(aErr => aErr.ToString()).ToArray()), ErrorClass.ERROR_MSG, ButtplugConsts.SystemMsgId); _bpLogger?.LogErrorMsg(err); res.Add(err); return(res.ToArray()); } if (!msgArray.Any()) { var err = new Error("No messages in array", ErrorClass.ERROR_MSG, ButtplugConsts.SystemMsgId); _bpLogger?.LogErrorMsg(err); res.Add(err); return(res.ToArray()); } // JSON input is an array of messages. // We currently only handle the first one. foreach (var o in msgArray.Children <JObject>()) { if (!o.Properties().Any()) { var err = new Error("No message name available", ErrorClass.ERROR_MSG, ButtplugConsts.SystemMsgId); _bpLogger?.LogErrorMsg(err); res.Add(err); continue; } var msgName = o.Properties().First().Name; if (!_messageTypes.Any() || !_messageTypes.ContainsKey(msgName)) { var err = new Error($"{msgName} is not a valid message class", ErrorClass.ERROR_MSG, ButtplugConsts.SystemMsgId); _bpLogger?.LogErrorMsg(err); res.Add(err); continue; } // This specifically could fail due to object conversion. res.Add(DeserializeAs(o, _messageTypes[msgName], msgName, aJsonMsg)); } return(res.ToArray()); }
public ButtplugMessage[] Deserialize(string aJsonMsg) { _bpLogger.Trace($"Got JSON Message: {aJsonMsg}"); var res = new List <ButtplugMessage>(); JArray msgArray; try { msgArray = JArray.Parse(aJsonMsg); } catch (JsonReaderException e) { res.Add(_bpLogger.LogErrorMsg(ButtplugConsts.SystemMsgId, ErrorClass.ERROR_MSG, $"Not valid JSON: {aJsonMsg} - {e.Message}")); return(res.ToArray()); } var errors = _schema.Validate(msgArray); if (errors.Any()) { res.Add(_bpLogger.LogErrorMsg(ButtplugConsts.SystemMsgId, ErrorClass.ERROR_MSG, "Message does not conform to schema: " + string.Join(", ", errors.Select(aErr => aErr.ToString()).ToArray()))); return(res.ToArray()); } if (!msgArray.Any()) { res.Add(_bpLogger.LogErrorMsg(ButtplugConsts.SystemMsgId, ErrorClass.ERROR_MSG, "No messages in array")); return(res.ToArray()); } // JSON input is an array of messages. // We currently only handle the first one. foreach (var o in msgArray.Children <JObject>()) { if (!o.Properties().Any()) { res.Add(_bpLogger.LogErrorMsg(ButtplugConsts.SystemMsgId, ErrorClass.ERROR_MSG, "No message name available")); continue; } var msgName = o.Properties().First().Name; if (!_messageTypes.Keys.Any() || !_messageTypes.Keys.Contains(msgName)) { res.Add(_bpLogger.LogErrorMsg(ButtplugConsts.SystemMsgId, ErrorClass.ERROR_MSG, $"{msgName} is not a valid message class")); continue; } var s = new JsonSerializer { MissingMemberHandling = MissingMemberHandling.Error }; // This specifically could fail due to object conversion. try { var r = o[msgName].Value <JObject>(); res.Add((ButtplugMessage)r.ToObject(_messageTypes[msgName], s)); _bpLogger.Trace($"Message successfully parsed as {msgName} type"); } catch (InvalidCastException e) { res.Add(_bpLogger.LogErrorMsg(ButtplugConsts.SystemMsgId, ErrorClass.ERROR_MSG, $"Could not create message for JSON {aJsonMsg}: {e.Message}")); } catch (JsonSerializationException e) { res.Add(_bpLogger.LogErrorMsg(ButtplugConsts.SystemMsgId, ErrorClass.ERROR_MSG, $"Could not create message for JSON {aJsonMsg}: {e.Message}")); } } return(res.ToArray()); }