// 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(InsteonDeviceCommands command, byte value)
        {
            InsteonDeviceCommands latchedPendingCommand;

            lock (pendingEvent)
            {
                if (pendingCommand == null)
                {
                    pendingCommand = command;
                    pendingValue   = value;
                    pendingRetry   = 0;
                    return;
                }
                latchedPendingCommand = pendingCommand.Value;
            }

            // block current thread if a command is pending
            Log.WriteLine("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
                Log.WriteLine("WARNING: Device {0} unblocking command {1} for pending command {2}", Address.ToString(), command.ToString(), latchedPendingCommand.ToString());
            }

            WaitAndSetPendingCommand(command, value); // try again
        }
Ejemplo n.º 2
0
 /// <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>
 public void Command(InsteonDeviceCommands command)
 {
     if (command == InsteonDeviceCommands.On)
         this.Command(command, 0xFF);
     else
         this.Command(command, 0x00);
 }
 /// <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>
 public void Command(InsteonDeviceCommands command, byte value)
 {
     if (!TryCommand(command, value))
     {
         throw new IOException(string.Format("Failed to send command '{0}' for device '{1}'", command.ToString(), Address.ToString()));
     }
 }
 /// <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>
 public void Command(InsteonDeviceCommands command)
 {
     if (command == InsteonDeviceCommands.On)
     {
         Command(command, 0xFF);
     }
     else
     {
         Command(command, 0x00);
     }
 }
 /// <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>
 public bool TryCommand(InsteonDeviceCommands command)
 {
     if (command == InsteonDeviceCommands.On)
     {
         return(TryCommand(command, 0xFF));
     }
     else
     {
         return(TryCommand(command, 0x00));
     }
 }
        private bool TryCommandInternal(InsteonDeviceCommands command, byte value)
        {
            byte[] message = GetStandardMessage(Address, (byte)command, value);
            Log.WriteLine("Device {0} Command(command:{1}, value:{2:X2})", Address.ToString(), command.ToString(), value);

            EchoStatus status = network.Messenger.TrySend(message);

            if (status == EchoStatus.ACK)
            {
                ackTimer.Change(Constants.deviceAckTimeout, Timeout.Infinite); // start ACK timeout timer
                return(true);
            }
            else
            {
                ClearPendingCommand();
                return(false);
            }
        }
        // invoked when a pending command times out
        private void PendingCommandTimerCallback(object state)
        {
            ackTimer.Change(Timeout.Infinite, Timeout.Infinite); // stop ACK timeout timer

            bool retry = false;
            InsteonDeviceCommands command = InsteonDeviceCommands.On;
            byte value      = 0;
            int  retryCount = 0;

            lock (pendingEvent)
            {
                if (pendingCommand == null)
                {
                    return;
                }

                pendingRetry += 1;
                if (pendingRetry <= Constants.deviceCommandRetries)
                {
                    retry      = true;
                    value      = pendingValue;
                    retryCount = pendingRetry;
                }
                else
                {
                    retry          = false;
                    command        = pendingCommand.Value;
                    pendingCommand = null;
                    pendingValue   = 0;
                    pendingEvent.Set(); // unblock any thread that may be waiting on the pending command
                }
            }

            if (retry)
            {
                Log.WriteLine("WARNING: Device {0} Command {1} timed out, retry {2} of {3}...", Address.ToString(), command, retryCount, Constants.deviceCommandRetries);
                TryCommandInternal(command, value);
            }
            else
            {
                Log.WriteLine("ERROR: Device {0} Command {1} timed out", Address.ToString(), command);
                OnDeviceCommandTimeout();
            }
        }
 // if a command is pending determines whether the current message completes the pending command
 private void PendingCommandAck(InsteonMessage message)
 {
     lock (pendingEvent)
     {
         if (pendingCommand != null)
         {
             int cmd1 = message.Properties[PropertyKey.Cmd1];
             if (Enum.IsDefined(typeof(InsteonDeviceCommands), cmd1))
             {
                 InsteonDeviceCommands command = (InsteonDeviceCommands)cmd1;
                 if (pendingCommand.Value == command)
                 {
                     pendingCommand = null;
                     pendingValue   = 0;
                     ackTimer.Change(Timeout.Infinite, Timeout.Infinite); // stop ACK timeout timer
                     pendingEvent.Set();                                  // unblock any thread that may be waiting on the pending command
                 }
             }
         }
     }
 }
        /// <summary>
        /// Gets a value that indicates the on-level of the device.
        /// </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 TryGetOnLevel(out byte value)
        {
            InsteonDeviceCommands command = InsteonDeviceCommands.StatusRequest;

            WaitAndSetPendingCommand(command, 0);
            Log.WriteLine("Device {0} GetOnLevel", Address.ToString());
            byte[] message = GetStandardMessage(Address, (byte)command, 0);
            Dictionary <PropertyKey, int> properties;
            EchoStatus status = network.Messenger.TrySendReceive(message, true, 0x50, out properties); // on-level returned in cmd2 of ACK

            if (status == EchoStatus.ACK && properties != null)
            {
                value = (byte)properties[PropertyKey.Cmd2];
                Log.WriteLine("Device {0} GetOnLevel returning {1:X2}", Address.ToString(), value);
                return(true);
            }
            else
            {
                ClearPendingCommand();
                value = 0;
                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>
 public bool TryCommand(InsteonDeviceCommands command, byte value)
 {
     WaitAndSetPendingCommand(command, value);
     return(TryCommandInternal(command, value));
 }
Ejemplo n.º 11
0
 /// <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>
 public bool TryCommand(InsteonDeviceCommands command)
 {
     if (command == InsteonDeviceCommands.On)
         return this.TryCommand(command, 0xFF);
     else
         return this.TryCommand(command, 0x00);
 }
Ejemplo n.º 12
0
        // 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(InsteonDeviceCommands command, byte value)
        {
            InsteonDeviceCommands latchedPendingCommand;

            lock (this.pendingEvent)
            {
                if (this.pendingCommand == null)
                {
                    this.pendingCommand = command;
                    this.pendingValue = value;
                    this.pendingRetry = 0;
                    return;
                }
                latchedPendingCommand = this.pendingCommand.Value;
            }

            // block current thread if a command is pending
            Log.WriteLine("Device {0} blocking command {1} for pending command {2}", this.Address.ToString(), command.ToString(), latchedPendingCommand.ToString());
            this.pendingEvent.Reset();
            if (!this.pendingEvent.WaitOne(Constants.deviceAckTimeout)) // wait at most deviceAckTimeout seconds
            {
                this.ClearPendingCommand(); // break deadlock and warn
                Log.WriteLine("WARNING: Device {0} unblocking command {1} for pending command {2}", this.Address.ToString(), command.ToString(), latchedPendingCommand.ToString());
            }

            this.WaitAndSetPendingCommand(command, value); // try again
        }
Ejemplo n.º 13
0
        private bool TryCommandInternal(InsteonDeviceCommands command, byte value)
        {
            var message = GetStandardMessage(this.Address, (byte)command, value);
            Log.WriteLine("Device {0} Command(command:{1}, value:{2:X2})", this.Address.ToString(), command.ToString(), value);

            var status = this.network.Messenger.TrySend(message);
            if (status == EchoStatus.ACK)
            {
                this.ackTimer.Change(Constants.deviceAckTimeout, Timeout.Infinite); // start ACK timeout timer   
                return true;
            }
            else
            {
                this.ClearPendingCommand();
                return false;
            }
        }
Ejemplo n.º 14
0
 /// <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>
 public bool TryCommand(InsteonDeviceCommands command, byte value)
 {
     this.WaitAndSetPendingCommand(command, value);
     return this.TryCommandInternal(command, value);
 }
Ejemplo n.º 15
0
 private bool TryParseCommand(string idText, out InsteonDeviceCommands command)
 {
     return(Enum.TryParse(idText, out command));
 }
Ejemplo n.º 16
0
        public bool TryCommand(InsteonAddress address, InsteonDeviceCommands command, byte data)
        {
            var device = Network.Devices.FirstOrDefault(a => a.Address.Equals(address));

            return(device?.TryCommand(command, data) ?? false);
        }
Ejemplo n.º 17
0
 /// <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>
 public void Command(InsteonDeviceCommands command, byte value)
 {
     if (!this.TryCommand(command, value))
         throw new IOException(string.Format("Failed to send command '{0}' for device '{1}'", command.ToString(), this.Address.ToString()));
 }