/// <summary>
        /// command specific implementation 
        /// </summary>
        internal override void commandImplementation()
        {
            MultiRecordResponse myResponse = new MultiRecordResponse();
            try
            {
                dynamic payload = null;

                if (!(this.argVal.ContainsKey('p')))
                {
                    Console.WriteLine(WcsCliConstants.commandFailure +
                        " No record data specified, please look at command help");
                    return;
                }
                else
                {
                    this.argVal.TryGetValue('p', out payload);
                }

                myResponse = WcsCli2CmConnectionManager.channel.SetPdbAssetInfo((string)payload);
            }

            catch (Exception ex)
            {
                SharedFunc.ExceptionOutput(ex);
                return;
            }

            ResponseValidation.ValidateResponse("PDB's FRU has been written successfully", myResponse, true);
        }
        /// <summary>
        /// Prepares and writes the Multi Record area portion of FRU for blade, CM and Pdb.
        /// </summary>
        /// <param name="deviceId"></param>
        /// <param name="recordData"></param>
        /// <param name="deviceType"></param>
        /// <returns></returns>
        private MultiRecordResponse WriteMultiRecordFru(int deviceId, string recordData, DeviceType deviceType)
        {
            Ipmi.FruCommonHeader commonHeader = null;
            Ipmi.FruMultiRecordInfo multiRecordInfo = null;

            CompletionCode completionCode = CompletionCode.UnspecifiedError;

            switch (deviceType)
            {
                case DeviceType.Server:
                    {
                        Ipmi.FruDevice fruData = WcsBladeFacade.GetFruDeviceInfo((byte)deviceId);
                        completionCode = (CompletionCode)fruData.CompletionCode;
                        commonHeader = fruData.CommonHeader;
                        multiRecordInfo = fruData.MultiRecordInfo;
                    }
                    break;
                case DeviceType.ChassisFruEeprom:
                case DeviceType.PdbFruEeprom:
                    {
                        Ipmi.FruDevice fruData = ChassisState.CmFruData.ReadFru(deviceType);
                        completionCode = (CompletionCode)fruData.CompletionCode;
                        commonHeader = fruData.CommonHeader;
                        multiRecordInfo = fruData.MultiRecordInfo;
                    }
                    break;
                default:
                    Tracer.WriteInfo("WriteMultiRecordFru() Unknown device type: {0} CompletionCode: {1}",
                        deviceType.ToString(), completionCode.ToString());
                    break;
            }

            MultiRecordResponse setAsset = new MultiRecordResponse();

            try
            {
                if (completionCode == (byte)CompletionCode.Success)
                {
                    if (multiRecordInfo == null || commonHeader == null)
                    {
                        Tracer.WriteError("WriteMultiRecordFru(). multiRecordInfo and/or commonHeader is null. Aborting...");
                        setAsset.completionCode = Contracts.CompletionCode.Failure;
                        setAsset.statusDescription = setAsset.completionCode.ToString();
                        return setAsset;
                    }

                    // Abort if the starting offset of MultiRecordArea is 0,
                    // suggesting that no Multi Record Area provision is made
                    if (commonHeader.MultiRecordAreaStartingOffset == 0)
                    {
                        Tracer.WriteError(@"WriteMultiRecordFru(). MultiRecordAreaStartingOffset is zero.
                                           Fru does not have Multi Record Area provisioned.");
                        setAsset.completionCode = Contracts.CompletionCode.WriteFruZeroStartingOffset;
                        setAsset.statusDescription = setAsset.completionCode.ToString();
                        return setAsset;
                    }

                    int idx = 0; // offset for different Multi Record Area fields (including header)
                    byte[] header = new byte[multiRecordInfo.HeaderSize];
                    byte maxWritesRemaining = 255;
                    bool writesRemainingReset = false; // to track if writes remaining has been set to the max value

                    // When starting offset of Multi Record Area is not 0, and both Record ID, Record Format is not set,
                    // it implies that the Multi Record Area is being written for the first time.
                    // The Multi Record Area format is as follows: [0] Record Type ID, [1] Record Format,
                    // [2] Record Length, [3] Record Checksum, [4] Header Checksum, [5-7] Manufacturer ID,
                    // [8] Language Code, [9] Writes Remaining, [10-N]: Record Data
                    // 0x1 is the header record format as per the specification.
                    if (commonHeader.MultiRecordAreaStartingOffset != 0 &&
                        multiRecordInfo.RecordTypeId != Ipmi.FruMultiRecordInfo.MultiRecordId &&
                        multiRecordInfo.RecordFormat != (byte)1)
                    {
                        // We are writing to Multi Record Area for the first time.
                        // Prepare and populate the Multi Record Header.
                        // We increment idx depending on the length of each header field. See IPMI FRU Specs. for
                        // details on the header format including length and offset of fields.

                        // Add Record Type Id at index [0]
                        header[idx++] = Ipmi.FruMultiRecordInfo.MultiRecordId;

                        // Add Record Format at index [1]
                        header[idx++] = 0x1;

                        // Add Writes Remaining at index [9]. This is custom defined maximum number of writes allowed.
                        header[idx + 7] = maxWritesRemaining; // writes remaining index is at index [9]
                        writesRemainingReset = true;
                    }
                    else // There is already an existing Multi Record Area portion of FRU
                    {
                        // Abort, if number of writes remaining is not greater than 0
                        if (!(multiRecordInfo.WritesRemaining > 0))
                        {
                            if (!ConfigLoaded.ResetMultiRecordFruWritesRemaining)
                            {
                                Tracer.WriteInfo("WriteMultiRecordFru(). Maximum allowable writes limit reached. Aborting...");
                                setAsset.completionCode = Contracts.CompletionCode.WriteFruZeroWritesRemaining;
                                setAsset.statusDescription = setAsset.completionCode.ToString();
                                return setAsset;
                            }
                            else
                            {
                                Tracer.WriteInfo("WriteMutliRecordFru(). Resetting the maximum writes remaining to default");
                                header[idx + 9] = maxWritesRemaining;
                                writesRemainingReset = true;
                            }
                        }

                        // Add Record Type Id at index [0]
                        header[idx++] = multiRecordInfo.RecordTypeId;

                        // Read from existing Record Format at index [1]
                        header[idx++] = multiRecordInfo.RecordFormat;
                    }

                    byte[] record = PrepareRecordData(recordData);

                    if (record == null)
                    {
                        record = new byte[] { }; // create a dummy empty record byte array
                    }

                    // Add Record Length at index [2]
                    // This is for 8-bit ASCII + Latin 1.
                    int manufacturerIdLen = multiRecordInfo.ManufacturerIdLen; // Specification defined Manufacturer ID length

                    int recordLength = (multiRecordInfo.HeaderSize + record.Length);

                    // If the length is not multiple of 8, extend length to next multiple of 8
                    if ((recordLength) % 8 != 0)
                        recordLength += recordLength % 8;

                    // The length has to be multiple of 8 bytes
                    header[idx++] = (byte)(recordLength / 8);

                    // Add Record Checksum [3]
                    header[idx++] = Ipmi.IpmiSharedFunc.TwoComplementChecksum(0, record.Length, record);

                    // Add Header Checksum [4]
                    header[idx++] = Ipmi.IpmiSharedFunc.TwoComplementChecksum(0, header.Length, header);

                    // Add Manufacturer ID at indices [5-7]
                    // Manufacturer ID is pre-defined and set to Microsoft
                    byte[] manufacturerIdArray = ConfigLoaded.MultiRecordFruManufacturerId;

                    System.Buffer.BlockCopy(manufacturerIdArray, 0, header, idx, manufacturerIdArray.Length);

                    idx += manufacturerIdLen;

                    // Add Language Code at index [8]
                    // 0 represents 'English' language code
                    header[idx++] = 0x0;

                    // Decrement WritesRemaining at index [9] if WritesRemaining is not reset to max value
                    if (!writesRemainingReset && multiRecordInfo.WritesRemaining > 0)
                    {
                        header[idx++] = (byte)(multiRecordInfo.WritesRemaining - 1);
                    }

                    // Add Record Data to Multi Record Area
                    byte[] writePayLoad = new byte[header.Length + record.Length];

                    System.Buffer.BlockCopy(header, 0, writePayLoad, 0, header.Length);

                    System.Buffer.BlockCopy(record, 0, writePayLoad, header.Length, record.Length);

                    // Maximum allowable bytes for the Multi Record Area portion (max. length allowed based on specification)
                    // It includes 10 bytes for the header, + 1 length byte, + 56 bytes for the max field size,
                    // + 1 length byte, + 56 bytes for the second max field size, + 1 end of field byte.
                    // This totals as 125 bytes. However, the MultiRecord Area length needs to be a multiple of 8 bytes.
                    // So if maxBytes < 128, the entire payload size follows the limit.
                    // Increasing higher than this number though won't change anything because the entire payload size
                    // to send to the FRU has already been created. Thus, we are keeping it at 126.
                    const int maxBytes = 128;
                    if (writePayLoad.Length < maxBytes)
                    {
                        ushort offset = commonHeader.MultiRecordAreaStartingOffset;

                        int allowedWrites = 16;
                        int writes = 0;
                        int writeIdx = 0;
                        int maxPayLoadSize = 16;
                        ushort writeOffset = offset;

                        try
                        {
                            // Since, Ipmi send/receive operates at granularity of 16 bytes chunk, we iterate over the
                            // entire payload length to send the full payload.
                            while ((writeOffset < writePayLoad.Length + offset) && (allowedWrites > writes))
                            {
                                ushort diff = (ushort)(writePayLoad.Length - writeIdx);

                                if (diff < maxPayLoadSize)
                                    maxPayLoadSize = diff;

                                byte[] payLoad = new byte[maxPayLoadSize]; // writes to Ipmi are at the granularity of 16 bytes payload

                                System.Buffer.BlockCopy(writePayLoad, writeIdx, payLoad, 0, payLoad.Length);
                                if (deviceType == DeviceType.ChassisFruEeprom || deviceType == DeviceType.PdbFruEeprom)
                                {
                                    completionCode = ChassisState.CmFruData.WriteChassisFru(writeOffset,
                                        (ushort)payLoad.Length, payLoad, deviceType);
                                }
                                else if (deviceType == DeviceType.Server)
                                {

                                    byte rawCompletionCode = WcsBladeFacade.WriteFruDevice((byte)deviceId, writeOffset,
                                        payLoad).CompletionCode;

                                    // if write fru failed
                                    if (rawCompletionCode != (byte)CompletionCode.Success)
                                    {
                                        if (Enum.IsDefined(typeof(CompletionCode), rawCompletionCode))
                                        {
                                            // convert Ipmi completion code byte to known Chassis Manager
                                            // completion code enum.
                                            completionCode = (CompletionCode)rawCompletionCode;
                                        }
                                        else
                                        {
                                            // unable to convert Ipmi completion code "byte" to Chassis Manager
                                            // completion code enum.
                                            completionCode = CompletionCode.IpmiInvalidCommand;
                                        }
                                    }
                                    // else successful completion code
                                    else
                                    {
                                        completionCode = CompletionCode.Success;
                                    }
                                }
                                else
                                {
                                    completionCode = CompletionCode.UnspecifiedError;
                                    Tracer.WriteError(string.Format(
                                        "WriteMultiRecordFru() Unknown Device Type. The device type: {0}", deviceType));
                                    break;
                                }

                                if (completionCode != CompletionCode.Success)
                                {
                                    Tracer.WriteInfo("WriteMultiRecordFru(). FRU Write unsuccessful, so breaking out of write while loop");
                                    break;
                                }

                                // Increment offset/index to account for the next 16 byte chunk of payload
                                writeOffset += (ushort)maxPayLoadSize;
                                writeIdx += maxPayLoadSize;
                                writes++;

                                if (writes > allowedWrites)
                                {
                                    Tracer.WriteInfo(@"WriteMultiRecordFru(). Number of FRU writes have been exhausted.
                                                       The data is greater than writes by max allowed writes");
                                }
                            }
                        }
                        catch (Exception ex)
                        {
                            Tracer.WriteError("WriteMultiRecordFru(). Exception occcurred while writing to FRU: " + ex);
                        }

                        if (completionCode == CompletionCode.Success)
                        {
                            setAsset.completionCode = Contracts.CompletionCode.Success;
                        }
                        else
                        {
                            setAsset.completionCode = ChassisManagerUtil.GetContractsCompletionCodeMapping((byte)completionCode);
                            setAsset.statusDescription = setAsset.completionCode.ToString();
                        }
                    }
                    else
                    {
                        Tracer.WriteError("WriteMultiRecordFru(). Maximum bytes reached and cannot write.");
                        setAsset.completionCode = Contracts.CompletionCode.WriteFruMaxRecordSizeReached;
                        setAsset.statusDescription = setAsset.completionCode.ToString();
                        return setAsset;
                    }
                }
                else
                {
                    Tracer.WriteInfo(string.Format("WriteMultiRecordFru(). Write FRU returned non-success completion code: {0}",
                        completionCode.ToString()));
                    setAsset.completionCode = Contracts.CompletionCode.Failure;
                    return setAsset;
                }
            }
            catch (Exception ex)
            {
                Tracer.WriteError("WriteMultiRecordFru(). Exception occurred in WriteMultiRecordFru method with details " + ex.Message);
                setAsset.completionCode = Contracts.CompletionCode.Failure;
                setAsset.statusDescription = setAsset.completionCode.ToString();
            }

            return setAsset;
        }
        private void VerifyCmOrPdbFruWritesRemaining(ref bool allPassed, bool verifyingSetCmAssetInfo)
        {
            bool testPassed;
            string currentApi = verifyingSetCmAssetInfo ? "SetChassisManagerAssetInfo" : "SetPdbAssetInfo";
            MultiRecordResponse cmOrPdbAssetInfo = new MultiRecordResponse();

            // Initialize FRU Writes Remaining Dictionary KeyValue Pair
            Dictionary<string, string> fruWritesRemainingKeyValue = new Dictionary<string, string>
            {
                {"ResetMultiRecordFruWritesRemaining", "0"}
            };

            // Set Channel for WcsCmAdmin User
            this.TestChannelContext = this.ListTestChannelContexts[(int)WCSSecurityRole.WcsCmAdmin];

            if (!ConfigureAppConfig(fruWritesRemainingKeyValue, false))
            {
                CmTestLog.Failure(string.Format("{0}: Setting App.Config failed for KeyValue '{1},{2}'",
                    currentApi, fruWritesRemainingKeyValue.Keys.First(), fruWritesRemainingKeyValue.Values.First()));
                allPassed = false;
                return;
            }

            // Restart CM Service
            if (!RestartCmService(currentApi))
            {
                allPassed = false;
                return;
            }

            // Fail FRU Writes Remaining before starting test
            int writeCount = 0;
            while (cmOrPdbAssetInfo.completionCode != CompletionCode.WriteFruZeroWritesRemaining &&
                writeCount < (AssetManagementConstants.fruWritesRemaining + 1))
            {
                if (verifyingSetCmAssetInfo)
                    cmOrPdbAssetInfo = this.TestChannelContext.SetChassisManagerAssetInfo(string.Empty);
                else
                    cmOrPdbAssetInfo = this.TestChannelContext.SetPdbAssetInfo(string.Empty);

                if ((cmOrPdbAssetInfo.completionCode != CompletionCode.Success)
                    && (cmOrPdbAssetInfo.completionCode != CompletionCode.WriteFruZeroWritesRemaining))
                {
                    CmTestLog.Failure(string.Format("{0}: Command returned Completion Code {1} while trying to return {2}",
                        currentApi, Enum.GetName(typeof(CompletionCode), cmOrPdbAssetInfo.completionCode), "WriteFruZeroWritesRemaining"));
                    allPassed = false;
                    return;
                }
                writeCount++;
            }
            if (cmOrPdbAssetInfo.completionCode != CompletionCode.WriteFruZeroWritesRemaining)
            {
                CmTestLog.Failure(string.Format("{0}: Command returned Completion Code {1} while trying to return {2}",
                        currentApi, Enum.GetName(typeof(CompletionCode), cmOrPdbAssetInfo.completionCode), "WriteFruZeroWritesRemaining"));
                allPassed = false;
                return;
            }

            CmTestLog.Info(string.Format("{0}: FRU Writes Remaining set to 0", currentApi));

            // Restore App.Config with Original Values
            if (!ConfigureAppConfig(fruWritesRemainingKeyValue, true))
            {
                CmTestLog.Failure(string.Format("{0}: App.Config cleanup failed", currentApi));
                allPassed = false;
                return;
            }

            // Restart CM Service
            if (!RestartCmService(currentApi))
            {
                allPassed = false;
                return;
            }

            // Verify SetChassisManagerAssetInfo resets number of writes to default value after configuring App.Config : WorkItem(8709)
            // Verify SetPdbAssetInfo resets number of writes to default value after configuring App.Config : WorkItem(10173)

            // Set App.Config key "ResetMultiRecordFruWritesRemaining" to value "1"
            fruWritesRemainingKeyValue["ResetMultiRecordFruWritesRemaining"] = "1";
            if (!ConfigureAppConfig(fruWritesRemainingKeyValue, false))
            {
                CmTestLog.Failure(string.Format("{0}: Setting App.Config failed for KeyValue '{1},{2}'",
                    currentApi, fruWritesRemainingKeyValue.Keys.First(), fruWritesRemainingKeyValue.Values.First()));
                allPassed = false;
                return;
            }

            // Restart CM Service
            if (!RestartCmService(currentApi))
            {
                allPassed = false;
                return;
            }

            if (verifyingSetCmAssetInfo)
                cmOrPdbAssetInfo = this.TestChannelContext.SetChassisManagerAssetInfo(string.Empty);
            else
                cmOrPdbAssetInfo = this.TestChannelContext.SetPdbAssetInfo(string.Empty);

            testPassed = ChassisManagerTestHelper.AreEqual(CompletionCode.Success, cmOrPdbAssetInfo.completionCode,
                string.Format("{0}: Command resets number of writes to default value", currentApi));
            allPassed &= testPassed;

            // Restore App.Config with Original Values
            if (!ConfigureAppConfig(fruWritesRemainingKeyValue, true))
            {
                CmTestLog.Failure(string.Format("{0}: App.Config cleanup failed", currentApi));
                allPassed = false;
                return;
            }

            if (!allPassed)
            {
                RestartCmService(currentApi);
                return;
            }

            // Starting test: Set command fails for attempting to set payload with 0 writes remaining
            // Set App.Config key "ResetMultiRecordFruWritesRemaining" to value "0"
            fruWritesRemainingKeyValue["ResetMultiRecordFruWritesRemaining"] = "0";

            if (!ConfigureAppConfig(fruWritesRemainingKeyValue, false))
            {
                CmTestLog.Failure(string.Format("{0}: Setting App.Config failed for KeyValue '{1},{2}'",
                    currentApi, fruWritesRemainingKeyValue.Keys.First(), fruWritesRemainingKeyValue.Values.First()));
                allPassed = false;
                return;
            }

            // Restart CM Service
            if (!RestartCmService(currentApi))
            {
                allPassed = false;
                return;
            }

            // Verify SetChassisManagerAssetInfo command should return WritFruZeroWritesRemaining after 256 iterations : WorkItem(4711)
            // Verify SetPdbAssetInfo command should return WritFruZeroWritesRemaining after 256 iterations : WorkItem(4713)
            for (int callApiCount = 0; callApiCount < 255; callApiCount++)
            {
                if (verifyingSetCmAssetInfo)
                    cmOrPdbAssetInfo = this.TestChannelContext.SetChassisManagerAssetInfo(string.Empty);
                else
                    cmOrPdbAssetInfo = this.TestChannelContext.SetPdbAssetInfo(string.Empty);

                if (cmOrPdbAssetInfo.completionCode != CompletionCode.Success)
                {
                    CmTestLog.Failure(string.Format("{0}: Command returns Completion Code {1} on callApiCount {2} before {3}",
                        currentApi, Enum.GetName(typeof(CompletionCode), cmOrPdbAssetInfo.completionCode),
                        callApiCount, "WriteFruZeroWritesRemaining"));
                    allPassed = false;
                    return;
                }
            }

            if (verifyingSetCmAssetInfo)
                cmOrPdbAssetInfo = this.TestChannelContext.SetChassisManagerAssetInfo(string.Empty);
            else
                cmOrPdbAssetInfo = this.TestChannelContext.SetPdbAssetInfo(string.Empty);

            testPassed = ChassisManagerTestHelper.AreEqual(CompletionCode.WriteFruZeroWritesRemaining, cmOrPdbAssetInfo.completionCode,
                string.Format("{0}: Command returns Completion Code {1} after 256 calls",
                currentApi, "WriteFruZeroWritesRemaining"));
            allPassed &= testPassed;

            // Restore App.Config with Original Values
            if (!ConfigureAppConfig(fruWritesRemainingKeyValue, true))
            {
                CmTestLog.Failure(string.Format("{0}: App.Config cleanup failed", currentApi));
                allPassed = false;
                return;
            }

            // Set App.Config key "ResetMultiRecordFruWritesRemaining" to value "1"
            fruWritesRemainingKeyValue["ResetMultiRecordFruWritesRemaining"] = "1";
            if (!ConfigureAppConfig(fruWritesRemainingKeyValue, false))
            {
                CmTestLog.Failure(string.Format("{0}: Setting App.Config failed for KeyValue '{1},{2}'",
                    currentApi, fruWritesRemainingKeyValue.Keys.First(), fruWritesRemainingKeyValue.Values.First()));
                allPassed = false;
                return;
            }

            // Restart CM Service
            if (!RestartCmService(currentApi))
            {
                allPassed = false;
                return;
            }

            if (verifyingSetCmAssetInfo)
                cmOrPdbAssetInfo = this.TestChannelContext.SetChassisManagerAssetInfo(string.Empty);
            else
                cmOrPdbAssetInfo = this.TestChannelContext.SetPdbAssetInfo(string.Empty);
            allPassed &= ChassisManagerTestHelper.AreEqual(CompletionCode.Success, cmOrPdbAssetInfo.completionCode,
                string.Format("{0}: FRU Writes Remaining reset", currentApi));

            // Restore App.Config with Original Values
            if (!ConfigureAppConfig(fruWritesRemainingKeyValue, true))
            {
                CmTestLog.Failure(string.Format("{0}: App.Config cleanup failed", currentApi));
                allPassed = false;
            }

            // Restart CM Service
            if (!RestartCmService(currentApi))
            {
                allPassed = false;
            }

            return;
        }