/// <summary> /// Sends an INSTEON command to the device. /// </summary> /// <remarks> /// This is a non-blocking method that sends an INSTEON message to the target device and returns immediately (as long as another command is not already pending for the device). /// Only one command can be pending to an INSTEON device at a time. This method will block if a second command is sent while a first command is still pending. /// The <see cref="DeviceStatusChanged">DeviceStatusChanged</see> event will be invoked if the command is successful. /// The <see cref="DeviceCommandTimeout">DeviceCommandTimeout</see> event will be invoked if the device does not respond within the expected timeout period. /// </remarks> /// <param name="command">Specifies the INSTEON device command to be invoked.</param> /// <param name="value">A parameter value required by some commands.</param> internal void Command(InsteonDirectCommands command, byte value) { if (!TryCommand(command, value)) { throw new IOException($"Failed to send command '{command}' for device '{Address}'"); } }
// blocks the current thread if a command is pending, then sets the current command as the pending command (note does not apply to all commands) private void WaitAndSetPendingCommand(InsteonDirectCommands command, byte value) { InsteonDirectCommands latchedPendingCommand; lock (pendingEvent) { if (pendingCommand == null) { pendingCommand = command; pendingValue = value; pendingRetry = 0; return; } latchedPendingCommand = pendingCommand.Value; } // block current thread if a command is pending logger.DebugFormat("Device {0} blocking command {1} for pending command {2}", Address.ToString(), command.ToString(), latchedPendingCommand.ToString()); pendingEvent.Reset(); if (!pendingEvent.WaitOne(Constants.deviceAckTimeout)) // wait at most deviceAckTimeout seconds { ClearPendingCommand(); // break deadlock and warn logger.WarnFormat("Device {0} unblocking command {1} for pending command {2}", Address.ToString(), command.ToString(), latchedPendingCommand.ToString()); } WaitAndSetPendingCommand(command, value); // try again }
private bool TryCommandInternal(InsteonDirectCommands command, byte value) { var message = GetStandardMessage(Address, (byte)command, value); logger.DebugFormat("Device {0} Command(command:{1}, value:{2:X2})", Address.ToString(), command.ToString(), value); var status = network.Messenger.TrySend(message); if (status == EchoStatus.ACK) { ackTimer.Change(Constants.deviceAckTimeout, Timeout.Infinite); // start ACK timeout timer ClearPendingCommand(); return(true); } ClearPendingCommand(); return(false); }
/// <summary> /// Gets a value that indicates the on-level of the device. (Status) /// </summary> /// <returns> /// A value indicating the on-level of the device. For a dimmer a value between 0 and 255 will be returned. For a non-dimmer a value 0 or 255 will be returned. /// </returns> /// <remarks> /// This is a blocking method that sends an INSTEON message to the target device and waits for a reply, or until the device command times out. /// </remarks> public bool TryGetStatus(out byte value, byte commandValue = 0x0) { const InsteonDirectCommands command = InsteonDirectCommands.StatusRequest; WaitAndSetPendingCommand(command, commandValue); logger.DebugFormat("Device {0} GetOnLevel", Address.ToString()); var message = GetStandardMessage(Address, (byte)command, commandValue); Dictionary <PropertyKey, int> properties; var status = network.Messenger.TrySendReceive(message, true, (byte)InsteonModemSerialCommand.StandardMessage, null, out properties); // on-level returned in cmd2 of ACK if (status == EchoStatus.ACK && properties != null) { value = (byte)properties[PropertyKey.Cmd2]; logger.DebugFormat("Device {0} GetOnLevel returning {1:X2}", Address.ToString(), value); ClearPendingCommand(); return(true); } ClearPendingCommand(); value = 0; return(false); }
private bool TryCommandInternal(InsteonDirectCommands command, byte value) { var message = GetStandardMessage(Address, (byte)command, value); logger.DebugFormat("Device {0} Command(command:{1}, value:{2:X2})", Address.ToString(), command.ToString(), value); var status = network.Messenger.TrySend(message); if (status == EchoStatus.ACK) { ackTimer.Change(Constants.deviceAckTimeout, Timeout.Infinite); // start ACK timeout timer ClearPendingCommand(); return true; } ClearPendingCommand(); return false; }
/// <summary> /// Sends an INSTEON command to the device. /// </summary> /// <param name="command">Specifies the INSTEON device command to be invoked.</param> /// <param name="value">A parameter value required by some commands.</param> /// <remarks> /// This method does not throw an exception. /// This is a non-blocking method that sends an INSTEON message to the target device and returns immediately (as long as another command is not already pending for the device). /// Only one command can be pending to an INSTEON device at a time. This method will block if a second command is sent while a first command is still pending. /// The <see cref="DeviceStatusChanged">DeviceStatusChanged</see> event will be invoked if the command is successful. /// The <see cref="DeviceCommandTimeout">DeviceCommandTimeout</see> event will be invoked if the device does not respond within the expected timeout period. /// </remarks> internal bool TryCommand(InsteonDirectCommands command, byte value) { WaitAndSetPendingCommand(command, value); return TryCommandInternal(command, value); }
/// <summary> /// Sends an INSTEON command to the device. /// </summary> /// <param name="command">Specifies the INSTEON device command to be invoked.</param> /// <param name="value">A parameter value required by some commands.</param> /// <remarks> /// This method does not throw an exception. /// This is a non-blocking method that sends an INSTEON message to the target device and returns immediately (as long as another command is not already pending for the device). /// Only one command can be pending to an INSTEON device at a time. This method will block if a second command is sent while a first command is still pending. /// The <see cref="DeviceStatusChanged">DeviceStatusChanged</see> event will be invoked if the command is successful. /// The <see cref="DeviceCommandTimeout">DeviceCommandTimeout</see> event will be invoked if the device does not respond within the expected timeout period. /// </remarks> internal bool TryCommand(InsteonDirectCommands command, byte value) { WaitAndSetPendingCommand(command, value); return(TryCommandInternal(command, value)); }