/// <summary> /// Given a B******g message and a schema version, turn the message into a JSON.Net JObject. /// This method can return null in some cases, which should be checked for by callers. /// </summary> /// <param name="aMsg">Message to convert to JSON.</param> /// <param name="aClientSpecVersion"> /// Schema version of the client the message will be sent to. /// </param> /// <exception cref="ButtplugMessageException"> /// Throws when no backward compatible non-system message can be found, or when a default /// constructor for a message is not available. /// </exception> /// <returns>JObject on success, but can return null in cases where a system message is not compatible with a client schema.</returns> private JObject ButtplugMessageToJObject([NotNull] ButtplugMessage aMsg, uint aClientSpecVersion) { // Support downgrading messages while (aMsg.SpecVersion > aClientSpecVersion) { if (aMsg.PreviousType == null) { if (aMsg.Id != ButtplugConsts.SystemMsgId) { throw new ButtplugMessageException(_bpLogger, $"No backwards compatible version for message #{aMsg.Name} at Schema Version {aClientSpecVersion}!", aMsg.Id); } // There's no previous version of this system message. We can't send the current // version, because whoever is on the other side won't be able to parse it. // However, this isn't exactly an error, because there's no real way to recover // from this and it's not like it'd do any good to send it. Therefore we just log // and throw the message away. _bpLogger.Warn($"System message skipped due to version mismatch - {aMsg}"); return(null); } var newMsg = (ButtplugMessage)aMsg.PreviousType.GetConstructor( new[] { aMsg.GetType() })?.Invoke(new object[] { aMsg }); if (newMsg == null) { throw new ButtplugMessageException(_bpLogger, $"No default constructor for message #{aMsg.GetType().Name}!", aMsg.Id); } return(ButtplugMessageToJObject(newMsg, aClientSpecVersion)); } return(new JObject(new JProperty(aMsg.Name, JObject.FromObject(aMsg)))); }
/// <summary> /// Serializes a single <see cref="ButtplugMessage"/> object into a JSON string for a specified version of the schema. /// </summary> /// <param name="aMsg"><see cref="ButtplugMessage"/> object</param> /// <param name="clientSchemaVersion">Target schema version</param> /// <returns>JSON string representing a B******g message</returns> public string Serialize([NotNull] ButtplugMessage aMsg, uint clientSchemaVersion) { // Warning: Any log messages in this function must be localOnly. They will possibly recurse. // Support downgrading messages var tmp = aMsg; while (tmp == null || tmp.SchemaVersion > clientSchemaVersion) { if (tmp?.PreviousType == null) { if (aMsg.Id == ButtplugConsts.SystemMsgId) { // There's no previous version of this system message _bpLogger?.Warn($"No messages serialized!"); return(null); } var err = new Error($"No backwards compatible version for message #{aMsg.GetType().Name}!", ErrorClass.ERROR_MSG, aMsg.Id); var eo = new JObject(new JProperty(err.GetType().Name, JObject.FromObject(err))); var ea = new JArray(eo); _bpLogger?.Error(err.ErrorMessage, true); _bpLogger?.Trace($"Message serialized to: {ea.ToString(Formatting.None)}", true); return(ea.ToString(Formatting.None)); } tmp = (ButtplugMessage)aMsg.PreviousType.GetConstructor( new Type[] { tmp.GetType() })?.Invoke(new object[] { tmp }); } var o = new JObject(new JProperty(aMsg.GetType().Name, JObject.FromObject(tmp))); var a = new JArray(o); _bpLogger?.Trace($"Message serialized to: {a.ToString(Formatting.None)}", true); return(a.ToString(Formatting.None)); }
/// <summary> /// Finds all Manager DLLs (DLLs named "B******g.Server.Managers.*.dll") in the local /// directory, loads them into the assembly cache, scans them for DeviceSubtypeManager /// deriving classes, and adds instances of those classes to the device manager. Handy if you /// just want to load all of the device managers at once, versus one-by-one by hand. Note /// that this function can be slow enough to delay startup and GUIs, and might best be run on /// a thread or Task. /// </summary> public async Task AddAllSubtypeManagers() { _bpLogger.Debug("Searching for Subtype Managers"); // Assume all assemblies will be named as "B******g.Server.Managers.*.dll" because that's // what we do now anyways. foreach (var path in _managerSearchDirs) { _bpLogger.Debug($"Searching {path} for Subtype Managers"); foreach (var dll in Directory.GetFiles(path, "B******g.Server.Managers.*.dll")) { _bpLogger.Debug($"Loading {dll}"); try { Assembly.LoadFile(dll); } catch (Exception) { _bpLogger.Warn($"Loading {dll} for subtype manager addition failed."); } } } // Within the assemblies, extract all subclasses of DeviceSubtypeManager, which are what // we need to add here. Make sure we don't add abstract subclasses of // DeviceSubtypeManager, liked TimedScanDeviceSubtypeManager. var types = from a in AppDomain.CurrentDomain.GetAssemblies() from t in a.GetTypes() where t.IsSubclassOf(typeof(DeviceSubtypeManager)) && !t.IsAbstract select t; // Add new instances of all found types to the DeviceManager. All DeviceSubtypeManager // classes should take a IButtplugLogManager as a constructor parameter. If not (which // will at least be the case for BluetoothDeviceManager, which is a base class), log and continue. foreach (var t in types) { try { _bpLogger.Info($"Adding subtype manager {t.Name} as part of AddAllSubtypeManagers."); var mgr = (IDeviceSubtypeManager)Activator.CreateInstance(t, _bpLogManager); AddDeviceSubtypeManager(mgr); } catch (MissingMethodException) { _bpLogger.Info($"Subtype manager {t.Name} not added, missing appropriate constructor."); } } }
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)); }
public static Error LogWarnMsg(this IButtplugLog logger, uint aId, Error.ErrorClass aCode, string aMsg) { logger.Warn(aMsg); return(new Error(aMsg, aCode, aId)); }
/// <summary> /// Logs an Error B******g message as a warning level log message. /// </summary> /// <param name="logger"></param> /// <param name="warning"></param> public static void LogWarnMsg(this IButtplugLog logger, Error warning) { logger.Warn(warning.ErrorMessage); }