diff --git a/OrpaonEMS.App/App.xaml.cs b/OrpaonEMS.App/App.xaml.cs index 4855e6f..fcfccc7 100644 --- a/OrpaonEMS.App/App.xaml.cs +++ b/OrpaonEMS.App/App.xaml.cs @@ -62,7 +62,12 @@ namespace OrpaonEMS.App /// private EnergyStorageService energyStorageService { get; set; } - protected override Window CreateShell() => null; + protected override Window CreateShell() + { + var container = ContainerLocator.Container; + var shell = container.Resolve("MainWindow"); + return shell as Window; + } protected override void RegisterTypes(IContainerRegistry services) { @@ -130,8 +135,8 @@ namespace OrpaonEMS.App { //从容器中获取MainView的实例对象 var container = ContainerLocator.Container; - var shell = container.Resolve("MainWindow"); - if (shell is Window view) + var view = Application.Current.MainWindow as Window; + if (view != null) { //更新Prism注册区域信息 var regionManager = container.Resolve(); diff --git a/OrpaonEMS.App/Services/EMSService.cs b/OrpaonEMS.App/Services/EMSService.cs index 7ad3056..cf5ae61 100644 --- a/OrpaonEMS.App/Services/EMSService.cs +++ b/OrpaonEMS.App/Services/EMSService.cs @@ -955,7 +955,6 @@ namespace OrpaonEMS.App.Services /// /// 在线客户端个数 - /// /// public ushort OnLineClientNum { get; set; } diff --git a/OrpaonEMS.App/Services/YuePuRunModelService.cs b/OrpaonEMS.App/Services/YuePuRunModelService.cs index a3bdedb..1121809 100644 --- a/OrpaonEMS.App/Services/YuePuRunModelService.cs +++ b/OrpaonEMS.App/Services/YuePuRunModelService.cs @@ -139,8 +139,6 @@ namespace OrpaonEMS.App.Services SolarCurBackFlow.TrigTimeOutHandler += SolarCurBackFlow_TrigTimeOutHandler; } - - } /// @@ -2300,6 +2298,130 @@ namespace OrpaonEMS.App.Services set { _SlaveControlMsg = value; RaisePropertyChanged(); } } + private double _SolarToEsAsFullSoc = 97; + /// + /// 光伏给储能充电 作为满的比值 + /// 90-97 + /// + public double SolarToEsAsFullSoc + { + get { return _SolarToEsAsFullSoc; } + set { _SolarToEsAsFullSoc = value; RaisePropertyChanged(); } + } + + private double _Master_ToSlaveByMasterSoc = 5; + /// + /// Master模式,切换到Slave模式时MasterSOC的阀值 + /// 两个同时考虑 + /// + public double Master_ToSlaveByMasterSoc + { + get { return _Master_ToSlaveByMasterSoc; } + set { _Master_ToSlaveByMasterSoc = value; RaisePropertyChanged(); } + } + + + private double _Master_ToSlaveBySlaveSoc = 15; + /// + /// Master模式,切换到Slave模式时Slave SOC的阀值 + /// 两个同时考虑 + /// + public double Master_ToSlaveBySlaveSoc + { + get { return _Master_ToSlaveBySlaveSoc; } + set { _Master_ToSlaveBySlaveSoc = value; RaisePropertyChanged(); } + } + + private double _Master_SolarToSlaveEsFullByMasterSoc = 95; + /// + /// Master模式,光伏给从储能充满了,是否切换到Slave模式,但是此时需要判断主储能MasterSOC是否满了(主储能也满的话,也无法接收光伏的电),否则不切换 + /// 主要考虑尽可能的不浪费光伏的电,主从储能只要有余量就要接受光伏的电,也要防止频繁的切换 + /// < SolarToEsAsFullSoc + /// + public double Master_SolarToSlaveEsFullByMasterSoc + { + get { return _Master_SolarToSlaveEsFullByMasterSoc; } + set { _Master_SolarToSlaveEsFullByMasterSoc = value; RaisePropertyChanged(); } + } + + + + private double _Slave_ToMasterBySlaveSoc = 5; + /// + /// Slave模式,切换到Master模式时SOC的阀值 + /// 两个同时考虑 + /// + public double Slave_ToMasterBySlaveSoc + { + get { return _Slave_ToMasterBySlaveSoc; } + set { _Slave_ToMasterBySlaveSoc = value; RaisePropertyChanged(); } + } + + + private double _Slave_ToMasterByMasterSoc = 15; + /// + /// Slave模式,切换到Master模式时SOC的阀值 + /// 两个同时考虑 + /// + public double Slave_ToMasterByMasterSoc + { + get { return _Slave_ToMasterByMasterSoc; } + set { _Slave_ToMasterByMasterSoc = value; RaisePropertyChanged(); } + } + + + private double _Slave_SolarToMasterEsFullBySlaverSoc = 95; + /// + /// Slave模式,光伏给主储能充满了,是否切换到Master模式,但是此时需要判断从储能SOC是否满了(从储能也满的话,也无法接收光伏的电),否则不切换 + /// 主要考虑尽可能的不浪费光伏的电,主从储能只要有余量就要接受光伏的电,也要防止频繁的切换 + /// < SolarToEsAsFullSoc + /// + public double Slave_SolarToMasterEsFullBySlaverSoc + { + get { return _Slave_SolarToMasterEsFullBySlaverSoc; } + set { _Slave_SolarToMasterEsFullBySlaverSoc = value; RaisePropertyChanged(); } + } + + private double _NightMaster_ToMasterFullSoc = 98; + /// + /// 晚上,主储能充满的标志,也是切换到从储能的控制标志 + /// + public double NightMaster_ToMasterFullSoc + { + get { return _NightMaster_ToMasterFullSoc; } + set { _NightMaster_ToMasterFullSoc = value; RaisePropertyChanged(); } + } + + + private double _NightSlave_ToSlaveFullSoc = 98; + /// + /// 晚上,从储能充满的标志,也是切换到主储能的控制标志 + /// + public double NightSlave_ToSlaveFullSoc + { + get { return _NightSlave_ToSlaveFullSoc; } + set { _NightSlave_ToSlaveFullSoc = value; RaisePropertyChanged(); } + } + + + + private string _LogicMsg; + /// + /// 逻辑消息 + /// + public string LogicMsg + { + get { return _LogicMsg; } + set + { + if (value != _LogicMsg) + { + RaisePropertyChanged(); + _LogicMsg = value; + } + } + } + /// /// 逻辑扫描1 @@ -2708,7 +2830,8 @@ namespace OrpaonEMS.App.Services //依据当前的状态判断是否需要切换到其他状态 switch (CurESChargInfo) { - case ESChargInfo.Master: + case ESChargInfo.Master://当前是白天Master状态 + ///////////////////////////获取当前的是白天还是夜晚,依据时间判断,留有余量,有可能实际已经晚上了,但是负载全部没有了 if (IsBetweenTime(DateTime.Now.ToString(), SunriseTimeStr, SunsetTimeStr)) { @@ -2722,27 +2845,52 @@ namespace OrpaonEMS.App.Services LogService.Info($"时间:{DateTime.Now.ToString()}-【动作】-当前是【Master】-Master->Night_Master 白天Master 到晚上Night_Master的切换"); DayNight = DayNightEnum.Night; + PreESChargInfo = ESChargInfo.Night_Master; SysRunStateMachine.Fire(ESChargInfoTrig.WaitTrig); + //var SolarCur = SolarEleMeter3.RtPw + SolarEleMeter2.RtPw; //根据日落和日出时间判断当前是晚上 continue; } - //判断光伏把储能是否充满,充满的话进入无光伏阶段 新策略 - if (SlaveClient.ClientInfo!.SOC >= 97) + + //?????? 如果昨晚充满了,今天的话,早上就要进入到无光伏的模式了,因为两个都满了 ,设置的数据不要太冲突 ???????? + //从储能箱晚上充电的SOC NightSlave_ToSlaveFullSoc 90 > SolarToEsAsFullSoc 97 + // + //能到这里说明是白天,也要判断光伏把储能是否充满,充满的话进入无光伏阶段 新策略 是否可以切换 + if (SlaveClient.ClientInfo!.SOC >= SolarToEsAsFullSoc) { - LogService.Info($"时间:{DateTime.Now.ToString()}-【动作】-当前是【Master】-光伏把从储能充满SOC:{SlaveClient.ClientInfo!.SOC},充满的话进入无光伏阶段 新策略"); - PreESChargInfo = ESChargInfo.NoSolar; - SysRunStateMachine.Fire(ESChargInfoTrig.WaitTrig); - continue; + //光伏把从储能充满,此时需要判断主储能是否满,满的话就是都满了进入无光伏阶段 + if (MasterClient.ClientInfo!.SOC >= Master_SolarToSlaveEsFullByMasterSoc) + { + //两个储能都满了,进行切换 + LogService.Info($"时间:{DateTime.Now.ToString()}-【动作】-当前是【Master】-光伏把【从储能】充满SOC:{SlaveClient.ClientInfo!.SOC},主储能也满了,充满的话进入无光伏阶段 新策略"); + + PreESChargInfo = ESChargInfo.NoSolar; + SysRunStateMachine.Fire(ESChargInfoTrig.WaitTrig); + + continue; + } + else + { + LogService.Info($"时间:{DateTime.Now.ToString()}-【动作】-当前是【Master】-从储能箱体被光伏充满SOC:{SlaveClient.ClientInfo!.SOC},主储能箱体还有余量:{MasterClient.ClientInfo!.SOC},切换到Slave,让主储能箱接受光伏电量"); + + //主储能箱体放完,从储能箱体还有电,则可以切换 + //先进入等待状态,为切换准备条件 + PreESChargInfo = ESChargInfo.Slave; + SysRunStateMachine.Fire(ESChargInfoTrig.WaitTrig); + + continue; + } + } //主储能箱体放完为止策略 新策略 - if (MasterClient.ClientInfo!.SOC <= 5) + if (MasterClient.ClientInfo!.SOC <= Master_ToSlaveByMasterSoc) { //此时切换到从储能箱体需要查看从储能箱体的SOC - if (SlaveClient.ClientInfo!.SOC >= 15) + if (SlaveClient.ClientInfo!.SOC >= Master_ToSlaveBySlaveSoc) { LogService.Info($"时间:{DateTime.Now.ToString()}-【动作】-当前是【Master】-主储能箱体放完SOC:{MasterClient.ClientInfo!.SOC},从储能箱体还有电SOC:{SlaveClient.ClientInfo!.SOC},切换到Slave"); @@ -2750,15 +2898,18 @@ namespace OrpaonEMS.App.Services //先进入等待状态,为切换准备条件 PreESChargInfo = ESChargInfo.Slave; SysRunStateMachine.Fire(ESChargInfoTrig.WaitTrig); + continue; } else { + LogicMsg = "主储能箱电放完,从储能箱还有电,暂不切换,维持当前状态"; //主储能箱体放完,从储能箱体也没有电,维持当前状态 } } else { //主储能箱维持当前的状态, + LogicMsg = "主储能箱电没有放完,维持当前状态"; } @@ -2810,30 +2961,49 @@ namespace OrpaonEMS.App.Services LogService.Info($"时间:{DateTime.Now.ToString()}-【动作】-当前是【Slave】-Slave->Night_Master 白天Slave 到晚上Night_Master的切换"); DayNight = DayNightEnum.Night; + PreESChargInfo = ESChargInfo.Night_Master; SysRunStateMachine.Fire(ESChargInfoTrig.WaitTrig); + //var SolarCur = SolarEleMeter3.RtPw + SolarEleMeter2.RtPw; //根据日落和日出时间判断当前是晚上 continue; } - //判断光伏把储能是否充满,充满的话进入无光伏阶段 新策略 - if (MasterClient.ClientInfo!.SOC >= 97) + //判断光伏把主储能是否充满,充满的话进入无光伏阶段 新策略 + if (MasterClient.ClientInfo!.SOC >= SolarToEsAsFullSoc) { + //判断从储能箱放电程度 + if (SlaveClient.ClientInfo!.SOC >= Slave_SolarToMasterEsFullBySlaverSoc) + { + //主从储能箱都充满了,无论出于什么原因,都不应该让光伏发电了,进入无光伏阶段 - LogService.Info($"时间:{DateTime.Now.ToString()}-【动作】-当前是【Slave】-光伏把主储能充满SOC:{MasterClient.ClientInfo!.SOC},充满的话进入无光伏阶段 新策略"); + LogService.Info($"时间:{DateTime.Now.ToString()}-【动作】-当前是【Slave】-光伏把主储能充满SOC:{MasterClient.ClientInfo!.SOC},充满的话进入无光伏阶段 新策略"); - PreESChargInfo = ESChargInfo.NoSolar; - SysRunStateMachine.Fire(ESChargInfoTrig.WaitTrig); - continue; + PreESChargInfo = ESChargInfo.NoSolar; + SysRunStateMachine.Fire(ESChargInfoTrig.WaitTrig); + + continue; + } + else + { + LogService.Info($"时间:{DateTime.Now.ToString()}-【动作】-当前是【Slave】-主储能箱体被光伏充满SOC:{MasterClient.ClientInfo!.SOC},从储能箱体还有余量:{SlaveClient.ClientInfo!.SOC},切换到Master,让从储能箱接受光伏电量"); + + //主储能箱体放完,从储能箱体还有电,则可以切换 + //先进入等待状态,为切换准备条件 + PreESChargInfo = ESChargInfo.Master; + SysRunStateMachine.Fire(ESChargInfoTrig.WaitTrig); + + continue; + } } - //主储能箱体放完为止策略 新策略 - if (SlaveClient.ClientInfo!.SOC <= 5) + //从储能箱体放完为止策略 新策略 + if (SlaveClient.ClientInfo!.SOC <= Slave_ToMasterBySlaveSoc) { //此时切换到从储能箱体需要查看从储能箱体的SOC - if (MasterClient.ClientInfo!.SOC >= 15) + if (MasterClient.ClientInfo!.SOC >= Slave_ToMasterByMasterSoc) { LogService.Info($"时间:{DateTime.Now.ToString()}-【动作】-当前是【Slave】-从储能箱体放完SOC:{SlaveClient.ClientInfo!.SOC},主储能箱体还有电SOC:{MasterClient.ClientInfo!.SOC},切换到Master"); @@ -2841,15 +3011,18 @@ namespace OrpaonEMS.App.Services //先进入等待状态,为切换准备条件 PreESChargInfo = ESChargInfo.Master; SysRunStateMachine.Fire(ESChargInfoTrig.WaitTrig); + continue; } else { //主储能箱体放完,从储能箱体也没有电,维持当前状态 + LogicMsg = "从储能箱电放完,主储能箱还有电,暂不切换,维持当前状态"; } } else { - //主储能箱维持当前的状态, + //主储能箱维持当前的状态 + LogicMsg = "从储能箱电没有放完,维持当前状态"; } @@ -2904,12 +3077,12 @@ namespace OrpaonEMS.App.Services //晚上切换到白天的话,直接到Master模式 PreESChargInfo = ESChargInfo.Master; SysRunStateMachine.Fire(ESChargInfoTrig.WaitTrig); - + continue; } else { //可能转到Night_Slave模式,本身充满,Night没有充满 - if (MasterClient.ClientInfo!.SOC >= ConfigDataService.energyStorageRunConfig.BMSSocUpSignLimitValue && SlaveClient.ClientInfo!.SOC <= 55) + if (MasterClient.ClientInfo!.SOC >= NightMaster_ToMasterFullSoc && SlaveClient.ClientInfo!.SOC <= (NightSlave_ToSlaveFullSoc - 10)) { LogService.Info($"时间:{DateTime.Now.ToString()}-【动作】-当前是【Night_Master】-主储能箱体充满SOC:{MasterClient.ClientInfo!.SOC},从储能箱体SOC:{SlaveClient.ClientInfo!.SOC},切换到Night_Slave"); @@ -2917,12 +3090,15 @@ namespace OrpaonEMS.App.Services //切换到Night_Slave模式 PreESChargInfo = ESChargInfo.Night_Slave; SysRunStateMachine.Fire(ESChargInfoTrig.WaitTrig); + continue; } else { //继续维持当前的状态 MasterControlMsg = "【主储能】夜晚充电"; SlaveControlMsg = "【从储能】夜晚待机"; + + LogicMsg = "【主储能】夜晚充电,【从储能】夜晚待机"; } } @@ -2950,7 +3126,7 @@ namespace OrpaonEMS.App.Services else { //可能转到Night_Master模式,本身充到SOC=60算充满 - if (SlaveClient.ClientInfo!.SOC >= 90 && MasterClient.ClientInfo!.SOC <= ConfigDataService.energyStorageRunConfig.BMSSocUpSignLimitValue * 0.9) + if (SlaveClient.ClientInfo!.SOC >= NightSlave_ToSlaveFullSoc && MasterClient.ClientInfo!.SOC <= (NightMaster_ToMasterFullSoc - 10)) { LogService.Info($"时间:{DateTime.Now.ToString()}-【动作】-当前是【Night_Slave】-从储能箱体充满SOC:{SlaveClient.ClientInfo!.SOC},主储能箱体SOC:{MasterClient.ClientInfo!.SOC},切换到Night_Master"); //切换到Night_Slave模式 @@ -2962,10 +3138,13 @@ namespace OrpaonEMS.App.Services //继续维持当前的状态 MasterControlMsg = "【主储能】夜晚待机"; SlaveControlMsg = "【从储能】夜晚充电"; + + LogicMsg = "【主储能】夜晚待机,【从储能】夜晚充电"; } } break; case ESChargInfo.Wait: + LogicMsg = "模式切换中"; //切换中 break; case ESChargInfo.NoSolar: @@ -2983,8 +3162,10 @@ namespace OrpaonEMS.App.Services LogService.Info($"时间:{DateTime.Now.ToString()}-【动作】-当前是【NoSolar】-NoSolar->Night_Master, NoSolar切换晚上Night_Master"); DayNight = DayNightEnum.Night; + PreESChargInfo = ESChargInfo.Night_Master; SysRunStateMachine.Fire(ESChargInfoTrig.WaitTrig); + //var SolarCur = SolarEleMeter3.RtPw + SolarEleMeter2.RtPw; //根据日落和日出时间判断当前是晚上 continue; @@ -2992,7 +3173,7 @@ namespace OrpaonEMS.App.Services //用Soc小的吸收光伏能量 - if (SlaveClient.ClientInfo!.SOC <= 50) + if (SlaveClient.ClientInfo!.SOC <= (SolarToEsAsFullSoc-10)) { LogService.Info($"时间:{DateTime.Now.ToString()}-【动作】-当前是【NoSolar】-从储能箱体SOC:{SlaveClient.ClientInfo!.SOC},主储能箱体SOC:{MasterClient.ClientInfo!.SOC},切换到Master"); @@ -3002,7 +3183,7 @@ namespace OrpaonEMS.App.Services continue; } - if (MasterClient.ClientInfo!.SOC <= 50) + if (MasterClient.ClientInfo!.SOC <= (SolarToEsAsFullSoc - 10)) { LogService.Info($"时间:{DateTime.Now.ToString()}-【动作】-当前是【NoSolar】-从储能箱体SOC:{SlaveClient.ClientInfo!.SOC},主储能箱体SOC:{MasterClient.ClientInfo!.SOC},切换到Slave"); @@ -3434,12 +3615,29 @@ namespace OrpaonEMS.App.Services /// private string SunsetTimeStr { get; set; } = "23:00"; + + //public DayNightEnum DayNight { get; set; } + + private DayNightEnum _DayNight; /// /// 白天和晚上 /// Day /// Night /// - public DayNightEnum DayNight { get; set; } + public DayNightEnum DayNight + { + get { return _DayNight; } + set + { + if (_DayNight != value) + { + LogService.Info($"时间:{DateTime.Now.ToString()}-【动作】-【DayNight】-【{_DayNight.ToString()}】->【{value.ToString()}】 白天和晚上的切换"); + _DayNight = value; + } + + } + } + /// /// 开关1 diff --git a/OrpaonEMS.App/Views/ControlConfigView.xaml b/OrpaonEMS.App/Views/ControlConfigView.xaml new file mode 100644 index 0000000..3cdd344 --- /dev/null +++ b/OrpaonEMS.App/Views/ControlConfigView.xaml @@ -0,0 +1,173 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +