/// <summary>
        /// Websocket Message Received event handler. Either tries to match incoming messages as
        /// replies to messages we've sent, or fires an event related to an incoming event, like
        /// device additions/removals, log messages, etc.
        /// </summary>
        /// <param name="aSender">Object sending the open event, unused.</param>
        /// <param name="aArgs">Event parameters, including the data received.</param>
        private void MessageReceivedHandler(object aSender, WebSocket4Net.MessageReceivedEventArgs aArgs)
        {
            var msgs = Deserialize(aArgs.Message);

            foreach (var msg in msgs)
            {
                if (msg.Id > 0 && _waitingMsgs.TryRemove(msg.Id, out TaskCompletionSource <ButtplugMessage> queued))
                {
                    queued.TrySetResult(msg);
                    continue;
                }

                switch (msg)
                {
                case Log l:
                    _owningDispatcher.Send(_ =>
                    {
                        Log?.Invoke(this, new LogEventArgs(l));
                    }, null);
                    break;

                case DeviceAdded d:
                    var dev = new ButtplugClientDevice(d);
                    _devices.AddOrUpdate(d.DeviceIndex, dev, (idx, old) => dev);
                    _owningDispatcher.Send(_ =>
                    {
                        DeviceAdded?.Invoke(this, new DeviceEventArgs(dev, DeviceEventArgs.DeviceAction.ADDED));
                    }, null);
                    break;

                case DeviceRemoved d:
                    if (_devices.TryRemove(d.DeviceIndex, out ButtplugClientDevice oldDev))
                    {
                        _owningDispatcher.Send(_ =>
                        {
                            DeviceRemoved?.Invoke(this, new DeviceEventArgs(oldDev, DeviceEventArgs.DeviceAction.REMOVED));
                        }, null);
                    }

                    break;

                case ScanningFinished sf:
                    _owningDispatcher.Send(_ =>
                    {
                        ScanningFinished?.Invoke(this, new ScanningFinishedEventArgs(sf));
                    }, null);
                    break;

                case Error e:
                    _owningDispatcher.Send(_ =>
                    {
                        ErrorReceived?.Invoke(this, new ErrorEventArgs(e));
                    }, null);
                    break;
                }
            }
        }
        /// <summary>
        /// Sends a DeviceMessage (e.g. <see cref="VibrateCmd"/> or <see cref="LinearCmd"/>). Handles
        /// constructing some parts of the message for the user.
        /// </summary>
        /// <param name="aDevice">The device to be controlled by the message.</param>
        /// <param name="aDeviceMsg">The device message (Id and DeviceIndex will be overriden).</param>
        /// <returns>
        /// <see cref="Ok"/> message on success, <see cref="Error"/> message with error info otherwise.
        /// </returns>
        public Task <ButtplugMessage> SendDeviceMessage(ButtplugClientDevice aDevice, ButtplugDeviceMessage aDeviceMsg)
        {
            if (!_devices.TryGetValue(aDevice.Index, out ButtplugClientDevice dev))
            {
                var t = new Task <ButtplugMessage>(() => new Error("Device not available.", Error.ErrorClass.ERROR_DEVICE, ButtplugConsts.SystemMsgId));
                t.Start(TaskScheduler.Default);
                return(t);
            }

            if (!dev.AllowedMessages.ContainsKey(aDeviceMsg.GetType().Name))
            {
                var t = new Task <ButtplugMessage>(() => new Error("Device does not accept message type: " + aDeviceMsg.GetType().Name, Error.ErrorClass.ERROR_DEVICE, ButtplugConsts.SystemMsgId));
                t.Start(TaskScheduler.Default);
                return(t);
            }

            aDeviceMsg.DeviceIndex = aDevice.Index;
            return(SendMessage(aDeviceMsg));
        }
        public void Connect(Uri aURL, bool aIgnoreSSLErrors = false)
        {
            if (_ws != null && (_ws.State == WebSocketState.Connecting || _ws.State == WebSocketState.Open))
            {
                throw new InvalidOperationException("Already connected!");
            }

            _bpLogger.Trace("Opening connection");
            _ws = new WebSocket(aURL.ToString());
            _waitingMsgs.Clear();
            _devices.Clear();
            _counter = 1;

            _connectedOrFailed = new TaskCompletionSource <bool>();
            _disconnected      = new TaskCompletionSource <bool>();

            _ws.Opened += OpenedHandler;
            _ws.Closed += ClosedHandler;
            _ws.Error  += ErrorHandler;

            if (aIgnoreSSLErrors)
            {
                _ws.Security.AllowNameMismatchCertificate = true;
                _ws.Security.AllowUnstrustedCertificate   = true;
                _ws.Security.AllowCertificateChainErrors  = true;
            }

            _ws.Open();

            _connecting = true;
            _connectedOrFailed.Task.Wait();
            _connecting = false;

            if (_ws.State != WebSocketState.Open)
            {
                throw new Exception("Connection failed!");
            }
            _bpLogger.Trace("Connected");

            _ws.MessageReceived += MessageReceivedHandler;

            var res = SendMessage(new RequestServerInfo(_clientName)).Result;

            switch (res)
            {
            case ServerInfo si:
                if (si.MaxPingTime > 0)
                {
                    _pingTimer = new Timer(OnPingTimer, null, 0, Convert.ToInt32(Math.Round(((double)si.MaxPingTime) / 2, 0)));
                }

                if (si.MessageVersion < 1)
                {
                    throw new Exception("B******g Server's schema version (" + si.MessageVersion +
                                        ") is less than the client's (" + 1 +
                                        "). A newer server is required.");
                }

                // Get full device list and populate internal list
                var resp = SendMessage(new RequestDeviceList()).Result;
                if ((resp as DeviceList)?.Devices == null)
                {
                    if (resp is Error)
                    {
                        _owningDispatcher.Send(_ =>
                        {
                            ErrorReceived?.Invoke(this, new ErrorEventArgs(resp as Error));
                        }, null);
                    }

                    return;
                }

                foreach (var d in (resp as DeviceList).Devices)
                {
                    if (_devices.ContainsKey(d.DeviceIndex))
                    {
                        continue;
                    }

                    var device = new ButtplugClientDevice(d);
                    if (_devices.TryAdd(d.DeviceIndex, device))
                    {
                        _owningDispatcher.Send(_ =>
                        {
                            DeviceAdded?.Invoke(this, new DeviceEventArgs(device, DeviceEventArgs.DeviceAction.ADDED));
                        }, null);
                    }
                }

                break;

            case Error e:
                throw new Exception(e.ErrorMessage);

            default:
                throw new Exception("Unexpecte message returned: " + res.GetType());
            }
        }
Beispiel #4
0
 /// <summary>
 /// Initializes a new instance of the <see cref="DeviceEventArgs"/> class.
 /// </summary>
 /// <param name="aDevice">The B******g Device.</param>
 /// <param name="aAction">The action of the event.</param>
 public DeviceEventArgs(ButtplugClientDevice aDevice, DeviceAction aAction)
 {
     Device = aDevice;
     Action = aAction;
 }