/// <summary>
        /// Creates the set request file for edl40 mode.
        /// </summary>
        /// <param name="portId">The port id.</param>
        /// <param name="operationMode">The operation mode.</param>
        /// <returns></returns>
        public static SmlFile CreateSetRequestFileForOperationMode(string portId, BooleanParam operationMode)
        {
            var handler = new SmlHandler(portId);
            var smlFile = new SmlFile();
            handler.AddOpenRequest(smlFile);

            handler.AddSetBooleanParamRequest(
               smlFile, operationMode);

            handler.AddCloseRequest(smlFile);

            return smlFile;
        }
        public async Task WriteEnableTariffFunction(Guid clientId, CommunicationPort communicationPort, bool isEnabled)
        {
            var port = CheckConfiguration(communicationPort, clientId);

            var param = new BooleanParam()
            {
                ObisCode = VendorSpecificRegisterIds.TariffEnabled.AsLong(),
                Data = isEnabled
            };

            await port.ComThread.WriteParameter(param);
        }
        /// <summary>
        /// Adds the set boolean param request.
        /// </summary>
        /// <param name="reqFile">The req file.</param>
        /// <param name="param">The parameter.</param>
        private void AddSetBooleanParamRequest(SmlFile reqFile, BooleanParam param)
        {
            var msg = new Core.Sml.Messages.Message()
            {
                TransactionId = new[] { (byte)(reqFile.Count + 1) },
                GroupNo = 0x00,
                SetProcParameterRequest = new Core.Sml.Messages.SetProcParameterRequest()
                {
                    TreePath = new List<Core.Obis.ObisId>()
                    {
                        new Core.Obis.ObisId((ulong)param.ObisCode)
                    },
                    Tree = new Tree(new Core.Obis.ObisId((ulong)param.ObisCode), param.Data.Value)
                }
            };

            //var obisId = (ObisId)param.ObisCode;
            //var paramTreePath = new SmlTreePath(obisId);
            //var tree = new SmlTree { ParameterName = new SmlOctetstring(ObisUtil.GetBytes(obisId)) };
            //if (param.Data.HasValue == false)
            //{
            //    throw new ArgumentNullException("param");
            //}

            //tree.ParameterValue = new SmlProcParValue(new SmlBoolean(param.Data.Value));
            //var msg = SmlMessageFactory.SetProcParamReq(paramTreePath, tree);

            //var transactionId = new byte[] { (byte)(reqFile.Count + 1) };
            //msg.TransactionId = new SmlOctetstring(transactionId);
            //msg.GroupNo = 0x00;
            reqFile.Add(msg);
        }
        public async Task<BooleanParam> ReadParameter(BooleanParam param)
        {
            var obisId = new ObisId((ulong) param.ObisCode);

            if (obisId == RegisterIds.GridOptionEnabled)
            {
                var hasGridOption = await this.productionParameters.GetGridOption().ConfigureAwait(false);
                return new BooleanParam()
                {
                    ObisCode = param.ObisCode,
                    Data = hasGridOption
                };
            }

            if (obisId == RegisterIds.BaudrateOptionEnabled)
            {
                var hasBaudRateOption = await this.productionParameters.GetBaudrateOptionEnabled().ConfigureAwait(false);
                return new BooleanParam()
                {
                    ObisCode = param.ObisCode,
                    Data = hasBaudRateOption
                };
            }

            if (param.ObisCode == ObisEdl.DzgTerminalControlReverse.AsLong())
            {
                var tc = await this.baseMeterDevice.TariffControl.GetTariffConfiguration().ConfigureAwait(false);
                
                return new BooleanParam()
                {
                    ObisCode = param.ObisCode,
                    Data = tc.HasFlag(TariffConfiguration.OnActivationUseT1)
                };
            }

            var result = await this.baseMeterDevice.ReadRegisterAsync(obisId).ConfigureAwait(false);
            if (result == null)
            {
                throw new NullReferenceException();
            }

            return new BooleanParam()
            {
                ObisCode = param.ObisCode, Data = result.GetAsBool()
            };
        }
        /// <summary>
        /// Reads the is operation mode edl40.
        /// </summary>
        /// <returns></returns>
        public async Task<BooleanParam> ReadParameter(BooleanParam param)
        {
            logger.Info("Reading boolean parameter.");

            return await Retry.Do(
                async () =>
                {
                    var requestFile = SmlHandler.CreateGetRequestFile(portName, param.ObisCode);
                    var resVal = await SendFile(requestFile).ConfigureAwait(false);
                    CheckAttentionResponse(param.ObisCode, resVal);
                   
                    var val = Convert.ToBoolean(resVal);
                    param.Data = val;

                    logger.Info("Responding with boolean result: {0}.", param.Data);
                    return param;
                });
        }
        /// <summary>
        /// Writes the is manipulation detection active.
        /// </summary>
        /// <param name="clientId">The client id.</param>
        /// <param name="communicationPort">The communication port.</param>
        /// <param name="isActive">if set to <c>true</c> [is active].</param>
        public async Task WriteIsManipulationDetectionActive(Guid clientId, CommunicationPort communicationPort, bool isActive)
        {
            CheckConfiguration(communicationPort, clientId);

            var boolParam = new BooleanParam {ObisCode = (long) ObisId.EdlIsManipulationDectectionActive, Data = isActive};
            await ClientComListDict[communicationPort.ComPortName].ComThread.WriteParameter(boolParam);
        }
        /// <summary>
        /// Writes the is info interface activated.
        /// </summary>
        /// <param name="clientId">The client id.</param>
        /// <param name="communicationPort">The communication port.</param>
        /// <param name="isActive">if set to <c>true</c> [is active].</param>
        public async Task WriteIsInfoInterfaceActivated(Guid clientId, CommunicationPort communicationPort, bool isActive)
        {
            var port = CheckConfiguration(communicationPort, clientId);

            var boolParam = new BooleanParam { ObisCode = (long)ObisId.EdlInfoInterfaceActive, Data = isActive };
            await port.ComThread.WriteParameter(boolParam);
        }
        /// <summary>
        /// Writes the is manufacturer specific billing data used.
        /// </summary>
        /// <param name="clientId">The client id.</param>
        /// <param name="communicationPort">The communication port.</param>
        /// <param name="isUsed">if set to <c>true</c> [is used].</param>
        public async Task WriteIsManufacturerSpecificBillingDataUsed(Guid clientId, CommunicationPort communicationPort, bool isUsed)
        {
            CheckConfiguration(communicationPort, clientId);

            var boolParam = new BooleanParam { ObisCode = (long)ObisId.EdlManufacturerPushData, Data = isUsed };
            await ClientComListDict[communicationPort.ComPortName].ComThread.WriteParameter(boolParam);
        }
        /// <summary>
        /// When TRUE: Opened meter is in factory mode
        /// When FALSE: Opened meter is not in factory mode (last step in production)
        /// </summary>
        /// <param name="clientId"></param>
        /// <param name="communicationPort"></param>
        /// <param name="isEnabled"></param>
        public async Task WriteEnableFactoryModeK2Enabled(Guid clientId, CommunicationPort communicationPort, bool isEnabled)
        {
            var port = CheckConfiguration(communicationPort, clientId);

            var param = new BooleanParam()
            {
                ObisCode = RegisterIds.KeyS2Mode.AsLong(),
                Data = isEnabled
            };

            await port.ComThread.WriteParameter(param);
        }
        public async Task<BooleanParam> ReadEnableFactoryModeK2Enabled(Guid clientId, CommunicationPort communicationPort)
        {
            var port = CheckConfiguration(communicationPort, clientId);

            var param = new BooleanParam
            {
                Data = null,
                ObisCode = RegisterIds.KeyS2Mode.AsLong()
            };

            param = await port.ComThread.ReadParameter(param);
            return param;
        }
        public async Task WriteReset(Guid clientId, CommunicationPort communicationPort, bool doReset)
        {
            var port = CheckConfiguration(communicationPort, clientId);

            var param = new BooleanParam()
            {
                ObisCode = RegisterIds.ResetDevice.AsLong(),
                Data = doReset
            };

            await port.ComThread.WriteParameter(param);
        }
        public async Task WriteEnableBaudRateSetting(Guid clientId, CommunicationPort communicationPort, bool isEnabled)
        {
            var port = CheckConfiguration(communicationPort, clientId);

            var param = new BooleanParam()
            {
                ObisCode = RegisterIds.BaudrateOptionEnabled.AsLong(),
                Data = isEnabled
            };

            await port.ComThread.WriteParameter(param);
        }
        public async Task<BooleanParam> ReadEnableBaudRateSetting(Guid clientId, CommunicationPort communicationPort)
        {
            var port = CheckConfiguration(communicationPort, clientId);

            var param = new BooleanParam
            {
                Data = null,
                ObisCode = RegisterIds.BaudrateOptionEnabled.AsLong()
            };

            param = await port.ComThread.ReadParameter(param);
            return param;
        }
        public async Task WriteEnableDisplayForOneHourAfterPowerUp(Guid clientId, CommunicationPort communicationPort, bool isEnabled)
        {
            var port = CheckConfiguration(communicationPort, clientId);

            // enable it first
            var param = new BooleanParam()
            {
                ObisCode = RegisterIds.DisplayPowerForOneHourAfterPowerUpEnabled.AsLong(),
                Data = true // let the feature be always enabled
            };

            await port.ComThread.WriteParameter(param);

            // activate setting after it was enabled
            param.ObisCode = RegisterIds.DisplayPowerForOneHourAfterPowerUp.AsLong();
            param.Data = isEnabled; // activate or deactivate this feature
            await port.ComThread.WriteParameter(param);
        }
        public async Task<BooleanParam> ReadEnableDisplayForOneHourAfterPowerUp(Guid clientId, CommunicationPort communicationPort)
        {
            var port = CheckConfiguration(communicationPort, clientId);

            var param = new BooleanParam
            {
                Data = null,
                ObisCode = RegisterIds.DisplayPowerForOneHourAfterPowerUpEnabled.AsLong()
            };

            param = await port.ComThread.ReadParameter(param);
            return param;
        }
        /// <summary>
        /// Writes the is operation mode edl40.
        /// </summary>
        /// <param name="clientId">The client id.</param>
        /// <param name="communicationPort">The communication port.</param>
        /// <param name="isActive">if set to <c>true</c> [is active].</param>
        public async Task WriteIsOperationModeEdl40(Guid clientId, CommunicationPort communicationPort, bool isActive)
        {
            var port = CheckConfiguration(communicationPort, clientId);

            var boolParam = new BooleanParam() { ObisCode = (long)ObisId.EdlOperationModeEdl40, Data = isActive };

            if (port.EdlMeterDevice != null)
            {
                // Set activation state, if set to true, the com thread will update the meter periodically
                port.EdlMeterDevice.IsOperationModeEdl40 = boolParam;
            }

            await port.ComThread.WriteParameter(boolParam);
        }
        /// <summary>
        /// Reads whether the manufacturer specific (extended) billing data set is used.
        /// </summary>
        /// <param name="clientId">The client id.</param>
        /// <param name="communicationPort">The communication port.</param>
        /// <returns>True if manufacturer specific billing data set is used.</returns>
        public async Task<BooleanParam> ReadIsManufacturerSpecificBillingDataUsed(Guid clientId, CommunicationPort communicationPort)
        {
            CheckConfiguration(communicationPort, clientId);

            var boolParam = new BooleanParam()
            {
                ObisCode = (long)ObisId.EdlManufacturerPushData,
                Data = null
            };

            boolParam = await ClientComListDict[communicationPort.ComPortName].ComThread.ReadParameter(boolParam);
            return boolParam;
        }
        public async Task WriteClearEeprom(Guid clientId, CommunicationPort communicationPort)
        {
            var port = CheckConfiguration(communicationPort, clientId);

            var param = new BooleanParam()
            {
                ObisCode = VendorSpecificRegisterIds.ClearApplicationEeprom.AsLong(),
                Data = true
            };

            await port.ComThread.WriteParameter(param);
        }
        /// <summary>
        /// Reads whether the INFO interface is activated.
        /// </summary>
        /// <param name="clientId">The client id.</param>
        /// <param name="communicationPort">The communication port.</param>
        /// <returns>True if INFO interface is activated.</returns>
        public async Task<BooleanParam> ReadIsInfoInterfaceActivated(Guid clientId, CommunicationPort communicationPort)
        {
            var port = CheckConfiguration(communicationPort, clientId);

            var boolParam = new BooleanParam { ObisCode = (long)ObisId.EdlInfoInterfaceActive };
            boolParam = await port.ComThread.ReadParameter(boolParam);
            return boolParam;
        }
        public async Task WriteEnableComTestForApproval(Guid clientId, CommunicationPort communicationPort)
        {
            var port = CheckConfiguration(communicationPort, clientId);

            var param = new BooleanParam()
            {
                ObisCode = 0x0101606013FF,
                Data = true
            };

            await port.ComThread.WriteParameter(param);
        }
        /// <summary>
        /// Reads whether the manipulation detection is active.
        /// </summary>
        /// <param name="clientId">The client id.</param>
        /// <param name="communicationPort">The communication port.</param>
        /// <returns>True if the manipulation detection is active.</returns>
        public async Task<BooleanParam> ReadIsManipulationDetectionActive(Guid clientId, CommunicationPort communicationPort)
        {
            CheckConfiguration(communicationPort, clientId);

            var boolParam = new BooleanParam()
            {
                ObisCode = (long) ObisId.EdlIsManipulationDectectionActive, Data = null
            };

            boolParam = await ClientComListDict[communicationPort.ComPortName].ComThread.ReadParameter(boolParam);
            return boolParam;
        }
        /// <summary>
        /// Writes the clear billing.
        /// </summary>
        /// <param name="clientId">The client id.</param>
        /// <param name="communicationPort">The communication port.</param>
        public async Task WriteClearBilling(Guid clientId, CommunicationPort communicationPort)
        {
            var port = CheckConfiguration(communicationPort, clientId);

            var boolParam = new BooleanParam()
            {
                ObisCode = (long) ObisId.EdlDzgClearBilling, // same for BM
                Data = true
            };

            await port.ComThread.WriteParameter(boolParam);
        }
        /// <summary>
        /// Reads the number of tariffs.
        /// </summary>
        /// <param name="clientId">The client id.</param>
        /// <param name="communicationPort">The communication port.</param>
        /// <returns>Total number of configured tariffs.</returns>
        public async Task<UnsignedParam> ReadNumberOfTariffs(Guid clientId, CommunicationPort communicationPort)
        {
            var port = CheckConfiguration(communicationPort, clientId);
            
            if (port.MeterType == MeterType.Basemeter)
            {
                var boolParam = new BooleanParam()
                {
                    ObisCode = RegisterIds.TariffOptionEnabled.AsLong()
                };

                boolParam = await port.ComThread.ReadParameter(boolParam);
                if (boolParam.Data.Value)
                {
                    return new UnsignedParam()
                    {
                        ObisCode = (long) ObisId.EdlDzgNumberOfTariffs,
                        Data = 2
                    };
                }
                else
                {
                    return new UnsignedParam()
                    {
                        ObisCode = (long)ObisId.EdlDzgNumberOfTariffs,
                        Data = 0
                    };
                }
            }

            var numParam = new UnsignedParam()
            {
                ObisCode = (long)ObisId.EdlDzgNumberOfTariffs,
                Data = null
            };
            numParam = await ClientComListDict[communicationPort.ComPortName].ComThread.ReadParameter(numParam);
            return numParam;
        }
        /// <summary>
        /// Writes the is current power on display allowed.
        /// </summary>
        /// <param name="clientId">The client id.</param>
        /// <param name="communicationPort">The communication port.</param>
        /// <param name="isAllowed">if set to <c>true</c> [is allowed].</param>
        public async Task WriteIsCurrentPowerOnDisplayAllowed(Guid clientId, CommunicationPort communicationPort, bool isAllowed)
        {
            CheckConfiguration(communicationPort, clientId);

            var boolParam = new BooleanParam
            {
                Data = isAllowed
            };

            switch (communicationPort.MeterType)
            {
                case MeterType.Edl:
                    boolParam.ObisCode = (long)ObisId.EdlActivePowerOnLcd;
                    break;
                case MeterType.Sym2:
                    throw new NotImplementedException();
                case MeterType.Basemeter:
                    boolParam.ObisCode = RegisterIds.PowerOnLcdEnabled.AsLong();
                    break;
                default:
                    throw new ArgumentOutOfRangeException();
            }

            await ClientComListDict[communicationPort.ComPortName].ComThread.WriteParameter(boolParam);
        }
        /// <summary>
        /// Writes the parameter.
        /// </summary>
        /// <param name="param">The param.</param>
        public async Task WriteParameter(BooleanParam param, bool enableManufacturerMode = false)
        {
            logger.Info(
                "Writing boolean parameter {0} with value {1}.",
                (ObisId)param.ObisCode,
                param.Data);

            await Retry.DoTask(
                async () =>
                {
                    var requestFile = SmlHandler.CreateSetRequestFile(portName, param);
                    await SendRequestFile(requestFile, enableManufacturerMode).ConfigureAwait(false);
                    if (writeOnlyParams.Contains(param.ObisCode))
                    {
                        return;
                    }

                    var result = await ReadParameter(new BooleanParam { ObisCode = param.ObisCode });

                    if (param.Data != result.Data)
                    {
                        throw new DataException(
                            string.Format(
                                "Failed to write and verify {0:X12}: written: {1}, read: {2}",
                                param.ObisCode,
                                param.Data,
                                result.Data));
                    }
                });
        }
 public async Task<BooleanParam> ReadCustomerPush(Guid clientId, CommunicationPort communicationPort)
 {
     var port = CheckConfiguration(communicationPort, clientId);
     
     var boolParam = new BooleanParam { ObisCode = (long)ObisId.EdlManufacturerPushData };
     boolParam = await port.ComThread.ReadParameter(boolParam);
     return boolParam;
 }
        public async Task WriteParameter(BooleanParam param, bool enableManufacturerMode = false)
        {
            var obisId = new ObisId((ulong)param.ObisCode);

            if (param.Data == null)
            {
                param.Data = false;
            }

            // delete historic energy is a special 'RESET' command
            if (obisId == RegisterIds.HistoricConsumptionDay)
            {
                await this.productionParameters.ClearHistoricConsumptionData().ConfigureAwait(false);
                return;
            }

            if (obisId == RegisterIds.GridOptionEnabled)
            {
                await this.productionParameters.SetGridOption(param.Data.Value).ConfigureAwait(false);
                return;
            }

            if (obisId == RegisterIds.BaudrateOptionEnabled)
            {
                await this.productionParameters.SetBaudrateOptionEnabled(param.Data.Value).ConfigureAwait(false);
                return;
            }

            if (param.ObisCode == ObisEdl.DzgTerminalControlReverse.AsLong())
            {
                var tc = await this.baseMeterDevice.TariffControl.GetTariffConfiguration().ConfigureAwait(false);
                if (param.Data.Value == true)
                {
                    tc |= TariffConfiguration.OnActivationUseT1;
                }
                else
                {
                    tc &= ~TariffConfiguration.OnActivationUseT1;
                }

                await this.baseMeterDevice.TariffControl.SetTariffConfiguration(tc).ConfigureAwait(false);
                return;
            }

            var result = await this.baseMeterDevice.WriteRegisterAsync(obisId, param.Data).ConfigureAwait(false);//(new MeterValue(param.Data, obisId));
            if (result != ResultCode.Ok)
            {
                throw new InvalidOperationException("Write failed with code: " + result);
            }
        }
        public async Task WriteCustomerPush(Guid clientId, CommunicationPort communicationPort, bool isOn)
        {
            var port = CheckConfiguration(communicationPort, clientId);

            var boolParam = new BooleanParam { ObisCode = (long)ObisId.EdlManufacturerPushData, Data = isOn };
            await port.ComThread.WriteParameter(boolParam);
        }
        /// <summary>
        /// Reads whether the operation mode is equal to EDL40.
        /// </summary>
        /// <param name="clientId">The client id.</param>
        /// <param name="communicationPort">The communication port.</param>
        /// <returns>True if operation mode equals to EDL40.</returns>
        public async Task<BooleanParam> ReadIsOperationModeEdl40(Guid clientId, CommunicationPort communicationPort)
        {
            var port = CheckConfiguration(communicationPort, clientId);

            var boolParam = new BooleanParam()
            {
                ObisCode = (long)ObisId.EdlOperationModeEdl40,
                Data = null
            };

            boolParam = await port.ComThread.ReadParameter(boolParam);
            return boolParam;
        }
        public async Task<BooleanParam> ReadEnableTariffFunction(Guid clientId, CommunicationPort communicationPort)
        {
            var port = CheckConfiguration(communicationPort, clientId);

            var param = new BooleanParam
            {
                Data = null,
                ObisCode = VendorSpecificRegisterIds.TariffEnabled.AsLong()
            };
            
            param = await port.ComThread.ReadParameter(param);
            return param;
        }