public void PLCLimitSwitchStatusEnumTest()
        {
            //GetFromByte Tests
            Assert.AreEqual(PLCLimitSwitchStatusEnum.UNDEFINED, PLCLimitSwitchStatusConversionHelper.GetFromByte(0x0));
            Assert.AreEqual(PLCLimitSwitchStatusEnum.WITHIN_SAFE_LIMITS, PLCLimitSwitchStatusConversionHelper.GetFromByte(0x1));
            Assert.AreEqual(PLCLimitSwitchStatusEnum.WITHIN_WARNING_LIMITS, PLCLimitSwitchStatusConversionHelper.GetFromByte(0x2));

            //ConvertToByte Tests
            Assert.AreEqual(0x0, PLCLimitSwitchStatusConversionHelper.ConvertToByte(PLCLimitSwitchStatusEnum.UNDEFINED));
            Assert.AreEqual(0x1, PLCLimitSwitchStatusConversionHelper.ConvertToByte(PLCLimitSwitchStatusEnum.WITHIN_SAFE_LIMITS));
            Assert.AreEqual(0x2, PLCLimitSwitchStatusConversionHelper.ConvertToByte(PLCLimitSwitchStatusEnum.WITHIN_WARNING_LIMITS));
        }
        public bool ProcessRequest(NetworkStream ActiveClientStream, byte[] query)
        {
            int ExpectedSize = query[0] + (256 * query[1]);

            if (query.Length != ExpectedSize)
            {
                throw new ArgumentException(
                          "ScaleModelPLCDriverController read a package specifying a size [" + ExpectedSize.ToString() + "], but the actual size was different [" + query.Length + "]."
                          );
            }

            byte CommandQueryTypeAndExpectedResponseStatus = query[2];
            byte CommandQueryTypeByte       = (byte)(CommandQueryTypeAndExpectedResponseStatus & 0x3F);
            byte ExpectedResponseStatusByte = (byte)(CommandQueryTypeAndExpectedResponseStatus >> 6);

            PLCCommandAndQueryTypeEnum        CommandQueryTypeEnum       = PLCCommandAndQueryTypeConversionHelper.GetFromByte(CommandQueryTypeByte);
            PLCCommandResponseExpectationEnum ExpectedResponseStatusEnum = PLCCommandResponseExpectationConversionHelper.GetFromByte(ExpectedResponseStatusByte);

            byte[] FinalResponseContainer;

            if (ExpectedResponseStatusEnum == PLCCommandResponseExpectationEnum.FULL_RESPONSE)
            {
                FinalResponseContainer = new byte[]
                {
                    0x13, 0x0,
                    0x0,
                    0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
                    0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0
                };

                switch (CommandQueryTypeEnum)
                {
                case PLCCommandAndQueryTypeEnum.TEST_CONNECTION:
                {
                    FinalResponseContainer[3] = 0x1;
                    FinalResponseContainer[2] = 0x1;
                    break;
                }

                case PLCCommandAndQueryTypeEnum.GET_CURRENT_AZEL_POSITIONS:
                {
                    double TestAzimuth   = 180.0;
                    double TestElevation = 42.0;

                    Array.Copy(BitConverter.GetBytes(TestAzimuth), 0, FinalResponseContainer, 3, 8);
                    Array.Copy(BitConverter.GetBytes(TestElevation), 0, FinalResponseContainer, 11, 8);

                    FinalResponseContainer[2] = 0x1;

                    break;
                }

                case PLCCommandAndQueryTypeEnum.GET_CURRENT_LIMIT_SWITCH_STATUSES:
                {
                    PLCLimitSwitchStatusEnum StatusAzimuthUnderRotation   = PLCLimitSwitchStatusEnum.WITHIN_SAFE_LIMITS;
                    PLCLimitSwitchStatusEnum StatusAzimuthOverRotation    = PLCLimitSwitchStatusEnum.WITHIN_SAFE_LIMITS;
                    PLCLimitSwitchStatusEnum StatusElevationUnderRotation = PLCLimitSwitchStatusEnum.WITHIN_SAFE_LIMITS;
                    PLCLimitSwitchStatusEnum StatusElevationOverRotation  = PLCLimitSwitchStatusEnum.WITHIN_SAFE_LIMITS;

                    int PacketSum =
                        PLCLimitSwitchStatusConversionHelper.ConvertToByte(StatusElevationOverRotation)
                        + (PLCLimitSwitchStatusConversionHelper.ConvertToByte(StatusElevationUnderRotation) * 0x4)
                        + (PLCLimitSwitchStatusConversionHelper.ConvertToByte(StatusAzimuthOverRotation) * 0x10)
                        + (PLCLimitSwitchStatusConversionHelper.ConvertToByte(StatusAzimuthUnderRotation) * 0x40)
                    ;

                    FinalResponseContainer[3] = (byte)PacketSum;
                    FinalResponseContainer[2] = 0x1;

                    break;
                }

                case PLCCommandAndQueryTypeEnum.GET_CURRENT_SAFETY_INTERLOCK_STATUS:
                {
                    FinalResponseContainer[3] = PLCSafetyInterlockStatusConversionHelper.ConvertToByte(PLCSafetyInterlockStatusEnum.LOCKED);
                    FinalResponseContainer[2] = 0x1;
                    break;
                }

                default:
                {
                    throw new ArgumentException("Invalid PLCCommandAndQueryTypeEnum value seen while expecting a response: " + CommandQueryTypeEnum.ToString());
                }
                }
            }
            else if (ExpectedResponseStatusEnum == PLCCommandResponseExpectationEnum.MINOR_RESPONSE)
            {
                FinalResponseContainer = new byte[]
                {
                    0x3, 0x0, 0x0
                };

                switch (CommandQueryTypeEnum)
                {
                case PLCCommandAndQueryTypeEnum.CANCEL_ACTIVE_OBJECTIVE_AZEL_POSITION:
                case PLCCommandAndQueryTypeEnum.SHUTDOWN:
                case PLCCommandAndQueryTypeEnum.CALIBRATE:
                {
                    FinalResponseContainer[2] = 0x1;
                    break;
                }

                case PLCCommandAndQueryTypeEnum.SET_OBJECTIVE_AZEL_POSITION:
                {
                    double NextAZ, NextEL;

                    try
                    {
                        NextAZ = BitConverter.ToDouble(query, 3);
                        NextEL = BitConverter.ToDouble(query, 11);
                    }
                    catch (Exception e)
                    {
                        if ((e is ArgumentException) || (e is ArgumentNullException) || (e is ArgumentOutOfRangeException))
                        {
                            // This error code means that the data could not be converted into a double-precision floating point
                            FinalResponseContainer[2] = 0x2;
                            break;
                        }
                        else
                        {
                            // Unexpected exception
                            throw e;
                        }
                    }

                    if ((NextAZ < 0) || (NextAZ > 360))
                    {
                        // This error code means that the objective azimuth position is invalid
                        FinalResponseContainer[2] = 0x3;
                        break;
                    }

                    if ((NextEL < 0) || (NextEL > 90))
                    {
                        // This error code means that the objective elevation position is invalid
                        FinalResponseContainer[2] = 0x4;
                        break;
                    }

                    // Otherwise, this is valid
                    // TODO: Perform task(s) to set objective orientation!

                    FinalResponseContainer[2] = 0x1;
                    break;
                }

                default:
                {
                    throw new ArgumentException("Invalid PLCCommandAndQueryTypeEnum value seen while NOT expecting a response: " + CommandQueryTypeEnum.ToString());
                }
                }
            }
            else
            {
                throw new ArgumentException("Invalid PLCCommandResponseExpectationEnum value seen while processing client request in ScaleModelPLCDriver: " + ExpectedResponseStatusEnum.ToString());
            }

            return(AttemptToWriteDataToServer(ActiveClientStream, FinalResponseContainer));
        }
        public bool ProcessRequest(NetworkStream ActiveClientStream, byte[] query)
        {
            int ExpectedSize = query[0] + (256 * query[1]);

            if (query.Length != ExpectedSize)
            {
                throw new ArgumentException(
                          "TestPLCDriverController read a package specifying a size [" + ExpectedSize.ToString() + "], but the actual size was different [" + query.Length + "]."
                          );
            }

            byte CommandQueryTypeAndExpectedResponseStatus = query[2];
            byte CommandQueryTypeByte       = (byte)(CommandQueryTypeAndExpectedResponseStatus & 0x3F);
            byte ExpectedResponseStatusByte = (byte)(CommandQueryTypeAndExpectedResponseStatus >> 6);

            PLCCommandAndQueryTypeEnum        CommandQueryTypeEnum       = PLCCommandAndQueryTypeConversionHelper.GetFromByte(CommandQueryTypeByte);
            PLCCommandResponseExpectationEnum ExpectedResponseStatusEnum = PLCCommandResponseExpectationConversionHelper.GetFromByte(ExpectedResponseStatusByte);

            byte[] FinalResponseContainer;

            if (ExpectedResponseStatusEnum == PLCCommandResponseExpectationEnum.FULL_RESPONSE)
            {
                FinalResponseContainer = new byte[]
                {
                    0x13, 0x0,
                    0x0,
                    0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
                    0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0
                };

                switch (CommandQueryTypeEnum)
                {
                case PLCCommandAndQueryTypeEnum.TEST_CONNECTION:
                {
                    FinalResponseContainer[3] = 0x1;
                    FinalResponseContainer[2] = 0x1;
                    break;
                }

                case PLCCommandAndQueryTypeEnum.GET_CURRENT_AZEL_POSITIONS:
                {
                    Array.Copy(BitConverter.GetBytes(CurrentOrientation.Azimuth), 0, FinalResponseContainer, 3, 8);
                    Array.Copy(BitConverter.GetBytes(CurrentOrientation.Elevation), 0, FinalResponseContainer, 11, 8);

                    FinalResponseContainer[2] = 0x1;

                    break;
                }

                case PLCCommandAndQueryTypeEnum.GET_CURRENT_LIMIT_SWITCH_STATUSES:
                {
                    double CurrentAZ = CurrentOrientation.Azimuth;
                    double CurrentEL = CurrentOrientation.Elevation;

                    double ThresholdAZ = MiscellaneousHardwareConstants.LIMIT_SWITCH_AZ_THRESHOLD_DEGREES;
                    double ThresholdEL = MiscellaneousHardwareConstants.LIMIT_SWITCH_EL_THRESHOLD_DEGREES;

                    // Subtracting out those 2 degrees is because of our actual rotational limits of (-2 : 362) and (-2 : 92) degrees in azimuth and elevation respectively
                    PLCLimitSwitchStatusEnum StatusAzimuthUnderRotation   = (CurrentAZ < (ThresholdAZ - 2.0)) ? PLCLimitSwitchStatusEnum.WITHIN_WARNING_LIMITS : PLCLimitSwitchStatusEnum.WITHIN_SAFE_LIMITS;
                    PLCLimitSwitchStatusEnum StatusAzimuthOverRotation    = (CurrentAZ > (360 + ThresholdAZ - 2.0)) ? PLCLimitSwitchStatusEnum.WITHIN_WARNING_LIMITS : PLCLimitSwitchStatusEnum.WITHIN_SAFE_LIMITS;
                    PLCLimitSwitchStatusEnum StatusElevationUnderRotation = (CurrentEL < (ThresholdEL - 2.0)) ? PLCLimitSwitchStatusEnum.WITHIN_WARNING_LIMITS : PLCLimitSwitchStatusEnum.WITHIN_SAFE_LIMITS;
                    PLCLimitSwitchStatusEnum StatusElevationOverRotation  = (CurrentEL > (90 + ThresholdEL - 2.0)) ? PLCLimitSwitchStatusEnum.WITHIN_WARNING_LIMITS : PLCLimitSwitchStatusEnum.WITHIN_SAFE_LIMITS;

                    int PacketSum =
                        PLCLimitSwitchStatusConversionHelper.ConvertToByte(StatusElevationOverRotation)
                        + (PLCLimitSwitchStatusConversionHelper.ConvertToByte(StatusElevationUnderRotation) * 0x4)
                        + (PLCLimitSwitchStatusConversionHelper.ConvertToByte(StatusAzimuthOverRotation) * 0x10)
                        + (PLCLimitSwitchStatusConversionHelper.ConvertToByte(StatusAzimuthUnderRotation) * 0x40)
                    ;

                    FinalResponseContainer[3] = (byte)PacketSum;
                    FinalResponseContainer[2] = 0x1;

                    break;
                }

                case PLCCommandAndQueryTypeEnum.GET_CURRENT_SAFETY_INTERLOCK_STATUS:
                {
                    FinalResponseContainer[3] = PLCSafetyInterlockStatusConversionHelper.ConvertToByte(PLCSafetyInterlockStatusEnum.LOCKED);
                    FinalResponseContainer[2] = 0x1;
                    break;
                }

                default:
                {
                    throw new ArgumentException("Invalid PLCCommandAndQueryTypeEnum value seen while expecting a response: " + CommandQueryTypeEnum.ToString());
                }
                }
            }
            else if (ExpectedResponseStatusEnum == PLCCommandResponseExpectationEnum.MINOR_RESPONSE)
            {
                FinalResponseContainer = new byte[]
                {
                    0x3, 0x0, 0x0
                };

                switch (CommandQueryTypeEnum)
                {
                case PLCCommandAndQueryTypeEnum.CANCEL_ACTIVE_OBJECTIVE_AZEL_POSITION:
                case PLCCommandAndQueryTypeEnum.SHUTDOWN:
                case PLCCommandAndQueryTypeEnum.CALIBRATE:
                {
                    FinalResponseContainer[2] = 0x1;
                    break;
                }

                case PLCCommandAndQueryTypeEnum.SET_OBJECTIVE_AZEL_POSITION:
                {
                    double NextAZ, NextEL;

                    try
                    {
                        NextAZ = BitConverter.ToDouble(query, 3);
                        NextEL = BitConverter.ToDouble(query, 11);
                    }
                    catch (Exception e)
                    {
                        if ((e is ArgumentException) || (e is ArgumentNullException) || (e is ArgumentOutOfRangeException))
                        {
                            // This error code means that the data could not be converted into a double-precision floating point
                            FinalResponseContainer[2] = 0x2;
                            break;
                        }
                        else
                        {
                            // Unexpected exception
                            throw e;
                        }
                    }

                    if ((NextAZ < 0) || (NextAZ > 360))
                    {
                        // This error code means that the objective azimuth position is invalid
                        FinalResponseContainer[2] = 0x3;
                        break;
                    }

                    if ((NextEL < 0) || (NextEL > 90))
                    {
                        // This error code means that the objective elevation position is invalid
                        FinalResponseContainer[2] = 0x4;
                        break;
                    }

                    // Otherwise, this is valid
                    CurrentOrientation = new Orientation(NextAZ, NextEL);

                    logger.Info("[TestPLCDriver] Setting current orientation to {" + CurrentOrientation.Azimuth.ToString() + ", " + CurrentOrientation.Elevation.ToString() + "}");

                    FinalResponseContainer[2] = 0x1;
                    break;
                }

                default:
                {
                    throw new ArgumentException("Invalid PLCCommandAndQueryTypeEnum value seen while NOT expecting a response: " + CommandQueryTypeEnum.ToString());
                }
                }
            }
            else
            {
                throw new ArgumentException("Invalid PLCCommandResponseExpectationEnum value seen while processing client request in ScaleModelPLCDriver: " + ExpectedResponseStatusEnum.ToString());
            }

            return(AttemptToWriteDataToServer(ActiveClientStream, FinalResponseContainer));
        }