Esempio n. 1
0
        /// <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));
        }
Esempio n. 3
0
        /// <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.");
                }
            }
        }
Esempio n. 4
0
        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));
        }
Esempio n. 5
0
 public static Error LogWarnMsg(this IButtplugLog logger, uint aId, Error.ErrorClass aCode, string aMsg)
 {
     logger.Warn(aMsg);
     return(new Error(aMsg, aCode, aId));
 }
Esempio n. 6
0
 /// <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);
 }