private void DeviceAddedHandler(object aObj, DeviceAddedEventArgs aEvent) { // Devices can be turned off by the time they get to this point, at which point they end up null. Make sure the device isn't null. if (aEvent.Device == null) { return; } var duplicates = from x in _devices where x.Value.Identifier == aEvent.Device.Identifier select x; if (duplicates.Any() && (duplicates.Count() > 1 || duplicates.First().Value.IsConnected)) { _bpLogger.Debug($"Already have device {aEvent.Device.Name} in Devices list"); return; } // If we get to 4 billion devices connected, this may be a problem. var deviceIndex = duplicates.Any() ? duplicates.First().Key : (uint)Interlocked.Increment(ref _deviceIndexCounter); _bpLogger.Info((duplicates.Any() ? "Re-" : string.Empty) + $"Adding Device {aEvent.Device.Name} at index {deviceIndex}"); _devices[deviceIndex] = aEvent.Device; aEvent.Device.DeviceRemoved += DeviceRemovedHandler; var msg = new DeviceAdded(deviceIndex, aEvent.Device.Name, GetAllowedMessageTypesAsStrings(aEvent.Device).ToArray()); DeviceMessageReceived?.Invoke(this, new MessageReceivedEventArgs(msg)); }
/// <summary> /// Initializes a new instance of the <see cref="ButtplugJsonMessageParser"/> class. /// </summary> /// <param name="aLogManager">Log manager</param> public ButtplugJsonMessageParser(IButtplugLogManager aLogManager = null) { _bpLogger = aLogManager.GetLogger(GetType()); _bpLogger?.Info($"Setting up {GetType().Name}"); IEnumerable <Type> allTypes; // Some classes in the library may not load on certain platforms due to missing symbols. // If this is the case, we should still find messages even though an exception was thrown. try { allTypes = Assembly.GetAssembly(typeof(ButtplugMessage)).GetTypes(); } catch (ReflectionTypeLoadException e) { allTypes = e.Types; } var messageClasses = allTypes.Where(t => t != null && t.IsClass && t.Namespace == "Buttplug4Net35.Messages" && typeof(ButtplugMessage).IsAssignableFrom(t)); var enumerable = messageClasses as Type[] ?? messageClasses.ToArray(); _bpLogger?.Debug($"Message type count: {enumerable.Length}"); _messageTypes = new Dictionary <string, Type>(); enumerable.ToList().ForEach(aMessageType => { _bpLogger?.Debug($"- {aMessageType.Name}"); _messageTypes.Add(aMessageType.Name, aMessageType); }); }
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)); }
/// <summary> /// Initializes a new instance of the <see cref="ButtplugJsonMessageParser"/> class. /// </summary> /// <param name="aLogManager">Log manager</param> public ButtplugJsonMessageParser(IButtplugLogManager aLogManager = null) { _bpLogger = aLogManager.GetLogger(GetType()); _bpLogger?.Info($"Setting up {GetType().Name}"); _serializer = new JsonSerializer { MissingMemberHandling = MissingMemberHandling.Error }; IEnumerable <Type> allTypes; // Some classes in the library may not load on certain platforms due to missing symbols. // If this is the case, we should still find messages even though an exception was thrown. try { allTypes = Assembly.GetAssembly(typeof(ButtplugMessage)).GetTypes(); } catch (ReflectionTypeLoadException e) { allTypes = e.Types; } var messageClasses = allTypes.Where(t => t != null && t.IsClass && t.Namespace == "B******g.Core.Messages" && typeof(ButtplugMessage).IsAssignableFrom(t)); var enumerable = messageClasses as Type[] ?? messageClasses.ToArray(); _bpLogger?.Debug($"Message type count: {enumerable.Length}"); _messageTypes = new Dictionary <string, Type>(); enumerable.ToList().ForEach(aMessageType => { _bpLogger?.Debug($"- {aMessageType.Name}"); _messageTypes.Add(aMessageType.Name, aMessageType); }); // Load the schema for validation var assembly = Assembly.GetExecutingAssembly(); const string resourceName = "B******g.Core.b******g-schema.json"; Stream stream = null; try { stream = assembly.GetManifestResourceStream(resourceName); using (var reader = new StreamReader(stream)) { stream = null; var result = reader.ReadToEnd(); _schema = JsonSchema4.FromJsonAsync(result).GetAwaiter().GetResult(); } } catch (Exception e) { _bpLogger.LogException(e); throw e; } finally { stream?.Dispose(); } }
public bool MayBeDevice(string advertName, List <Guid> advertGUIDs) { if (_deviceInfo.NamePrefixes.Any()) { foreach (var deviceInfoNamePrefix in _deviceInfo.NamePrefixes) { if (advertName.IndexOf(deviceInfoNamePrefix) != 0) { continue; } _bpLogger.Debug($"Found {advertName} via NamePrefix {deviceInfoNamePrefix}"); return(true); } } if ((_deviceInfo.Names.Any() && !_deviceInfo.Names.Contains(advertName)) || !_deviceInfo.Names.Any()) { _bpLogger.Trace($"Dropping query for {advertName}."); return(false); } if (_deviceInfo.Names.Any() && !advertGUIDs.Any()) { _bpLogger.Debug("Found " + advertName + " for " + _deviceInfo.GetType()); return(true); } _bpLogger.Trace("Found " + advertName + " for " + _deviceInfo.GetType() + " with services " + advertGUIDs); foreach (var s in _deviceInfo.Services) { _bpLogger.Trace("Expecting " + s); } foreach (var s in advertGUIDs) { _bpLogger.Trace("Got " + s); } // Intersect doesn't intersect until the enumerator is called var sv = _deviceInfo.Services.Intersect(advertGUIDs); foreach (var s in sv) { _bpLogger.Trace("Matched " + s); return(true); } return(false); }
public ButtplugServer(ButtplugServerOptions aOptions) { ButtplugUtils.ArgumentNotNull(aOptions, nameof(aOptions)); _clientName = null; _serverName = aOptions.ServerName; _maxPingTime = aOptions.MaxPingTime; _pingTimedOut = false; if (_maxPingTime != 0) { // Create a new timer that wont fire any events just yet _pingTimer = new Timer(PingTimeoutHandler, null, Timeout.Infinite, Timeout.Infinite); } BpLogManager = new ButtplugLogManager(); _bpLogger = BpLogManager.GetLogger(GetType()); _bpLogger.Debug("Setting up ButtplugServer"); _parser = new ButtplugJsonMessageParser(BpLogManager); _deviceManager = aOptions.DeviceManager ?? new DeviceManager(BpLogManager, aOptions.SubtypeManagerSearchPaths); _bpLogger.Info("Finished setting up ButtplugServer"); _deviceManager.DeviceMessageReceived += DeviceMessageReceivedHandler; _deviceManager.ScanningFinished += ScanningFinishedHandler; if (!DeviceConfigurationManager.HasManager) { DeviceConfigurationManager.LoadBaseConfigurationFromResource(); } }
/// <summary> /// Initializes a new instance of the <see cref="ButtplugJsonMessageParser"/> class. /// </summary> /// <param name="aLogManager">Log manager, passed from the parser owner.</param> public ButtplugJsonMessageParser([NotNull] IButtplugLogManager aLogManager) { // Set up logging. if (aLogManager == null) { throw new ArgumentNullException(nameof(aLogManager)); } _bpLogger = aLogManager.GetLogger(GetType()); _bpLogger?.Info($"Setting up {GetType().Name}"); _serializer = new JsonSerializer { MissingMemberHandling = MissingMemberHandling.Error }; _messageTypes = new Dictionary <string, Type>(); foreach (var aMessageType in ButtplugUtils.GetAllMessageTypes()) { _bpLogger?.Debug($"- {aMessageType.Name}"); _messageTypes.Add(aMessageType.Name, aMessageType); } // If we can't find any message types in our assembly, the system is basically useless. if (!_messageTypes.Any()) { throw new ButtplugMessageException(_bpLogger, "No message types available."); } // Load the schema for validation. Schema file is an embedded resource in the library. var jsonSchemaString = ButtplugUtils.GetStringFromFileResource("B******g.b******g-schema.json"); _schema = JsonSchema.FromJsonAsync(jsonSchemaString)?.GetAwaiter().GetResult() ?? throw new InvalidOperationException(); }
public bool MayBeDevice(int aVendorId, int aProductId) { if (_deviceInfo.VendorId != aVendorId || _deviceInfo.ProductId != aProductId) { return(false); } _bpLogger.Debug("Matched " + _deviceInfo.Name); return(true); }
/// <summary> /// Initializes a new instance of the <see cref="ButtplugJsonMessageParser"/> class. /// </summary> /// <param name="aLogManager">Log manager, passed from the parser owner.</param> public ButtplugJsonMessageParser([NotNull] IButtplugLogManager aLogManager) { // Set up logging. if (aLogManager == null) { throw new ArgumentNullException(nameof(aLogManager)); } _bpLogger = aLogManager.GetLogger(GetType()); _bpLogger?.Info($"Setting up {GetType().Name}"); _serializer = new JsonSerializer { MissingMemberHandling = MissingMemberHandling.Error }; _messageTypes = new Dictionary <string, Type>(); foreach (var aMessageType in ButtplugUtils.GetAllMessageTypes()) { _bpLogger?.Debug($"- {aMessageType.Name}"); _messageTypes.Add(aMessageType.Name, aMessageType); } // If we can't find any message types in our assembly, the system is basically useless. if (!_messageTypes.Any()) { throw new ButtplugMessageException(_bpLogger, "No message types available."); } // Load the schema for validation. Schema file is an embedded resource in the library. var assembly = Assembly.GetExecutingAssembly(); const string resourceName = "B******g.b******g-schema.json"; var stream = assembly.GetManifestResourceStream(resourceName); try { using (var reader = new StreamReader(stream ?? throw new InvalidOperationException())) { stream = null; var result = reader.ReadToEnd(); _schema = JsonSchema4.FromJsonAsync(result)?.GetAwaiter().GetResult() ?? throw new InvalidOperationException(); } } finally { // Always make sure we dispose of the resource stream, even if we throw. All // exceptions should be rethrown though. stream?.Dispose(); } }
public ButtplugServer([NotNull] string aServerName, uint aMaxPingTime, DeviceManager aDeviceManager = null) { _serverName = aServerName; _maxPingTime = aMaxPingTime; _pingTimedOut = false; if (aMaxPingTime != 0) { // Create a new timer that wont fire any events just yet _pingTimer = new Timer(PingTimeoutHandler, null, Timeout.Infinite, Timeout.Infinite); } BpLogManager = new ButtplugLogManager(); _bpLogger = BpLogManager.GetLogger(GetType()); _bpLogger.Debug("Setting up ButtplugServer"); _parser = new ButtplugJsonMessageParser(BpLogManager); _deviceManager = aDeviceManager ?? new DeviceManager(BpLogManager); _bpLogger.Info("Finished setting up ButtplugServer"); _deviceManager.DeviceMessageReceived += DeviceMessageReceivedHandler; _deviceManager.ScanningFinished += ScanningFinishedHandler; BpLogManager.LogMessageReceived += LogMessageReceivedHandler; }
public bool MayBeDevice(string advertName, List <Guid> advertGUIDs) { if (_deviceInfo.Names.Any() && !_deviceInfo.Names.Contains(advertName)) { return(false); } if (_deviceInfo.Names.Any() && !advertGUIDs.Any()) { _bpLogger.Debug("Found " + advertName + " for " + _deviceInfo.GetType().ToString()); _bpLogger.Debug("No advertised services?"); return(true); } _bpLogger.Debug("Found " + advertName + " for " + _deviceInfo.GetType().ToString()); foreach (var s in _deviceInfo.Services) { _bpLogger.Debug("Expecting " + s.ToString()); } foreach (var s in advertGUIDs) { _bpLogger.Debug("Got " + s.ToString()); } // Intersect doesn't intersect until the enumerator is called var sv = _deviceInfo.Services.Intersect(advertGUIDs); foreach (var s in sv) { _bpLogger.Debug("Matched " + s.ToString()); return(true); } return(false); }
public async Task <ButtplugMessage> SendMessageAsync([NotNull] ButtplugMessage aMsg, CancellationToken aToken = default(CancellationToken)) { var combinedToken = CancellationTokenSource.CreateLinkedTokenSource(_internalToken.Token, aToken); _bpLogger.Trace($"Got Message {aMsg.Id} of type {aMsg.GetType().Name} to send"); var id = aMsg.Id; ButtplugUtils.ArgumentNotNull(aMsg, nameof(aMsg)); if (id == ButtplugConsts.SystemMsgId) { throw new ButtplugMessageException(_bpLogger, "Message Id 0 is reserved for outgoing system messages. Please use another Id.", id); } if (aMsg is IButtplugMessageOutgoingOnly) { throw new ButtplugMessageException(_bpLogger, $"Message of type {aMsg.GetType().Name} cannot be sent to server", id); } if (_pingTimedOut) { throw new ButtplugPingException(_bpLogger, "Ping timed out.", id); } // If we get a message that's not RequestServerInfo first, return an error. if (!_receivedRequestServerInfo && !(aMsg is RequestServerInfo)) { throw new ButtplugHandshakeException(_bpLogger, "RequestServerInfo must be first message received by server!", id); } _bpLogger.Debug($"Got {aMsg.Name} message."); switch (aMsg) { case RequestLog m: 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: if (_receivedRequestServerInfo) { throw new ButtplugHandshakeException(_bpLogger, "Already received RequestServerInfo, cannot be sent twice.", id); } _receivedRequestServerInfo = true; _clientSpecVersion = rsi.MessageVersion; _deviceManager.SpecVersion = _clientSpecVersion; // Start the timer _pingTimer?.Change((int)_maxPingTime, (int)_maxPingTime); _clientName = rsi.ClientName; ClientConnected?.Invoke(this, EventArgs.Empty); return(new ServerInfo(_serverName, ButtplugConsts.CurrentSpecVersion, _maxPingTime, id)); case Core.Messages.Test m: return(new Core.Messages.Test(m.TestString, id)); } return(await _deviceManager.SendMessageAsync(aMsg, combinedToken.Token).ConfigureAwait(false)); }
protected DeviceSubtypeManager([NotNull] IButtplugLogManager aLogManager) { LogManager = aLogManager; BpLogger = aLogManager.GetLogger(GetType()); BpLogger.Debug($"Setting up Device Manager {GetType().Name}"); }
public ButtplugMessage[] Deserialize(string aJsonMsg) { _bpLogger.Trace($"Got JSON Message: {aJsonMsg}"); var res = new List <ButtplugMessage>(); JArray a; try { a = JArray.Parse(aJsonMsg); } catch (JsonReaderException e) { _bpLogger.Debug($"Not valid JSON: {aJsonMsg}"); _bpLogger.Debug(e.Message); res.Add(new Error("Not valid JSON", ErrorClass.ERROR_MSG, ButtplugConsts.SystemMsgId)); return(res.ToArray()); } var errors = _schema.Validate(a); if (errors.Any()) { res.Add(new Error("Message does not conform to schema: " + string.Join(", ", errors.Select(aErr => aErr.ToString()).ToArray()), ErrorClass.ERROR_MSG, ButtplugConsts.SystemMsgId)); return(res.ToArray()); } if (!a.Any()) { res.Add(new Error("No messages in array", ErrorClass.ERROR_MSG, ButtplugConsts.SystemMsgId)); return(res.ToArray()); } // JSON input is an array of messages. // We currently only handle the first one. foreach (var o in a.Children <JObject>()) { if (!o.Properties().Any()) { res.Add(new Error("No message name available", ErrorClass.ERROR_MSG, ButtplugConsts.SystemMsgId)); continue; } var msgName = o.Properties().First().Name; if (!_messageTypes.Keys.Any() || !_messageTypes.Keys.Contains(msgName)) { res.Add(new Error($"{msgName} is not a valid message class", ErrorClass.ERROR_MSG, ButtplugConsts.SystemMsgId)); 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()); }