Beispiel #1
0
        internal IObservable <Command> ReceiveNodeEvents(byte nodeID, Command command)
        {
            if (nodeID == 0)
            {
                throw new ArgumentOutOfRangeException(nameof(nodeID), nodeID, "nodeID must be greater than 0");
            }
            if (command == null)
            {
                throw new ArgumentNullException(nameof(command));
            }

            var commandPipeline = Messages
                                  // decode the response
                                  .Select(message => Decode(message, hasCallbackID: false))
                                  // we only want events (no responses)
                                  .OfType <ControllerEvent>()
                                  // after SendData controler will respond with ApplicationCommandHandler
                                  .Where(@event => Equals(@event.Function, Function.ApplicationCommandHandler))
                                  // deserialize the received payload to a NodeResponse
                                  .Select(@event => @event.Payload.Deserialize <NodeResponse>())
                                  // verify if the responding node is the correct one
                                  .Where(response => response.NodeID == nodeID)
                                  // and select the command
                                  .Select(response => response.Command);

            // check if the command contains an ecapsulated MultiChannelCommand
            if (Encapsulation.Flatten(command).OfType <MultiChannelCommand>().Any())
            {
                // extract MultiChannelCommand
                var multiChannelRequestCommand = Encapsulation.Flatten(command).OfType <MultiChannelCommand>().Single();

                commandPipeline = commandPipeline
                                  // extract the MultiChannelCommand from the response
                                  .Select(reply => Encapsulation.Flatten(reply).OfType <MultiChannelCommand>().SingleOrDefault())
                                  // verify if present
                                  .Where(reply => reply != null)
                                  // verify if the MultiChannelCommand is the correct one
                                  .Where(reply => reply.CommandClass == multiChannelRequestCommand.CommandClass && reply.CommandID == multiChannelRequestCommand.CommandID)
                                  // verify if the endpoint is the correct one
                                  .Where(reply => reply.SourceEndpointID == multiChannelRequestCommand.SourceEndpointID);
            }

            // get the most inner command
            var requestCommand = Encapsulation.Flatten(command).Last();

            return(commandPipeline
                   // select the inner most command
                   .Select(reply => Encapsulation.Flatten(reply).Last())
                   // verify if the response conmmand is the correct one
                   .Where(reply => reply.CommandClass == requestCommand.CommandClass && reply.CommandID == requestCommand.CommandID));
        }
Beispiel #2
0
        // NodeCommand, with return value. Request followed by:
        // 1) a response from the controller
        // 2) a event from the controller: command deliverd at node)
        // 3) a event from the node: return value
        internal async Task <Command> Send(byte nodeID, Command command, byte responseCommandID, CancellationToken cancellationToken = default)
        {
            if (nodeID == 0)
            {
                throw new ArgumentOutOfRangeException(nameof(nodeID), nodeID, "nodeID must be greater than 0");
            }
            if (command == null)
            {
                throw new ArgumentNullException(nameof(command));
            }
            if (responseCommandID == 0)
            {
                throw new ArgumentOutOfRangeException(nameof(responseCommandID), responseCommandID, "responseCommandID must be greater than 0");
            }

            // generate new callback
            var callbackID = GetNextCallbackID();

            // create the request
            var nodeRequest       = new NodeRequest(nodeID, command);
            var controllerRequest = new ControllerRequest(Function.SendData, nodeRequest.Serialize());

            var responsePipeline = Messages
                                   // decode the response
                                   .Select(message => Decode(message, hasCallbackID: false))
                                   // we only want responses (no events)
                                   .OfType <ControllerResponse>()
                                   // verify matching function
                                   .Where(response => Equals(response.Function, controllerRequest.Function))
                                   // unknown what the 0x01 byte means, probably: ready, finished, OK
                                   .Where(response => response.Payload[0] == 0x01);


            var eventPipeline = Messages
                                // decode the response
                                .Select(message => Decode(message, hasCallbackID: true))
                                // we only want events (no responses)
                                .OfType <ControllerEvent>()
                                // verify matching function
                                .Where(@event => Equals(@event.Function, controllerRequest.Function))
                                // verify matching callback
                                .Where(@event => Equals(@event.CallbackID, callbackID))
                                // deserialize the received payload to a NodeCommandCompleted
                                .Select(@event => @event.Payload.Deserialize <NodeCommandCompleted>())
                                // verify the state
                                .Verify(completed => completed.TransmissionState == TransmissionState.CompleteOK, completed => new TransmissionException(completed.TransmissionState));

            var commandPipeline = Messages
                                  // wait until the response pipeline has finished
                                  .SkipUntil(responsePipeline)
                                  // wait until the event pipeline has finished
                                  .SkipUntil(eventPipeline)
                                  // decode the response
                                  .Select(message => Decode(message, hasCallbackID: false))
                                  // we only want events (no responses)
                                  .OfType <ControllerEvent>()
                                  // after SendData controler will respond with ApplicationCommandHandler
                                  .Where(@event => Equals(@event.Function, Function.ApplicationCommandHandler))
                                  // deserialize the received payload to a NodeResponse
                                  .Select(@event => @event.Payload.Deserialize <NodeResponse>())
                                  // verify if the responding node is the correct one
                                  .Where(response => response.NodeID == nodeID)
                                  // and select the command
                                  .Select(response => response.Command);

            // check if the command contains an ecapsulated MultiChannelCommand
            if (Encapsulation.Flatten(command).OfType <MultiChannelCommand>().Any())
            {
                // extract MultiChannelCommand
                var multiChannelRequestCommand = Encapsulation.Flatten(command).OfType <MultiChannelCommand>().Single();

                // insert MultiChannel filtering
                commandPipeline = commandPipeline
                                  // extract the MultiChannelCommand from the response
                                  .Select(reply => Encapsulation.Flatten(reply).OfType <MultiChannelCommand>().Single())
                                  // verify if the MultiChannelCommand is the correct one
                                  .Where(reply => reply.CommandClass == multiChannelRequestCommand.CommandClass && reply.CommandID == multiChannelRequestCommand.CommandID)
                                  // verify if the endpoint the correct one
                                  .Where(reply => reply.SourceEndpointID == multiChannelRequestCommand.TargetEndpointID);
            }

            // get the most inner command
            var requestCommand = Encapsulation.Flatten(command).Last();

            commandPipeline = commandPipeline
                              // select the inner most command
                              .Select(reply => Encapsulation.Flatten(reply).Last())
                              // verify if the response conmmand is the correct one
                              .Where(reply => reply.CommandClass == requestCommand.CommandClass && reply.CommandID == responseCommandID);

            return(await Send(Encode(controllerRequest, callbackID), commandPipeline, cancellationToken));
        }