Пример #1
0
        public bool SetPowerLimit(int gpuIndex, int value)
        {
            if (!TryGpuAdapterIndex(gpuIndex, out int adapterIndex))
            {
                return(false);
            }
            ADLODNPowerLimitSetting info = new ADLODNPowerLimitSetting();

            try {
                var r = AdlNativeMethods.ADL2_OverdriveN_PowerLimit_Get(context, adapterIndex, ref info);
                if (r < AdlStatus.ADL_OK)
                {
                    NTMinerConsole.DevError(() => $"{nameof(AdlNativeMethods.ADL2_OverdriveN_PowerLimit_Get)} {r.ToString()}");
                    return(false);
                }
                NTMinerConsole.DevWarn(() => $"{nameof(AdlNativeMethods.ADL2_OverdriveN_PowerLimit_Get)} result={r.ToString()},iMode={info.iMode.ToString()},iTDPLimit={info.iTDPLimit.ToString()},iMaxOperatingTemperature={info.iMaxOperatingTemperature.ToString()}");
                info.iMode     = AdlConst.ODNControlType_Manual;
                info.iTDPLimit = value - 100;
                r = AdlNativeMethods.ADL2_OverdriveN_PowerLimit_Set(context, adapterIndex, ref info);
                if (r < AdlStatus.ADL_OK)
                {
                    NTMinerConsole.DevError(() => $"{nameof(AdlNativeMethods.ADL2_OverdriveN_PowerLimit_Set)} {r.ToString()}");
                    return(false);
                }
                return(true);
            }
            catch (Exception e) {
                Logger.ErrorDebugLine(e);
                return(false);
            }
        }
Пример #2
0
 public bool GetMemoryClock(int gpuIndex, out int memoryClock, out int iVddc)
 {
     memoryClock = 0;
     iVddc       = 0;
     try {
         if (!TryGpuAdapterIndex(gpuIndex, out int adapterIndex))
         {
             return(false);
         }
         ADLODNPerformanceLevelsX2 info = ADLODNPerformanceLevelsX2.Create();
         var r = AdlNativeMethods.ADL2_OverdriveN_MemoryClocksX2_Get(context, adapterIndex, ref info);
         if (r < AdlStatus.ADL_OK)
         {
             NTMinerConsole.DevError(() => $"{nameof(AdlNativeMethods.ADL2_OverdriveN_MemoryClocksX2_Get)} {r.ToString()}");
             return(false);
         }
         int index = 0;
         for (int i = 0; i < info.aLevels.Length; i++)
         {
             if (info.aLevels[i].iEnabled != 0)
             {
                 index = i;
             }
             NTMinerConsole.DevWarn(() => "GetMemoryClock " + info.aLevels[i].ToString());
         }
         memoryClock = info.aLevels[index].iClock * 10;
         iVddc       = info.aLevels[index].iVddc;
         return(true);
     }
     catch {
         return(false);
     }
 }
Пример #3
0
        private void SendReGetServerAddressMessage(string[] nodeAddresses)
        {
            if (nodeAddresses == null || nodeAddresses.Length == 0)
            {
                return;
            }
            var             thisNodeIp = ServerRoot.HostConfig.ThisServerAddress;
            ShardingHasher  hash       = new ShardingHasher(nodeAddresses);
            List <TSession> needReConnSessions;

            lock (_locker) {
                needReConnSessions = _dicByWsSessionId.Values.Where(a => hash.GetTargetNode(a.ClientId) != thisNodeIp).ToList();
            }
            if (needReConnSessions.Count != 0)
            {
                foreach (var session in needReConnSessions)
                {
                    string password = session.GetSignPassword();
                    if (!string.IsNullOrEmpty(password))
                    {
                        try {
                            session.SendAsync(new WsMessage(Guid.NewGuid(), WsMessage.ReGetServerAddress)
                            {
                                Data = hash.GetTargetNode(session.ClientId)
                            }, password);
                        }
                        catch {
                        }
                    }
                }
                NTMinerConsole.DevWarn($"通知了 {needReConnSessions.Count.ToString()} 个Ws客户端重新连接");
            }
        }
Пример #4
0
 public void AddMessagePath(MessagePath <TMessage> messagePath)
 {
     lock (_locker) {
         if (typeof(ICmd).IsAssignableFrom(typeof(TMessage)))
         {
             bool isExist = _messagePaths.Any(a => messagePath.PathId == a.PathId);
             if (isExist)
             {
                 /// <see cref="ICmd"/>
                 throw new Exception($"一种命令只应被一个处理器处理:{typeof(TMessage).Name}");
             }
         }
         else if (messagePath.Location != Anonymous.Location)
         {
             var paths = _messagePaths.Where(a => a.PathName == messagePath.PathName && a.PathId == messagePath.PathId && a.Priority == messagePath.Priority).ToArray();
             if (paths.Length != 0)
             {
                 foreach (var path in paths)
                 {
                     NTMinerConsole.DevWarn(() => $"重复的路径:{path.PathName} {path.Description}");
                 }
                 NTMinerConsole.DevWarn(() => $"重复的路径:{messagePath.PathName} {messagePath.Description}");
             }
         }
         _messagePaths.Add(messagePath);
         Sort();
     }
 }
Пример #5
0
        public OverClockRange GetClockRange(int gpuIndex)
        {
            OverClockRange result = new OverClockRange(gpuIndex);

            try {
                if (!TryGpuAdapterIndex(gpuIndex, out int adapterIndex))
                {
                    return(result);
                }
                ADLODNCapabilitiesX2 info = new ADLODNCapabilitiesX2();
                var r = AdlNativeMethods.ADL2_OverdriveN_CapabilitiesX2_Get(context, adapterIndex, ref info);
                if (r < AdlStatus.ADL_OK)
                {
                    NTMinerConsole.DevError(() => $"{nameof(AdlNativeMethods.ADL2_OverdriveN_CapabilitiesX2_Get)} {r.ToString()}");
                    return(result);
                }
                result.PowerCurr = GetPowerLimit(gpuIndex);
                result.TempCurr  = GetTempLimit(gpuIndex);
                if (GetMemoryClock(gpuIndex, out int memoryClock, out int iVddc))
                {
                    result.MemoryClockDelta = memoryClock;
                    result.MemoryVoltage    = iVddc;
                }
                if (GetCoreClock(gpuIndex, out int coreClock, out iVddc))
                {
                    result.CoreClockDelta = coreClock;
                    result.CoreVoltage    = iVddc;
                }
                result.CoreClockMin     = info.sEngineClockRange.iMin * 10;
                result.CoreClockMax     = info.sEngineClockRange.iMax * 10;
                result.MemoryClockMin   = info.sMemoryClockRange.iMin * 10;
                result.MemoryClockMax   = info.sMemoryClockRange.iMax * 10;
                result.PowerMin         = info.power.iMin + 100;
                result.PowerMax         = info.power.iMax + 100;
                result.PowerDefault     = info.power.iDefault + 100;
                result.TempLimitMin     = info.powerTuneTemperature.iMin;
                result.TempLimitMax     = info.powerTuneTemperature.iMax;
                result.TempLimitDefault = info.powerTuneTemperature.iDefault;
                result.VoltMin          = info.svddcRange.iMin;
                result.VoltMax          = info.svddcRange.iMax;
                result.VoltDefault      = info.svddcRange.iDefault;
                if (info.fanSpeed.iMax == 0)
                {
                    result.FanSpeedMin = 0;
                }
                else
                {
                    result.FanSpeedMin = info.fanSpeed.iMin * 100 / info.fanSpeed.iMax;
                }
                result.FanSpeedMax = 100;
#if DEBUG
                NTMinerConsole.DevWarn(() => $"GetClockRange {result.ToString()}");
#endif
            }
            catch (Exception e) {
                Logger.ErrorDebugLine(e);
            }
            return(result);
        }
Пример #6
0
        public OverClockRange GetClockRange(IGpu gpu)
        {
            int            busId  = gpu.GetOverClockId();
            OverClockRange result = new OverClockRange(busId);

            try {
                if (GetClockDelta(busId,
                                  out int outCoreCurrFreqDelta, out int outCoreMinFreqDelta,
                                  out int outCoreMaxFreqDelta,
                                  out int outMemoryCurrFreqDelta, out int outMemoryMinFreqDelta,
                                  out int outMemoryMaxFreqDelta))
                {
                    result.CoreClockMin     = outCoreMinFreqDelta;
                    result.CoreClockMax     = outCoreMaxFreqDelta;
                    result.CoreClockDelta   = outCoreCurrFreqDelta;
                    result.MemoryClockMin   = outMemoryMinFreqDelta;
                    result.MemoryClockMax   = outMemoryMaxFreqDelta;
                    result.MemoryClockDelta = outMemoryCurrFreqDelta;
                    int voltage = GetVoltage(busId);
                    result.CoreVoltage   = voltage;
                    result.MemoryVoltage = voltage;
                    result.VoltMin       = 0;
                    result.VoltMax       = 0;
                }

                if (GetPowerLimit(busId, out uint outCurrPower, out uint outMinPower, out uint outDefPower, out uint outMaxPower))
                {
                    result.PowerMin     = (int)outMinPower;
                    result.PowerMax     = (int)outMaxPower;
                    result.PowerDefault = (int)outDefPower;
                    result.PowerCurr    = (int)outCurrPower;
                }

                if (GetTempLimit(busId, out int outCurrTemp, out int outMinTemp, out int outDefTemp, out int outMaxTemp))
                {
                    result.TempLimitMin     = outMinTemp;
                    result.TempLimitMax     = outMaxTemp;
                    result.TempLimitDefault = outDefTemp;
                    result.TempCurr         = outCurrTemp;
                }

                if (GetCooler(busId, out uint minCooler, out uint currCooler, out uint maxCooler))
                {
                    result.FanSpeedCurr = (int)currCooler;
                    result.FanSpeedMin  = (int)minCooler;
                    result.FanSpeedMax  = (int)maxCooler;
                }
#if DEBUG
                NTMinerConsole.DevWarn(() => $"GetClockRange {result.ToString()}");
#endif
            }
            catch (Exception e) {
                Logger.ErrorDebugLine(e);
            }
            return(result);
        }
Пример #7
0
 public void AddMessagePath(MessagePath <TMessage> messagePath)
 {
     lock (_locker) {
         if (typeof(ICmd).IsAssignableFrom(typeof(TMessage)))
         {
             bool isExist = _messagePaths.Any(a => messagePath.PathId == a.PathId);
             if (isExist)
             {
                 /// <see cref="ICmd"/>
                 throw new Exception($"一种命令只应被一个处理器处理:{typeof(TMessage).Name}");
             }
         }
         else if (messagePath.Location != AnonymousMessagePath.Location && _messagePaths.Any(a => a.Path == messagePath.Path && a.PathId == messagePath.PathId))
         {
             NTMinerConsole.DevWarn(() => $"重复的路径:{messagePath.Path} {messagePath.Description}");
         }
         _messagePaths.Add(messagePath);
     }
 }
Пример #8
0
        private void Ws_OnMessage(object sender, MessageEventArgs e)
        {
            WebSocket ws = (WebSocket)sender;

            if (_ws != ws)
            {
                return;
            }
            #region
            if (e.IsPing)
            {
                return;
            }
            WsMessage message = null;
            if (e.IsBinary)
            {
                message = VirtualRoot.BinarySerializer.Deserialize <WsMessage>(e.RawData);
            }
            else  // 此时一定IsText,因为取值只有IsBinary、IsPing、IsText这三种
            {
                if (string.IsNullOrEmpty(e.Data) || e.Data[0] != '{' || e.Data[e.Data.Length - 1] != '}')
                {
                    return;
                }
                message = VirtualRoot.JsonSerializer.Deserialize <WsMessage>(e.Data);
            }
            if (message == null)
            {
                return;
            }
            switch (_appType)
            {
            case NTMinerAppType.MinerClient:
                if (message.Type == WsMessage.UpdateAESPassword)
                {
                    if (message.TryGetData(out AESPassword aesPassword))
                    {
                        aesPassword.Password = Cryptography.RSAUtil.DecryptString(aesPassword.Password, aesPassword.PublicKey);
                        _aesPassword         = aesPassword;
                    }
                    return;
                }
                if (_aesPassword == null)
                {
                    return;
                }
                if (message.Sign != message.CalcSign(_aesPassword.Password))
                {
                    UpdateWsStateAsync(description: "来自群控的消息签名错误", toOut: true);
                    return;
                }
                break;

            case NTMinerAppType.MinerStudio:
                if (message.Type == WsMessage.ReLogin)
                {
                    UpdateWsStateAsync(description: "用户密码已变更,请重新登录", toOut: true);
                    return;
                }
                if (message.Sign != message.CalcSign(RpcRoot.RpcUser.Password))
                {
                    UpdateWsStateAsync(description: "来自群控的消息签名错误", toOut: true);
                    return;
                }
                break;

            default:
                break;
            }
            if (message.Type == WsMessage.ReGetServerAddress)
            {
                ws.CloseAsync(CloseStatusCode.Normal, WsMessage.ReGetServerAddress);
                return;
            }
            if (TryGetHandler(message.Type, out Action <Action <WsMessage>, WsMessage> handler))
            {
                handler.Invoke(SendAsync, message);
            }
            else
            {
                NTMinerConsole.DevWarn(() => $"OnMessage Received InvalidType {e.Data}");
            }
            #endregion
        }
Пример #9
0
        public ClientDataSet(
            IMinerDataRedis minerRedis, IClientActiveOnRedis clientActiveOnRedis,
            ISpeedDataRedis speedDataRedis, IMinerClientMqSender mqSender)
            : base(isPull: false, getDatas: callback => {
            var getMinersTask          = minerRedis.GetAllAsync();
            var getClientActiveOnsTask = clientActiveOnRedis.GetAllAsync();
            var getSpeedsTask          = speedDataRedis.GetAllAsync();
            Task.WhenAll(getMinersTask, getClientActiveOnsTask, getSpeedsTask).ContinueWith(t => {
                NTMinerConsole.UserInfo($"从redis加载了 {getMinersTask.Result.Count} 条MinerData,和 {getSpeedsTask.Result.Count} 条SpeedData");
                Dictionary <Guid, SpeedData> speedDataDic       = getSpeedsTask.Result;
                Dictionary <string, DateTime> clientActiveOnDic = getClientActiveOnsTask.Result;
                List <ClientData> clientDatas = new List <ClientData>();
                DateTime speedOn = DateTime.Now.AddMinutes(-3);
                foreach (var minerData in getMinersTask.Result)
                {
                    var clientData = ClientData.Create(minerData);
                    if (clientActiveOnDic.TryGetValue(minerData.Id, out DateTime activeOn))
                    {
                        clientData.MinerActiveOn = activeOn;
                    }
                    clientDatas.Add(clientData);
                    if (speedDataDic.TryGetValue(minerData.ClientId, out SpeedData speedData) && speedData.SpeedOn > speedOn)
                    {
                        clientData.Update(speedData, out bool _);
                    }
                }
                callback?.Invoke(clientDatas);
            });
        }) {
            _minerRedis          = minerRedis;
            _clientActiveOnRedis = clientActiveOnRedis;
            _speedDataRedis      = speedDataRedis;
            _mqSender            = mqSender;
            VirtualRoot.BuildEventPath <Per100MinuteEvent>("周期将矿机的MinerActiveOn或NetActiveOn时间戳持久到redis", LogEnum.DevConsole, message => {
                var minerCients = _dicByObjectId.Values.ToArray();
                DateTime time   = message.BornOn.AddSeconds(-message.Seconds);
                int count       = 0;
                foreach (var minerClient in minerCients)
                {
                    // 如果活跃则更新到redis,否则就不更新了
                    DateTime activeOn = minerClient.GetActiveOn();
                    if (activeOn > time)
                    {
                        clientActiveOnRedis.SetAsync(minerClient.Id, activeOn);
                        count++;
                    }
                }
                NTMinerConsole.DevWarn($"{count.ToString()} 条活跃矿机的时间戳被持久化");
            }, this.GetType());
            // 上面的持久化时间戳到redis的目的主要是为了下面那个周期找出不活跃的矿机记录删除掉的逻辑能够在重启WebApiServer服务进程后不中断
            VirtualRoot.BuildEventPath <Per2MinuteEvent>("周期找出用不活跃的矿机记录删除掉", LogEnum.DevConsole, message => {
                var clientDatas = _dicByObjectId.Values.ToArray();
                Dictionary <string, List <ClientData> > dicByMACAddress = new Dictionary <string, List <ClientData> >();
                DateTime minerClientExpireTime = message.BornOn.AddDays(-7);
                // 因为SpeedData尺寸较大,时效性较短,可以比CientData更早删除
                DateTime minerSpeedExpireTime = message.BornOn.AddMinutes(-3);
                int count = 0;
                foreach (var clientData in clientDatas)
                {
                    DateTime activeOn = clientData.GetActiveOn();
                    // 如果7天都没有活跃了
                    if (activeOn <= minerClientExpireTime)
                    {
                        RemoveByObjectId(clientData.Id);
                        count++;
                    }
                    else if (activeOn <= minerSpeedExpireTime)
                    {
                        _speedDataRedis.DeleteByClientIdAsync(clientData.ClientId);
                    }
                    else if (!string.IsNullOrEmpty(clientData.MACAddress))
                    {
                        if (!dicByMACAddress.TryGetValue(clientData.MACAddress, out List <ClientData> list))
                        {
                            list = new List <ClientData>();
                            dicByMACAddress.Add(clientData.MACAddress, list);
                        }
                        list.Add(clientData);
                    }
                }
                if (count > 0)
                {
                    NTMinerConsole.DevWarn($"{count.ToString()} 条不活跃的矿机记录被删除");
                }
                List <string> toRemoveIds = new List <string>();
                foreach (var kv in dicByMACAddress)
                {
                    if (kv.Value.Count > 1)
                    {
                        toRemoveIds.AddRange(kv.Value.Select(a => a.Id));
                    }
                }
                if (toRemoveIds.Count > 0)
                {
                    count = 0;
                    foreach (var id in toRemoveIds)
                    {
                        RemoveByObjectId(id);
                        count++;
                    }
                    NTMinerConsole.DevWarn($"{count.ToString()} 条MAC地址重复的矿机记录被删除");
                }

                NTMinerConsole.DevDebug($"QueryClients平均耗时 {AverageQueryClientsMilliseconds.ToString()} 毫秒");
            }, this.GetType());
            // 收到Mq消息之前一定已经初始化完成,因为Mq消费者在ClientSetInitedEvent事件之后才会创建
            VirtualRoot.BuildEventPath <SpeedDatasMqEvent>("收到SpeedDatasMq消息后更新ClientData内存", LogEnum.None, path: message => {
                if (message.AppId == ServerRoot.HostConfig.ThisServerAddress)
                {
                    return;
                }
                if (message.ClientIdIps == null || message.ClientIdIps.Length == 0)
                {
                    return;
                }
                if (IsOldMqMessage(message.Timestamp))
                {
                    NTMinerConsole.UserOk(nameof(SpeedDatasMqEvent) + ":" + MqKeyword.SafeIgnoreMessage);
                    return;
                }
                speedDataRedis.GetByClientIdsAsync(message.ClientIdIps.Select(a => a.ClientId).ToArray()).ContinueWith(t => {
                    if (t.Result != null && t.Result.Length != 0)
                    {
                        foreach (var item in t.Result)
                        {
                            string minerIp = string.Empty;
                            var clientIdIp = message.ClientIdIps.FirstOrDefault(a => a.ClientId == item.ClientId);
                            if (clientIdIp != null)
                            {
                                minerIp = clientIdIp.MinerIp;
                            }
                            ReportSpeed(item.SpeedDto, minerIp, isFromWsServerNode: true);
                        }
                    }
                });
            }, this.GetType());
            VirtualRoot.BuildEventPath <MinerClientWsClosedMqEvent>("收到MinerClientWsClosedMq消息后更新NetActiveOn和IsOnline", LogEnum.None, path: message => {
                if (IsOldMqMessage(message.Timestamp))
                {
                    NTMinerConsole.UserOk(nameof(MinerClientWsClosedMqEvent) + ":" + MqKeyword.SafeIgnoreMessage);
                    return;
                }
                if (_dicByClientId.TryGetValue(message.ClientId, out ClientData clientData))
                {
                    clientData.NetActiveOn = message.Timestamp;
                    clientData.IsOnline    = false;
                }
            }, this.GetType());
            VirtualRoot.BuildEventPath <MinerClientsWsBreathedMqEvent>("收到MinerClientsWsBreathedMq消息后更新NetActiveOn", LogEnum.None, path: message => {
                if (IsOldMqMessage(message.Timestamp))
                {
                    NTMinerConsole.UserOk(nameof(MinerClientsWsBreathedMqEvent) + ":" + MqKeyword.SafeIgnoreMessage);
                    return;
                }
                if (message.ClientIds != null && message.ClientIds.Length != 0)
                {
                    foreach (var clientId in message.ClientIds)
                    {
                        if (_dicByClientId.TryGetValue(clientId, out ClientData clientData))
                        {
                            clientData.NetActiveOn = message.Timestamp;
                            clientData.IsOnline    = true;
                        }
                    }
                }
            }, this.GetType());
            VirtualRoot.BuildEventPath <MinerSignSetedMqEvent>("更新内存中的MinerData的MinerSign部分", LogEnum.None, path: message => {
                if (_dicByObjectId.TryGetValue(message.Data.Id, out ClientData clientData))
                {
                    clientData.Update(message.Data);
                }
                else
                {
                    clientData = ClientData.Create(message.Data);
                    Add(clientData);
                }
                clientData.NetActiveOn        = DateTime.Now;
                clientData.IsOnline           = true;
                clientData.IsOuterUserEnabled = true;
            }, this.GetType());
            VirtualRoot.BuildCmdPath <QueryClientsForWsMqCommand>(path: message => {
                QueryClientsResponse response = AppRoot.QueryClientsForWs(message.Query);
                _mqSender.SendResponseClientsForWs(message.AppId, message.LoginName, message.SessionId, message.MqMessageId, response);
            }, this.GetType(), LogEnum.None);
        }
Пример #10
0
        public void Route <TMessage>(TMessage message) where TMessage : IMessage
        {
            if (message == null)
            {
                throw new ArgumentNullException(nameof(message));
            }
            MessagePath <TMessage>[] messagePaths = PathSetSet.GetMessagePathSet <TMessage>().GetMessagePaths();
            if (messagePaths.Length == 0)
            {
                Type messageType = typeof(TMessage);
                MessageTypeAttribute messageTypeAttr = MessageTypeAttribute.GetMessageTypeAttribute(messageType);
                if (!messageTypeAttr.IsCanNoPath)
                {
                    NTMinerConsole.DevWarn(() => messageType.FullName + "类型的消息没有对应的处理器");
                }
            }
            else
            {
                foreach (var messagePath in messagePaths)
                {
                    try {
                        bool canGo = false;
                        if (message is IEvent evt)
                        {
                            canGo =
                                evt.TargetPathId == PathId.Empty || // 事件不是特定路径的事件则放行
                                messagePath.PathId == PathId.Empty || // 路径不是特定事件的路径则放行
                                evt.TargetPathId == messagePath.PathId;    // 路径是特定事件的路径且路径和事件造型放行
                        }
                        else if (message is ICmd cmd)
                        {
                            // 路径不是特定命令的路径则放行
                            if (messagePath.PathId == PathId.Empty)
                            {
                                canGo = true;
                            }
                            else
                            {
                                canGo = messagePath.PathId == cmd.MessageId;
                            }
                        }
                        if (canGo && messagePath.ViaTimesLimit > 0)
                        {
                            // ViaTimesLimite小于0表示是不限定通过的次数的路径,不限定通过的次数的路径不需要消息每通过一次递减一次ViaTimesLimit计数
                            messagePath.DecreaseViaTimesLimit(onDownToZero: RemovePath);
                        }
                        if (!messagePath.IsEnabled)
                        {
                            continue;
                        }
                        if (canGo)
                        {
                            switch (messagePath.LogType)
                            {
                            case LogEnum.DevConsole:
                                if (DevMode.IsDevMode)
                                {
                                    NTMinerConsole.DevDebug(() => $"({typeof(TMessage).Name})->({messagePath.Location.Name}){messagePath.Description}");
                                }
                                break;

                            case LogEnum.UserConsole:
                                NTMinerConsole.UserInfo($"({typeof(TMessage).Name})->({messagePath.Location.Name}){messagePath.Description}");
                                break;

                            case LogEnum.Log:
                                Logger.InfoDebugLine($"({typeof(TMessage).Name})->({messagePath.Location.Name}){messagePath.Description}");
                                break;

                            case LogEnum.None:
                            default:
                                break;
                            }
                            messagePath.Go(message);
                        }
                    }
                    catch (Exception e) {
                        Logger.ErrorDebugLine(e);
                    }
                }
            }
        }
Пример #11
0
 public void WarnDebugLine(object message)
 {
     NTMinerConsole.DevWarn(message?.ToString());
     _log.Warn(message);
 }