/// <summary>
        /// Downloads the firmware file to the meter and activates it.
        /// The activation will cause the meter to drop the psem task so meter log off must
        /// follow this function call on success.  This method supports resuming
        /// a previous failed FWDL.
        /// </summary>
        /// <param name="path">Complete file path of the firmware file</param>
        /// <param name="usBlockIndex">Dual purpose parameter. The passed in value indicates
        /// which block to begin downloading. The passed out parameter indicates which block to
        /// resume downloading in case there was a failure. This can then passed in again to
        /// restart the download at the point where it left off.</param>
        /// <param name="blnRetry">Whether or not to leave the FWDL in a state
        /// to permit subsequent retries at point of faliure. If false the pending table
        /// will be cleared on failure.</param>
        /// <param name="blnActivate">Whether or not to activate the firmware.</param>
        /// <returns>Itron.Metering.Device.FWDownloadResult</returns>
        // Revision History
        // MM/DD/YY who Version ID Number Description
        // -------- --- ------- -- ------ ---------------------------------------
        // 08/28/06 AF  7.35.00    N/A    Created
        // 09/15/06 AF  7.35.00    N/A    Added Catch for TimeOutException
        // 10/18/06 AF  7.40.00    N/A    Removed wait within the main loop
        // 05/13/08 AF  1.50.24           Removed IFirmwareDownload from the method name
        // 04/19/10 AF  2.40.39           Added M2 Gateway support
        // 08/18/11 AF  2.52.05           Added support for authentication using a hash code
        // 08/26/11 AF  2.52.08           Added support for Cisco f/w
        // 09/22/11 AF  2.52.21    N/A    Added support for Cisco config file f/w d/l - TODO remove when no longer needed
        // 10/12/11 AF  2.53.00           Changed the Cisco Comm fw enum name
        // 03/22/12 JJJ 2.60.xx           Added support for ChoiceConnect FW
        // 05/10/12 JJJ 2.60.xx           Tweaked FW Type passed to AuthenticateFWDL if ChoiceConnect, make RFLAN
        // 04/19/13 jrf 2.80.21 TQ 7639   Adding support for ICS comm module FWDL.
        // 07/08/13 jrf 2.80.51 TC 13201  Created to support retries of FWDL.
        // 07/15/13 jrf 2.80.?? TC 15062  Added parameter to control activation.
        // 08/22/13 jrf 2.85.26 WR 420902 Decrementing the block index on a failed block write, so
        //                                on a retry we will start back on the correct block.
        // 11/07/13 AF  3.50.02 TQ9508,9514 Have to activate the pending table using mfg proc 69 rather than std proc 13
        //                                  for I-210 and kV2c ICM firmware
        //  11/15/13 AF  3.50.03	    Class re-architecture - removed code specific to ICS_Gateway
        // 12/02/13 jrf 3.50.10            Refactored code used to modify the FWType byte to it's own method.
        // 02/07/14 jrf 3.50.32 WR 419257  Modified to use a dynamic FWDL block size based on the negotiated
        //                                 PSEM packet size.
        // 08/24/16 PGH 4.70.15 701952     Added HAN OTA Firmware
        // 02/06/17 AF  4.71.07 743128     Added supported for ICM modem firmware
        // 03/10/17 AF  4.71.09 749833     Renamed the firmware type because 37 is for Verizon LTE only
        // 12/05/17 AF  4.73.00 Task 469253 Added back the verizon modem fwdl
        // 12/06/17 AF  4.73.00 Task 469254 Added ATT/Rogers modem fwdl
        //
        public FWDownloadResult DownloadFW(string path, ref ushort usBlockIndex, bool blnRetry = false, bool blnActivate = true)
        {
            FWDownloadResult     Result           = FWDownloadResult.UNKNOWN_DRIVER_ERROR;
            ProcedureResultCodes ProcResult       = ProcedureResultCodes.INVALID_PARAM;
            PSEMResponse         ProtocolResponse = PSEMResponse.Ok;
            byte   byEventNumber  = 0;
            ushort idTable        = (ushort)PendingTableIds.RegisterFWTbl | PENDING_BIT;
            ushort usNumberChunks = 0;
            //A non-zero starting block means we are need to pick up were we left off.
            bool blnResumeFWDL = (0 != usBlockIndex);

            System.IO.FileStream   streamFile;
            System.IO.MemoryStream streamHeader = new System.IO.MemoryStream();
            System.IO.MemoryStream streamPSEM   = new System.IO.MemoryStream();

            try
            {
                if (true == blnResumeFWDL)
                {
                    Result = FWDownloadResult.SUCCESS;
                }
                else
                {
                    // Tell the meter to enter firmware download mode
                    Result = EnterFirmwareDownloadMode(path);
                }


                if (FWDownloadResult.SUCCESS != Result)
                {
                    m_Logger.WriteLine(Logger.LoggingLevel.Detailed,
                                       "Initiate F/W Download procedure failed with result = "
                                       + Result);
                }
                else
                {
                    // Meter is ready to receive the firmware file
                    streamFile = new System.IO.FileStream(path, System.IO.FileMode.Open, FileAccess.Read);
                    byte[] bybuffer = new byte[streamFile.Length];

                    streamFile.Read(bybuffer, 0, (int)streamFile.Length);
                    streamFile.Position = 0;

                    switch (bybuffer[9])
                    {
                    case (byte)FirmwareType.RegisterFW:
                    case (byte)FirmwareType.M2GTWY:
                    case (byte)FirmwareType.DisplayFW:
                    {
                        byEventNumber = REGISTER_EVENT_NUMBER;
                        idTable       = (ushort)PendingTableIds.RegisterFWTbl | PENDING_BIT;
                        break;
                    }

                    case (byte)FirmwareType.ZigbeeFW:
                    {
                        byEventNumber = ZIGBEE_EVENT_NUMBER;
                        idTable       = (ushort)PendingTableIds.HANModuleFWTbl | PENDING_BIT;
                        break;
                    }

                    case (byte)FirmwareType.RFLANFW:
                    case (byte)FirmwareType.PLANFW:
                    case (byte)FirmwareType.CiscoCommFW:
                    case (byte)FirmwareType.CiscoCfgFW:
                    case (byte)FirmwareType.ChoiceConnectFW:
                    case (byte)FirmwareType.ICSFW:
                    case (byte)FirmwareType.ICS_MODEM_FW_Sierra_Verizon_LTE:
                    case (byte)FirmwareType.ICS_MODEM_FW_Sierra_ATT_Rogers_Bell_LTE:
                    {
                        byEventNumber = COMM_EVENT_NUMBER;
                        idTable       = (ushort)PendingTableIds.CommModuleFWTbl | PENDING_BIT;
                        break;
                    }

                    case (byte)FirmwareType.HAN_OTA_FW:
                    case (byte)FirmwareType.HANDevFW:
                    {
                        byEventNumber = HAN_DEV_EVENT_NUMBER;
                        idTable       = (ushort)PendingTableIds.HANDeviceFWTbl | PENDING_BIT;
                        break;
                    }

                    default:
                    {
                        throw new NotImplementedException("Table not supported");
                    }
                    }

                    BuildPendingHeader(ref streamHeader, false, false,
                                       byEventNumber, PendingEventRecord.PendingEventCode.NonTimeTrigger);

                    usNumberChunks = (ushort)(streamFile.Length / FWDLBlockSize);

                    if (streamFile.Length != FWDLBlockSize * usNumberChunks)
                    {
                        usNumberChunks++;
                    }

                    OnShowProgress(new ShowProgressEventArgs(1, usNumberChunks + 1,
                                                             "Firmware Download",
                                                             "Downloading..."));

                    ushort usSendSize = FWDLBlockSize;

                    for (;
                         (usBlockIndex < usNumberChunks) && (PSEMResponse.Ok == ProtocolResponse);
                         usBlockIndex++)
                    {
                        // The last chunk could be smaller
                        if (usNumberChunks - 1 == usBlockIndex)
                        {
                            usSendSize = (ushort)(streamFile.Length % FWDLBlockSize);
                            // If no remainder then it is a full packet
                            if (0 == usSendSize)
                            {
                                usSendSize = FWDLBlockSize;
                            }
                        }

                        streamHeader.Position = 0;
                        streamPSEM.Position   = 0;
                        streamPSEM.SetLength(0);
                        streamHeader.WriteTo(streamPSEM);
                        streamPSEM.Write(bybuffer, usBlockIndex * FWDLBlockSize, usSendSize);

                        ProtocolResponse = m_PSEM.OffsetWrite((ushort)idTable,
                                                              usBlockIndex * FWDLBlockSize,
                                                              streamPSEM.ToArray());
                        OnStepProgress(new ProgressEventArgs());
                    }

                    // Translate Protocol result
                    Result = TranslateProtocolResult(ProtocolResponse);

                    streamFile.Close();

                    //Check on success and then activate the table
                    if (PSEMResponse.Ok == ProtocolResponse)
                    {
                        if (FWDLLogSupported)
                        {
                            //Construct the hash code and call the procedure to authenticate
                            CENTRON_AMI_FW_File FWFile = new CENTRON_AMI_FW_File(path);
                            byte[] FWHashCode          = FWFile.HashCode;

                            if ((bybuffer[9] != (byte)FirmwareType.ICSFW) && (bybuffer[9] != (byte)FirmwareType.ICS_MODEM_FW_Sierra_Verizon_LTE))
                            {
                                //Some devices may require this byte to be adjusted.
                                bybuffer[9] = SelectFWTypeByte(bybuffer[9]);

                                ProcResult = AuthenticateFWDL(idTable, bybuffer[9], FWHashCode);
                            }
                            else //Skip authenticate for ICS comm module and modem FWDL
                            {
                                ProcResult = ProcedureResultCodes.COMPLETED;
                            }
                        }
                        else
                        {
                            ProcResult = ProcedureResultCodes.COMPLETED;
                        }

                        if (ProcResult == ProcedureResultCodes.COMPLETED)
                        {
                            if (true == blnActivate)
                            {
                                // Activate the pending table using std proc 13
                                ProcResult = ActivatePendingTable(false, false, byEventNumber,
                                                                  PendingEventRecord.PendingEventCode.NonTimeTrigger);

                                Result = TranslateProcedureResult(ProcResult);
                            }
                        }
                        else
                        {
                            if (false == blnRetry)
                            {
                                //We couldn't authenticate using the hash code so activation will fail
                                ProcResult = ClearPendingTable(false, false, byEventNumber,
                                                               PendingEventRecord.PendingEventCode.NonTimeTrigger);
                            }

                            //TODO - not sure this is the correct error
                            Result = FWDownloadResult.SECURITY_ERROR;
                        }

                        OnStepProgress(new ProgressEventArgs());
                    }
                    else //PSEMResponse.Ok != ProtocolResponse
                    {
                        //Decrement the block index so we make sure we restart on the block that we failed on.
                        usBlockIndex--;

                        if (false == blnRetry)
                        {
                            // Write failed, so clear the pending table
                            ProcResult = ClearPendingTable(false, false, byEventNumber,
                                                           PendingEventRecord.PendingEventCode.NonTimeTrigger);
                        }

                        Result = FWDownloadResult.WRITE_ERROR;
                    }
                    OnHideProgress(new EventArgs());
                }
            }

            catch (Exception e)
            {
                if (false == blnRetry)
                {
                    // Log it and pass it up
                    OnHideProgress(new EventArgs());
                    m_Logger.WriteException(this, e);
                    throw e;
                }
                else
                {
                    Result = FWDownloadResult.WRITE_ERROR;
                }
            }

            return(Result);
        }
예제 #2
0
        /// <summary>
        /// Downloads the firmware file to the meter and activates it.
        /// On download failure, the pending table is cleared.  The activation
        /// will cause the meter to drop the psem task so meter log off must
        /// follow this function call
        /// </summary>
        /// <param name="path">Complete file path of the firmware file</param>
        /// <returns>Itron.Metering.Device.FWDownloadResult</returns>
        // Revision History
        // MM/DD/YY who Version Issue# Description
        // -------- --- ------- ------ ---------------------------------------
        // 08/28/06 AF  7.35.00  N/A   Created
        // 09/15/06 AF  7.35.00  N/A   Added Catch for TimeOutException
        // 10/18/06 AF  7.40.00  N/A   Removed wait within the main loop
        // 05/13/08 AF  1.50.24        Removed IFirmwareDownload from the method name
        // 04/19/10 AF  2.40.39        Added M2 Gateway support
        // 08/18/11 AF  2.52.05        Added support for authentication using a hash code
        // 08/26/11 AF  2.52.08        Added support for Cisco f/w
        // 09/22/11 AF  2.52.21  N/A   Added support for Cisco config file f/w d/l - TODO remove when no longer needed
        // 10/12/11 AF  2.53.00        Changed the Cisco Comm fw enum name
        // 03/22/12 JJJ 2.60.xx        Added support for ChoiceConnect FW
        // 05/10/12 JJJ 2.60.xx        Tweaked FW Type passed to AuthenticateFWDL if ChoiceConnect, make RFLAN
        //
        public FWDownloadResult DownloadFW(string path)
        {
            FWDownloadResult     Result           = FWDownloadResult.UNKNOWN_DRIVER_ERROR;
            ProcedureResultCodes ProcResult       = ProcedureResultCodes.INVALID_PARAM;
            PSEMResponse         ProtocolResponse = PSEMResponse.Ok;
            byte   byEventNumber  = 0;
            ushort idTable        = (ushort)PendingTableIds.RegisterFWTbl | PENDING_BIT;
            ushort usNumberChunks = 0;
            ushort intIndex;

            System.IO.FileStream   streamFile;
            System.IO.MemoryStream streamHeader = new System.IO.MemoryStream();
            System.IO.MemoryStream streamPSEM   = new System.IO.MemoryStream();

            try
            {
                // Tell the meter to enter firmware download mode
                Result = EnterFirmwareDownloadMode(path);

                if (FWDownloadResult.SUCCESS != Result)
                {
                    m_Logger.WriteLine(Logger.LoggingLevel.Detailed,
                                       "Initiate F/W Download procedure failed with result = "
                                       + Result);
                }
                else
                {
                    // Meter is ready to receive the firmware file
                    streamFile = new System.IO.FileStream(path, System.IO.FileMode.Open, FileAccess.Read);
                    byte[] bybuffer = new byte[streamFile.Length];

                    streamFile.Read(bybuffer, 0, (int)streamFile.Length);
                    streamFile.Position = 0;

                    switch (bybuffer[9])
                    {
                    case (byte)FirmwareType.RegisterFW:
                    case (byte)FirmwareType.M2GTWY:
                    case (byte)FirmwareType.DisplayFW:
                    {
                        byEventNumber = REGISTER_EVENT_NUMBER;
                        idTable       = (ushort)PendingTableIds.RegisterFWTbl | PENDING_BIT;
                        break;
                    }

                    case (byte)FirmwareType.ZigbeeFW:
                    {
                        byEventNumber = ZIGBEE_EVENT_NUMBER;
                        idTable       = (ushort)PendingTableIds.HANModuleFWTbl | PENDING_BIT;
                        break;
                    }

                    case (byte)FirmwareType.RFLANFW:
                    case (byte)FirmwareType.PLANFW:
                    case (byte)FirmwareType.CiscoCommFW:
                    case (byte)FirmwareType.CiscoCfgFW:
                    case (byte)FirmwareType.ChoiceConnectFW:
                    {
                        byEventNumber = COMM_EVENT_NUMBER;
                        idTable       = (ushort)PendingTableIds.CommModuleFWTbl | PENDING_BIT;
                        break;
                    }

                    case (byte)FirmwareType.HANDevFW:
                    {
                        byEventNumber = HAN_DEV_EVENT_NUMBER;
                        idTable       = (ushort)PendingTableIds.HANDeviceFWTbl | PENDING_BIT;
                        break;
                    }

                    default:
                    {
                        throw new NotImplementedException("Table not supported");
                    }
                    }

                    BuildPendingHeader(ref streamHeader, false, false,
                                       byEventNumber, PendingEventRecord.PendingEventCode.NonTimeTrigger);

                    usNumberChunks = (ushort)(streamFile.Length / BLOCK_SIZE);

                    if (streamFile.Length != BLOCK_SIZE * usNumberChunks)
                    {
                        usNumberChunks++;
                    }

                    OnShowProgress(new ShowProgressEventArgs(1, usNumberChunks + 1,
                                                             "Firmware Download",
                                                             "Downloading..."));

                    ushort usSendSize = BLOCK_SIZE;

                    for (intIndex = 0;
                         (intIndex < usNumberChunks) && (PSEMResponse.Ok == ProtocolResponse);
                         intIndex++)
                    {
                        // The last chunk could be smaller
                        if (usNumberChunks - 1 == intIndex)
                        {
                            usSendSize = (ushort)(streamFile.Length % BLOCK_SIZE);
                            // If no remainder then it is a full packet
                            if (0 == usSendSize)
                            {
                                usSendSize = BLOCK_SIZE;
                            }
                        }

                        streamHeader.Position = 0;
                        streamPSEM.Position   = 0;
                        streamPSEM.SetLength(0);
                        streamHeader.WriteTo(streamPSEM);
                        streamPSEM.Write(bybuffer, intIndex * BLOCK_SIZE, usSendSize);

                        ProtocolResponse = m_PSEM.OffsetWrite((ushort)idTable,
                                                              intIndex * BLOCK_SIZE,
                                                              streamPSEM.ToArray());
                        OnStepProgress(new ProgressEventArgs());
                    }

                    // Translate Protocol result
                    Result = TranslateProtocolResult(ProtocolResponse);

                    streamFile.Close();

                    //Check on success and then activate the table
                    if (PSEMResponse.Ok == ProtocolResponse)
                    {
                        if (FWDLLogSupported)
                        {
                            //Construct the hash code and call the procedure to authenticate
                            CENTRON_AMI_FW_File FWFile = new CENTRON_AMI_FW_File(path);
                            byte[] FWHashCode          = FWFile.HashCode;

                            // if MSM ChoiceConnect meter and ChoiceConnect FWDL request, spoof RFLAN FWDL
                            CENTRON_AMI AmiDevice = this as CENTRON_AMI;
                            if (AmiDevice != null &&
                                bybuffer[9] == (byte)FirmwareType.ChoiceConnectFW &&
                                AmiDevice.IsChoiceConnectMsmMeter)
                            {
                                bybuffer[9] = (byte)FirmwareType.RFLANFW;
                            }

                            ProcResult = AuthenticateFWDL(idTable, bybuffer[9], FWHashCode);
                        }
                        else
                        {
                            ProcResult = ProcedureResultCodes.COMPLETED;
                        }

                        if (ProcResult == ProcedureResultCodes.COMPLETED)
                        {
                            // Activate the pending table
                            ProcResult = ActivatePendingTable(false, false, byEventNumber,
                                                              PendingEventRecord.PendingEventCode.NonTimeTrigger);

                            Result = TranslateProcedureResult(ProcResult);
                        }
                        else
                        {
                            //We couldn't authenticate using the hash code so activation will fail
                            ProcResult = ClearPendingTable(false, false, byEventNumber,
                                                           PendingEventRecord.PendingEventCode.NonTimeTrigger);

                            //TODO - not sure this is the correct error
                            Result = FWDownloadResult.SECURITY_ERROR;
                        }

                        OnStepProgress(new ProgressEventArgs());
                    }
                    else
                    {
                        // Write failed, so clear the pending table
                        ProcResult = ClearPendingTable(false, false, byEventNumber,
                                                       PendingEventRecord.PendingEventCode.NonTimeTrigger);

                        Result = FWDownloadResult.WRITE_ERROR;
                    }
                    OnHideProgress(new EventArgs());
                }
            }

            catch (Exception e)
            {
                // Log it and pass it up
                OnHideProgress(new EventArgs());
                m_Logger.WriteException(this, e);
                throw e;
            }

            return(Result);
        }