Ejemplo n.º 1
0
 /// <summary>
 /// This generates a Response of a given Type with a custom message and Error Code
 /// </summary>
 /// <typeparam name="T">The Type of Response to create and fill</typeparam>
 /// <param name="code">The Alpaca Error Code</param>
 /// <param name="message">The message to use</param>
 /// <param name="clientTransactionID">The Client Transaction ID</param>
 /// <param name="serverTransactionID">The Server Transaction ID</param>
 /// <returns></returns>
 public static T ExceptionResponseBuilder <T>(AlpacaErrors code, string message, uint clientTransactionID = 0, uint serverTransactionID = 0) where T : Response, new()
 {
     return(new T()
     {
         ClientTransactionID = clientTransactionID,
         ServerTransactionID = serverTransactionID,
         ErrorNumber = code,
         ErrorMessage = message
     });
 }
 /// <summary>
 /// Initialise the ArrayMetadataV1 structure
 /// </summary>
 /// <param name="errorNumber">Transaction error number.</param>
 /// <param name="clientTransactionID">Client's transaction ID</param>
 /// <param name="serverTransactionID">Device's transaction ID</param>
 /// <param name="imageElementType">Intended element type of the resultant array.</param>
 /// <param name="transmissionElementType">Element type actually sent.</param>
 /// <param name="arrayRank">Rank of the arrayresultant array.</param>
 /// <param name="arrayDimension1">Size of the first dimension of the resultant array (array[Dimension1, Dimension2, Dimension3]).</param>
 /// <param name="arrayDimension1">Size of the second dimension of the resultant array (array[Dimension1, Dimension2, Dimension3]).</param>
 /// <param name="arrayDimension2">Size of the third dimension of the resultant array (array[Dimension1, Dimension2, Dimension3]).</param>
 public ArrayMetadataV1(AlpacaErrors errorNumber,
                        uint clientTransactionID,
                        uint serverTransactionID,
                        ImageArrayElementTypes imageElementType,
                        ImageArrayElementTypes transmissionElementType,
                        int arrayRank,
                        int arrayDimension1,
                        int arrayDimension2,
                        int arrayDimension3)
 {
     MetadataVersion          = 1;
     this.ErrorNumber         = errorNumber;
     this.ClientTransactionID = clientTransactionID;
     this.ServerTransactionID = serverTransactionID;
     DataStart                    = AlpacaTools.ARRAY_METADATAV1_LENGTH;
     this.ImageElementType        = imageElementType;
     this.TransmissionElementType = transmissionElementType;
     this.Rank                    = arrayRank;
     this.Dimension1              = arrayDimension1;
     this.Dimension2              = arrayDimension2;
     this.Dimension3              = arrayDimension3;
 }
        /// <summary>
        /// Gets a driver exception from an error code and adds the message if available
        /// </summary>
        /// <param name="code">The Alpaca ErrorCode for the Exception</param>
        /// <param name="message">The optional message of the exception</param>
        /// <returns>Null if there is no exception, otherwise the ASCOM Exception for the error code</returns>
        public static DriverException ExceptionFromErrorCode(AlpacaErrors code, string message = "")
        {
            switch (code)
            {
            case AlpacaErrors.InvalidValue:
                return(new InvalidValueException(message));

            case AlpacaErrors.ValueNotSet:
                return(new ValueNotSetException(message));

            case AlpacaErrors.NotConnected:
                return(new NotConnectedException(message));

            case AlpacaErrors.InvalidWhileParked:
                return(new ParkedException(message));

            case AlpacaErrors.InvalidWhileSlaved:
                return(new SlavedException(message));

            case AlpacaErrors.InvalidOperationException:
                return(new InvalidOperationException(message));

            case AlpacaErrors.UnspecifiedError:
                return(new DriverException(message));

            case AlpacaErrors.NotImplemented:
                return(new NotImplementedException(message));

            case AlpacaErrors.ActionNotImplementedException:
                return(new ActionNotImplementedException(message));

            case AlpacaErrors.AlpacaNoError:
            //No Error
            default:
                return(null);
            }
        }
 /// <summary>
 /// Create a new ShutterStateResponse with the supplied parameter values
 /// </summary>
 /// <param name="clientTransactionID">Client transaction ID</param>
 /// <param name="serverTransactionID">Server transaction ID</param>
 /// <param name="errorMessage">Value to return</param>
 /// <param name="errorCode">Server transaction ID</param>
 public ShutterStateResponse(uint clientTransactionID, uint serverTransactionID, string errorMessage, AlpacaErrors errorCode)
 {
     base.ServerTransactionID = serverTransactionID;
     base.ClientTransactionID = clientTransactionID;
     base.ErrorMessage        = errorMessage;
     base.ErrorNumber         = errorCode;
 }
Ejemplo n.º 5
0
 /// <summary>
 /// Create a new EquatorialCoordinateTypeResponse with the supplied parameter values
 /// </summary>
 /// <param name="clientTransactionID">Client transaction ID</param>
 /// <param name="serverTransactionID">Server transaction ID</param>
 /// <param name="errorMessage">Value to return</param>
 /// <param name="errorCode">Server transaction ID</param>
 public EquatorialCoordinateTypeResponse(uint clientTransactionID, uint serverTransactionID, string errorMessage, AlpacaErrors errorCode)
 {
     base.ServerTransactionID = serverTransactionID;
     base.ClientTransactionID = clientTransactionID;
     base.ErrorMessage        = errorMessage;
     base.ErrorNumber         = errorCode;
 }
Ejemplo n.º 6
0
        /// <summary>
        /// Convert an Alpaca error number and message to a byte array for transfer to a client.
        /// </summary>
        /// <param name="metadataVersion">Required metadata version - Currently 1</param>
        /// <param name="alpacaErrorNumber">Alpaca error number</param>
        /// <param name="errorMessage">Error message to encode.</param>
        /// <returns></returns>
        /// <exception cref="InvalidValueException"></exception>
        public static byte[] ErrorMessageToByteArray(int metadataVersion, uint clientTransactionID, uint serverTransactionID, AlpacaErrors alpacaErrorNumber, string errorMessage)
        {
            // Validate supplied parameters
            if (metadataVersion != 1)
            {
                throw new InvalidValueException($"ErrorMessageToByteArray - Unsupported metadata version: {metadataVersion}.");
            }

            if (alpacaErrorNumber == AlpacaErrors.AlpacaNoError)
            {
                throw new InvalidValueException($"ErrorMessageToByteArray - Supplied error number is {alpacaErrorNumber}, this indicates 'Success' rather than an 'Error'.");
            }

            if ((alpacaErrorNumber < AlpacaErrors.AlpacaNoError) | (alpacaErrorNumber > AlpacaErrors.DriverMax))
            {
                throw new InvalidValueException($"ErrorMessageToByteArray - Invalid Alpaca error number: {alpacaErrorNumber}.");
            }

            if (string.IsNullOrEmpty(errorMessage))
            {
                throw new InvalidValueException($"ErrorMessageToByteArray - Error message is either null or an empty string.");
            }

            switch (metadataVersion)
            {
            case 1:
                // Create a metadata structure containing the supplied error number
                ArrayMetadataV1 arrayMetadataV1 = new ArrayMetadataV1(alpacaErrorNumber, clientTransactionID, serverTransactionID, ImageArrayElementTypes.Unknown, ImageArrayElementTypes.Unknown, 0, 0, 0, 0);

                // Create a byte array from the metadata structure
                byte[] arrayMetadataV1Bytes = arrayMetadataV1.ToByteArray <ArrayMetadataV1>();

                // Create a byte array containing the UTF8 encoded string
                byte[] errorMessagebytes = Encoding.UTF8.GetBytes(errorMessage);

                // Create a return byte array that is large enough to contain both the metadata bytes and the UTF8 encoded message bytes
                byte[] returnByteArray = new byte[ARRAY_METADATAV1_LENGTH + errorMessagebytes.Length];

                // Copy the metadata bytes to the start of the return array
                Array.Copy(arrayMetadataV1Bytes, returnByteArray, arrayMetadataV1Bytes.Length);

                // Copy the error message bytes after the metadata bytes
                Array.Copy(errorMessagebytes, 0, returnByteArray, ARRAY_METADATAV1_LENGTH, errorMessagebytes.Length);

                // Return the composite byte array
                return(returnByteArray);

            default:
                throw new InvalidValueException($"ErrorMessageToByteArray - Unsupported metadata version: {metadataVersion}");
            }
        }
Ejemplo n.º 7
0
        /// <summary>
        /// Alpaca Extension - Convert the array or error message to a byte array for transmission to a client
        /// </summary>
        /// <param name="imageArray">The 2D or 3D source image array. (Ignored when returning an error.)</param>
        /// <param name="metadataVersion">Metadata version to use (Currently 1).</param>
        /// <param name="clientTransactionID">Client's transaction ID.</param>
        /// <param name="serverTransactionID">Device's transaction ID.</param>
        /// <param name="errorNumber">Error number. 0 for success, non-zero for an error.</param>
        /// <param name="errorMessage">Error message. Empty string for success, error message for an error.</param>
        /// <returns>Byte array prefixed with array metadata.</returns>
        /// <exception cref="InvalidValueException">If only one of the error number and error message indicates an error.</exception>
        /// <exception cref="InvalidValueException">Image array is null for a successful transaction or the array rank is <2 or >3 or the array is of type object</exception>
        /// <exception cref="InvalidValueException">The array element type is not supported.</exception>
        public static byte[] ToByteArray(this Array imageArray, int metadataVersion, uint clientTransactionID, uint serverTransactionID, AlpacaErrors errorNumber, string errorMessage)
        {
            int transmissionElementSize; // Managed size of transmitted elements

            // Handle error conditions
            if ((errorNumber != 0) | (!string.IsNullOrWhiteSpace(errorMessage)))
            {
                // Validate error parameters
                if ((errorNumber == 0) & (!string.IsNullOrWhiteSpace(errorMessage)))
                {
                    throw new InvalidValueException($"ToByteArray - Error number is {errorNumber} but an error message has been supplied: '{errorMessage}'");
                }
                if ((errorNumber != 0) & (string.IsNullOrWhiteSpace(errorMessage)))
                {
                    throw new InvalidValueException($"ToByteArray - Error number is {errorNumber} but no error message has been supplied: '{errorMessage}'");
                }

                // Handle metadata versions
                switch (metadataVersion)
                {
                case 1:
                    byte[]          errorMessageBytes = Encoding.UTF8.GetBytes(errorMessage);
                    ArrayMetadataV1 metadataVersion1  = new ArrayMetadataV1();
                    metadataVersion1.ErrorNumber = errorNumber;

                    byte[] metadataBytes = metadataVersion1.ToByteArray();
                    byte[] returnBytes   = new byte[metadataBytes.Length + errorMessageBytes.Length];
                    Array.Copy(metadataBytes, returnBytes, metadataBytes.Length);
                    Array.Copy(errorMessageBytes, 0, returnBytes, metadataBytes.Length, errorMessageBytes.Length);
                    return(returnBytes);

                default:
                    throw new InvalidValueException($"Unsupported metadata version: {metadataVersion}");
                }
            }

            // At this point we have a successful transaction so validate the incoming array
            if (imageArray is null)
            {
                throw new InvalidValueException("ToByteArray - Supplied array is null.");
            }
            if ((imageArray.Rank < 2) | (imageArray.Rank > 3))
            {
                throw new InvalidValueException($"ToByteArray - Only arrays of rank 2 and 3 are supported. The supplied array has a rank of {imageArray.Rank}.");
            }

            // We can't handle object arrays so test for this
            string arrayTypeName = imageArray.GetType().Name;

            if (arrayTypeName.ToLowerInvariant().Contains("object"))
            {
                throw new InvalidValueException($"ToByteArray - Object arrays of type {arrayTypeName} are not supported.");
            }

            // Set the array type
            ImageArrayElementTypes intendedElementType     = ImageArrayElementTypes.Unknown;
            ImageArrayElementTypes transmissionElementType = ImageArrayElementTypes.Unknown;

            // Get the type code of the array elements
            TypeCode arrayElementTypeCode = Type.GetTypeCode(imageArray.GetType().GetElementType());

            // Set the element type of the intended array and default the transmission element type to be the same as the intended type
            switch (arrayElementTypeCode)
            {
            case TypeCode.Byte:
                intendedElementType     = ImageArrayElementTypes.Byte;
                transmissionElementType = ImageArrayElementTypes.Byte;
                transmissionElementSize = 1;
                break;

            case TypeCode.Int16:
                intendedElementType     = ImageArrayElementTypes.Int16;
                transmissionElementType = ImageArrayElementTypes.Int16;
                transmissionElementSize = 2;
                break;

            case TypeCode.Int32:
                intendedElementType     = ImageArrayElementTypes.Int32;
                transmissionElementType = ImageArrayElementTypes.Int32;
                transmissionElementSize = 4;
                break;

            case TypeCode.Int64:
                intendedElementType     = ImageArrayElementTypes.Int64;
                transmissionElementType = ImageArrayElementTypes.Int64;
                transmissionElementSize = 8;
                break;

            case TypeCode.Single:
                intendedElementType     = ImageArrayElementTypes.Single;
                transmissionElementType = ImageArrayElementTypes.Single;
                transmissionElementSize = 4;
                break;

            case TypeCode.Double:
                intendedElementType     = ImageArrayElementTypes.Double;
                transmissionElementType = ImageArrayElementTypes.Double;
                transmissionElementSize = 8;
                break;

            case TypeCode.Decimal:
                intendedElementType     = ImageArrayElementTypes.Decimal;
                transmissionElementType = ImageArrayElementTypes.Decimal;
                transmissionElementSize = 16;
                break;

            default:
                throw new InvalidValueException($"ToByteArray - Received an unsupported return array type: {imageArray.GetType().Name}, with elements of type: {imageArray.GetType().GetElementType().Name}");
            }

            // Special handling for Int32 arrays - see if we can convert them to Int16 or UInt16 arrays
            if (arrayElementTypeCode == TypeCode.Int32)
            {
                // NOTE
                // NOTE - This algorithm uses a UInt16 array to transmit an array with Int16 values because we are only interested in the byte values for this purpose,
                // NOTE - not the arithmetic interpretation of those bytes.
                // NOTE

                bool arrayIsInt16  = true; // Flag indicating whether the supplied array conforms to the Int16 value range -32768 to +32767. Start by assuming success
                bool arrayIsUint16 = true; // Flag indicating whether the supplied array conforms to the UInt16 value range 0 to +65535. Start by assuming success

                // Handle 2D and 3D arrays
                switch (imageArray.Rank)
                {
                case 2:
                    UInt16[,] uInt16Array = new UInt16[imageArray.GetLength(0), imageArray.GetLength(1)];     // Array to hold the 16bit transmission array (either Int16 or UInt16 values)

                    // Get the device's Int32 image array
                    int[,] int2dArray = (int[, ])imageArray;

                    // Parellelise the array copy to improve performance
                    Parallel.For(0, imageArray.GetLength(0), (i) => // Iterate over the slowest changing dimension
                    {
                        Int32 int32ElementValue;                    // Local variable to hold the Int32 element being tested (saves calculating an array offset later in the process)
                        Int16 int16ElementValue;                    // Local variable to hold the Int16 element value (saves calculating an array offset later in the process)
                        UInt16 uInt16ElementValue;                  // Local variable to hold the Unt16 element value (saves calculating an array offset later in the process)
                        bool arrayIsInt16Internal  = true;          // Local variable to hold the Int16 status within this particular thread. Used to reduce thread conflict when updating the arrayIsInt16 variable.
                        bool arrayIsUint16Internal = true;          // Local variable to hold the UInt16 status within this particular thread. Used to reduce thread conflict when updating the arrayIsInt16 variable.

                        // Iterate over the fastest changing dimension
                        for (int j = 0; j < imageArray.GetLength(1); j++)
                        {
                            // Get the current array element value
                            int32ElementValue = int2dArray[i, j];

                            // Truncate the supplied 4-byte Int32 value to create a 2-byte UInt16 value
                            uInt16ElementValue = (UInt16)int32ElementValue;

                            // Truncate the supplied 4-byte Int32 value to create a 2-byte Int16 value
                            int16ElementValue = (Int16)int32ElementValue;

                            // Store the UInt16 value in the array.
                            uInt16Array[i, j] = uInt16ElementValue;


                            // Compare the Int16 and Int32 values.
                            if (int16ElementValue != int32ElementValue)
                            {
                                arrayIsInt16Internal = false;
                            }

                            // Compare the UInt16 and Int32 values.
                            if (uInt16ElementValue != int32ElementValue)
                            {
                                arrayIsUint16Internal = false;
                            }
                        }

                        // Update the master arrayIsInt16 and arrayIsUint16 variables as the logical AND of the mater and update values.
                        arrayIsInt16  &= arrayIsInt16Internal;
                        arrayIsUint16 &= arrayIsUint16Internal;
                    });

                    // Return the appropriate array if either a UInt16 or Int16 array was provided by the device
                    if (arrayIsUint16)                                           // Supplied array has UInt16 values so return the shorter array in place of the supplied Int32 array
                    {
                        imageArray = uInt16Array;                                // Assign the Int16 array to the imageArray variable in place of the Int32 array
                        transmissionElementType = ImageArrayElementTypes.UInt16; // Flag that the array elements are UInt16
                        transmissionElementSize = sizeof(UInt16);                // Indicate that the transmitted elements are of UInt16 size rather than Int32 size
                    }
                    else if (arrayIsInt16)                                       // Supplied array has Int16 values so return the shorter array in place of the supplied Int32 array
                    {
                        imageArray = uInt16Array;                                // Assign the UInt16 array to the imageArray variable in place of the Int32 array (at the byte level Int32 and UInt32 are equivalent - both consist of two bytes)
                        transmissionElementType = ImageArrayElementTypes.Int16;  // Flag that the array elements are Int16
                        transmissionElementSize = sizeof(Int16);                 // Indicate that the transmitted elements are of UInt16 size rather than Int32 size
                    }
                    else
                    {
                        // No action, continue to use the supplied Int32 array because its values fall outside the Uint16 and Int16 number ranges
                    }
                    break;

                case 3:
                    UInt16[,,] uInt163dArray = new UInt16[imageArray.GetLength(0), imageArray.GetLength(1), imageArray.GetLength(2)];      // Array to hold the 16bit transmission array (either Int16 or UInt16 values)

                    // Get the device's Int32 image array
                    Int32[,,] int3dArray = (Int32[, , ])imageArray;

                    // Parellelise the array copy to improve performance
                    Parallel.For(0, imageArray.GetLength(0), (i) => // Iterate over the slowest changing dimension
                    {
                        bool arrayIsInt16Internal1  = true;         // Local variable to hold the Int16 status within this particular thread. Used to reduce thread conflict when updating the arrayisInt16 variable.
                        bool arrayIsUint16Internal1 = true;         // Local variable to hold the UInt16 status within this particular thread. Used to reduce thread conflict when updating the arrayisInt16 variable.

                        // Iterate over the mid changing dimension
                        for (int j = 0; j < imageArray.GetLength(1); j++)
                        {
                            Int32 int32ElementValue;            // Local variable to hold the Int32 element being tested (saves calculating an array offset later in the process)
                            Int16 int16ElementValue;            // Local variable to hold the Int16 element value (saves calculating an array offset later in the process)
                            UInt16 uInt16ElementValue;          // Local variable to hold the UInt16 element value (saves calculating an array offset later in the process)
                            bool arrayIsInt16Internal2  = true; // Local variable to hold the Int16 status within this particular thread. Used to reduce thread conflict when updating the arrayIsInt16 variable.
                            bool arrayIsUInt16Internal2 = true; // Local variable to hold the Int16 status within this particular thread. Used to reduce thread conflict when updating the arrayIsUInt16 variable.

                            // Iterate over the fastest changing dimension
                            for (int k = 0; k < imageArray.GetLength(2); k++)
                            {
                                // Get the current array element value
                                int32ElementValue = int3dArray[i, j, k];

                                // Truncate the supplied 4-byte Int32 value to create a 2-byte UInt16 value
                                uInt16ElementValue = (UInt16)int32ElementValue;

                                // Truncate the supplied 4-byte Int32 value to create a 2-byte Int16 value
                                int16ElementValue = (Int16)int32ElementValue;

                                // Copy the Int32 value to the corresponding Int16 array element.
                                uInt163dArray[i, j, k] = uInt16ElementValue;

                                // Compare the Int16 and Int32 values.
                                if (int16ElementValue != int32ElementValue)
                                {
                                    arrayIsInt16Internal2 = false;                                             // If they are not the same the Int32 value was outside the range of Int16 and arrayIsInt16Internal will be set false
                                }
                                // Compare the UInt16 and Int32 values.
                                if (uInt16ElementValue != int32ElementValue)
                                {
                                    arrayIsUInt16Internal2 = false;
                                }
                            }

                            // Update the arrayIsInt16Internal1 and arrayIsUint16Internal1 variables as the logical AND of the mater and update values.
                            arrayIsInt16Internal1  &= arrayIsInt16Internal2;
                            arrayIsUint16Internal1 &= arrayIsUInt16Internal2;
                        }

                        // Update the master arrayIsInt16 and arrayIsUint16 variables as the logical AND of the mater and update values.
                        arrayIsInt16  &= arrayIsInt16Internal1;
                        arrayIsUint16 &= arrayIsUint16Internal1;
                    });

                    // Return the appropriate array if either a UInt16 or Int16 array was provided by the device
                    if (arrayIsUint16)                                           // Supplied array has UInt16 values so return the shorter array in place of the supplied Int32 array
                    {
                        imageArray = uInt163dArray;                              // Assign the Int16 array to the imageArray variable in place of the Int32 array
                        transmissionElementType = ImageArrayElementTypes.UInt16; // Flag that the array elements are UInt16
                        transmissionElementSize = sizeof(UInt16);                // Indicate that the transmitted elements are of UInt16 size rather than Int32 size
                    }
                    else if (arrayIsInt16)                                       // Supplied array has Int16 values so return the shorter array in place of the supplied Int32 array
                    {
                        imageArray = uInt163dArray;                              // Assign the UInt16 array to the imageArray variable in place of the Int32 array (at the byte level Int32 and UInt32 are equivalent - both consist of two bytes)
                        transmissionElementType = ImageArrayElementTypes.Int16;  // Flag that the array elements are Int16
                        transmissionElementSize = sizeof(Int16);                 // Indicate that the transmitted elements are of UInt16 size rather than Int32 size
                    }
                    else
                    {
                        // No action, continue to use the supplied Int32 array because its values fall outside the Uint16 and Int16 number ranges
                    }
                    break;

                default:
                    throw new InvalidValueException($"ToByteArray - The camera returned an array of rank: {imageArray.Rank}, which is not supported.");
                }
            }

            switch (metadataVersion)
            {
            case 1:
                // Create a version 1 metadata structure
                ArrayMetadataV1 metadataVersion1;
                if (imageArray.Rank == 2)
                {
                    metadataVersion1 = new ArrayMetadataV1(AlpacaErrors.AlpacaNoError, clientTransactionID, serverTransactionID, intendedElementType, transmissionElementType, 2, imageArray.GetLength(0), imageArray.GetLength(1), 0);
                }
                else
                {
                    metadataVersion1 = new ArrayMetadataV1(AlpacaErrors.AlpacaNoError, clientTransactionID, serverTransactionID, intendedElementType, transmissionElementType, 3, imageArray.GetLength(0), imageArray.GetLength(1), imageArray.GetLength(2));
                }

                // Turn the metadata structure into a byte array
                byte[] metadataVersion2Bytes = metadataVersion1.ToByteArray <ArrayMetadataV1>();

                // Create a return array of size equal to the sum of the metadata and image array lengths
                byte[] imageArrayBytesV2 = new byte[imageArray.Length * transmissionElementSize + metadataVersion2Bytes.Length];     // Size the image array bytes as the product of the transmission element size and the number of elements

                // Copy the metadata bytes to the start of the return byte array
                Array.Copy(metadataVersion2Bytes, imageArrayBytesV2, metadataVersion2Bytes.Length);

                // Copy the image array bytes after the metadata
                Buffer.BlockCopy(imageArray, 0, imageArrayBytesV2, metadataVersion2Bytes.Length, imageArray.Length * transmissionElementSize);

                // Return the byte array
                return(imageArrayBytesV2);

            default:
                throw new InvalidValueException($"ToByteArray - Unsupported metadata version: {metadataVersion}");
            }
        }
 /// <summary>
 /// Create a new AlpacaConfiguredDevicesResponse with the supplied parameter values
 /// </summary>
 /// <param name="clientTransactionID">Client transaction ID</param>
 /// <param name="serverTransactionID">Server transaction ID</param>
 /// <param name="errorMessage">Value to return</param>
 /// <param name="errorCode">Server transaction ID</param>
 public AlpacaConfiguredDevicesResponse(uint clientTransactionID, uint serverTransactionID, string errorMessage, AlpacaErrors errorCode)
 {
     base.ServerTransactionID = serverTransactionID;
     base.ClientTransactionID = clientTransactionID;
     base.ErrorMessage        = errorMessage;
     base.ErrorNumber         = errorCode;
 }