internal RequestMessage Encode(ControllerRequest command, byte?callbackID) { if (command == null) { throw new ArgumentNullException(nameof(command)); } // create writer to serialize te request using (var writer = new PayloadWriter()) { // write the function writer.WriteByte((byte)command.Function); // does the command has payload? if (command.Payload != null) { // yes, so write the payload writer.WriteObject(command.Payload); } if (callbackID != null) { // write the callback writer.WriteByte(callbackID.Value); } // create a hostmessage, use the serialized payload return(new RequestMessage(writer.ToPayload())); } }
public static async Task <NodeInfo> SendRequestNodeInfo(this ZWaveChannel channel, byte nodeID, CancellationToken cancellationToken = default) { if (channel == null) { throw new ArgumentNullException(nameof(channel)); } if (nodeID == 0) { throw new ArgumentOutOfRangeException(nameof(nodeID), nodeID, "nodeID must be greater than 0"); } var command = new ControllerRequest(Function.RequestNodeInfo, new Payload(nodeID)); var request = channel.Encode(command, null); var responsePipeline = channel.Messages // decode the response .Select(message => channel.Decode(message, hasCallbackID: false)) // we only want responses (no events) .OfType <ControllerResponse>() // verify matching function .Where(response => Equals(response.Function, command.Function)) // unknown what the 0x01 byte means, probably: ready, finished, OK .Where(response => response.Payload[0] == 0x01); var pipeline = channel.Messages // wait until the response pipeline has finished .SkipUntil(responsePipeline) // decode the response .Select(message => channel.Decode(message, hasCallbackID: false)) // we only want events (no responses) .OfType <ControllerEvent>() // verify matching function .Where(@event => Equals(@event.Function, Function.ApplicationUpdate)) // deserialize the received payload to a NodeUpdate .Select(@event => @event.Payload.Deserialize <NodeUpdate>()); // send request var nodeUpdate = await channel.Send(request, pipeline, cancellationToken); // verify the state if (nodeUpdate.State != NodeUpdateState.InfoReceived) { throw new OperationFailedException($"RequestNodeInfo failed with state: {nodeUpdate.State}"); } // return the data return(nodeUpdate.Info); }
// NodeCommand, no return value. Request followed by: // 1) a response from the controller // 2) a event from the controller: command deliverd at node) internal async Task Send(byte nodeID, Command command, 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)); } // 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 pipeline = Messages // wait until the response pipeline has finished .SkipUntil(responsePipeline) // 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 mathing 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)); await Send(Encode(controllerRequest, callbackID), pipeline, cancellationToken); }
// ControllerRequest: request followed by one response from the controller internal async Task <T> Send <T>(ControllerRequest request, CancellationToken cancellationToken = default) where T : IPayloadSerializable, new() { if (request == null) { throw new ArgumentNullException(nameof(request)); } // create the response pipeline var pipeline = 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, request.Function)) // and finally deserialize the received payload .Select(response => response.Payload.Deserialize <T>()); return(await Send(Encode(request, null), pipeline, cancellationToken)); }
public static async Task <NeighborUpdateStatus> SendRequestNeighborUpdate(this ZWaveChannel channel, byte nodeID, IProgress <NeighborUpdateStatus> progress, CancellationToken cancellationToken = default) { if (channel == null) { throw new ArgumentNullException(nameof(channel)); } if (nodeID == 0) { throw new ArgumentOutOfRangeException(nameof(nodeID), nodeID, "nodeID must be greater than 0"); } var responseTimeout = TimeSpan.FromSeconds(5); var callbackID = ZWaveChannel.GetNextCallbackID(); var command = new ControllerRequest(Function.RequestNodeNeighborUpdate, new Payload(nodeID)); var request = channel.Encode(command, callbackID); var pipeline = channel.Messages // decode the response .Select(message => channel.Decode(message, hasCallbackID: true)) // we only want events (no responses) .OfType <ControllerEvent>() // verify matching function .Where(@event => Equals(@event.Function, command.Function)) // verify mathing callback (can be null) .Where(@event => Equals(@event.CallbackID, callbackID)) // deserialize the received payload .Select(@event => @event.Payload.Deserialize <Payload>()) // report progress .Do(payload => progress?.Report((NeighborUpdateStatus)payload[0])) // and check for final state .Where(payload => (NeighborUpdateStatus)payload[0] == NeighborUpdateStatus.Done || (NeighborUpdateStatus)payload[0] == NeighborUpdateStatus.Failed); // send request var response = await channel.Send(request, pipeline, cancellationToken); // return the status of the final response return((NeighborUpdateStatus)response[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)); }