using CsvHelper.Configuration; using HslCommunication; using HslCommunication.ModBus; using OrpaonEMS.App.Com; using OrpaonEMS.App.Models; using OrpaonEMS.App.PrismEvent; using OrpaonEMS.Core.Enums; using OrpaonEMS.Model.Enums; using OrpaonEMS.Model.MasterSlave; using Prism.Events; using Prism.Mvvm; using Stateless; using System; using System.Collections.Generic; using System.Globalization; using System.Threading; using System.Threading.Tasks; using System.Windows; namespace OrpaonEMS.App.Services { /// /// 月浦储能柜运行模型 /// public class YuePuRunModelService : BindableBase { /////// /////怎么认定为晚上,光伏发电功率低于一个阈值时可认为是晚上 public YuePuRunModelService(ILogService logService, ConfigDataService configDataService,BmsDataService bmsDataService,FFService fFService, InPowerPCSDataService inPowerPCSDataService, IEventAggregator eventAggregator) { ConfigDataService = configDataService; BmsDataService = bmsDataService; FFService = fFService; InPowerPCSDataService = inPowerPCSDataService; LogService = logService; //CurNightChargEleModel = new NightChargEleModel(ConfigDataService); if (ConfigDataService.IsMaster) { _eventAggregator = eventAggregator; _eventAggregator.GetEvent().Subscribe(OnPeakValleyTimeEventMsgReceived); MeterMdDrive?.Close(); MeterMdDrive = new ModbusRtu(); MeterMdDrive.AddressStartWithZero = true;//华为光伏逆变器从0开始的 MeterMdDrive.IsStringReverse = false; MeterMdDrive.DataFormat = HslCommunication.Core.DataFormat.ABCD; MeterMdDrive.ReceiveTimeOut = 500; try { MeterMdDrive.SerialPortInni(sp => { sp.PortName = "COM2"; //sp.PortName = "COM10"; sp.BaudRate = 9600; sp.DataBits = 8; sp.StopBits = System.IO.Ports.StopBits.One; sp.Parity = System.IO.Ports.Parity.None; }); var result = MeterMdDrive.Open(); if (!result.IsSuccess) { MessageBox.Show("Meter仪表连接失败"); } MeterScanDeviceStart(); } catch (Exception ex) { MessageBox.Show(ex.Message); } //SolarMdDrive SolarMdDrive?.Close(); SolarMdDrive = new ModbusRtu(); SolarMdDrive.AddressStartWithZero = false;//跟ModbusPull一样的地址 SolarMdDrive.IsStringReverse = false; SolarMdDrive.DataFormat = HslCommunication.Core.DataFormat.ABCD; SolarMdDrive.ReceiveTimeOut = 500; try { SolarMdDrive.SerialPortInni(sp => { sp.PortName = "COM8"; //sp.PortName = "COM10"; sp.BaudRate = 9600; sp.DataBits = 8; sp.StopBits = System.IO.Ports.StopBits.One; sp.Parity = System.IO.Ports.Parity.None; }); var result = SolarMdDrive.Open(); if (!result.IsSuccess) { MessageBox.Show("光伏连接失败"); } SolarLinkTrigTimeModel = new TrigTimeModel(1, 10);//trigThValue 参数无所谓,因为不用这个参数 ,bool类型 SolarLinkTrigTimeModel.TrigTimeOutHandler += SolarLinkTrigTimeModel_TrigTimeOutHandler; SolarScanDeviceStart(); } catch (Exception ex) { MessageBox.Show(ex.Message); } ModbusTcpNetDrive = new ModbusTcpNet("192.0.3.200", 502); //ModbusTcpNetDrive = new ModbusTcpNet("127.0.0.1", 502); //为了方便和ModbusPull及手册的地址能对应的话,设置从1开始 ModbusTcpNetDrive.AddressStartWithZero = false; ModbusTcpNetDrive.SetPersistentConnection(); var DATA = ModbusTcpNetDrive.ConnectServer(); if (!DATA.IsSuccess) { MessageBox.Show("远程IO 连接失败"); } QFSwitch1 = new SwitchModel("17", "18", ModbusTcpNetDrive); QFSwitch2 = new SwitchModel("19", "20", ModbusTcpNetDrive); QFSwitch3 = new SwitchModel("21", "22", ModbusTcpNetDrive); QFSwitch4 = new SwitchModel("23", "24", ModbusTcpNetDrive); ReIOScanDeviceStart(); //初始的设置,防止上一次的残留导致状态和开关的IO的不同步 //CurESChargInfo = ESChargInfo.Master; //Thread.Sleep(1000); //QFSwitch3.SetRtSwitch(SwitchEm.Off); //QFSwitch4.SetRtSwitch(SwitchEm.On); //QFSwitch2.SetRtSwitch(SwitchEm.Off); //QFSwitch1.SetRtSwitch(SwitchEm.On); // 放电时间模型初始化:基于配置的 DisChargeModel 列表与 SelectedDisChargeModel try { var list = ConfigDataService.GetDisChargeModel(); int selectedId = 1; try { selectedId = ConfigDataService.ControlConfigValue?.SelectedDisChargeModel ?? 1; } catch { selectedId = 1; } DisChargeModelItem? selected = null; if (list != null && list.Count > 0) { foreach (var it in list) { if (it != null && it.Model == selectedId) { selected = it; break; } } if (selected == null) { selected = list[0]; } } // 映射到旧的枚举(如可用),否则回退到 DischargeA var enumValue = DisChargeType.DischargeA; if (selected != null && System.Enum.IsDefined(typeof(DisChargeType), selected.Model)) { enumValue = (DisChargeType)selected.Model; } CurDischargeTimeModel = new DischargeTimeModel(enumValue); // 设置具体时间(优先使用配置时间),格式期望 HH:mm if (selected != null && !string.IsNullOrWhiteSpace(selected.DisChargeTime) && TimeSpan.TryParse(selected.DisChargeTime, out var ts)) { CurDischargeTimeModel.CurDischargeTime = new DateTime(DateTime.Now.Year, DateTime.Now.Month, DateTime.Now.Day, ts.Hours, ts.Minutes, 0); } else { // 解析失败使用默认 08:00 CurDischargeTimeModel.CurDischargeTime = new DateTime(DateTime.Now.Year, DateTime.Now.Month, DateTime.Now.Day, 8, 0, 0); LogService.Warn("DisChargeModel 配置时间缺失或格式非法,已使用默认 08:00"); } } catch (System.Exception ex) { // 失败时初始化为默认模式与时间 CurDischargeTimeModel = new DischargeTimeModel(DisChargeType.DischargeA) { CurDischargeTime = new DateTime(DateTime.Now.Year, DateTime.Now.Month, DateTime.Now.Day, 8, 0, 0) }; LogService.Error($"初始化放电时间模型失败: {ex.Message}"); } StateMachineInitial(); LogicScan(); SolarCurBackFlow = new TrigTimeModel(-5, 10); SolarCurBackFlow.TrigTimeOutHandler += SolarCurBackFlow_TrigTimeOutHandler; } } /// /// 削峰填谷的时间信息 /// /// /// private void OnPeakValleyTimeEventMsgReceived(ElePVEnum data) { PeakValleySglModel = data; } private ElePVEnum _PeakValleySglModel = ElePVEnum.Peak; /// /// 削峰填谷的信号模型 /// 由其他的线程轮训赋值监控 /// public ElePVEnum PeakValleySglModel { get { return _PeakValleySglModel; } set { //if (_PeakValleySglModel != value)//监听线程不停的赋值,变化时触发执行 //{ _PeakValleySglModel = value;//削峰填谷不使用状态机 //} } } /// /// 当前的放电时间模型 /// public DischargeTimeModel CurDischargeTimeModel { get; set; } /// /// 税务大楼光伏逆流触发模型 /// public TrigTimeModel SolarCurBackFlow { get; set; } /// /// 事件聚合器 /// private readonly IEventAggregator _eventAggregator; /// /// 光伏通信失败触发 /// /// /// /// private void SolarLinkTrigTimeModel_TrigTimeOutHandler(object? sender, EventArgs e) { Console.WriteLine($"时间:{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}-【月浦】- 光伏通信失败 "); } /// /// 税务大楼光伏通信失败触发模型 /// public TrigTimeModel SolarLinkTrigTimeModel { get; set; } /// /// 税务大楼的光伏逆流触发事件 /// /// /// /// private void SolarCurBackFlow_TrigTimeOutHandler(object? sender, EventArgs e) { //关闭光伏 CloseSolar(); Console.WriteLine($"时间:{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}-【月浦】- 软件检测到光伏逆流,关闭光伏逆变器 "); LogService.Info($"时间:{DateTime.Now.ToString()}-【动作】-软件检测到光伏逆流,关闭光伏逆变器"); } #region 系统运行状态机 /// /// 系统运行的状态机 /// public StateMachine SysRunStateMachine { get; set; } ///// ///// 夜间充电模型信息 ///// 标记白天放电信息 ///// //public NightChargEleModel CurNightChargEleModel; /*= new NightChargEleModel();*/ /// /// 系统运行的状态机 /// private void StateMachineInitial() { //初始状态是这个状态,不然同一个状态会重复进入导致状态机报错 CurESChargInfo = ESChargInfo.Inital; SysRunStateMachine = new StateMachine(ESChargInfo.Inital); //初始状态 SysRunStateMachine.Configure(ESChargInfo.Inital) .Permit(ESChargInfoTrig.SlaveTrig, ESChargInfo.Slave) .Permit(ESChargInfoTrig.Night_MasterTrig, ESChargInfo.Night_Master) .Permit(ESChargInfoTrig.Night_SlaveTrig, ESChargInfo.Night_Slave) .Permit(ESChargInfoTrig.MasterTrig, ESChargInfo.Master) .Permit(ESChargInfoTrig.NoSolarTrig, ESChargInfo.NoSolar) .Permit(ESChargInfoTrig.WaitTrig, ESChargInfo.Wait) .Ignore(ESChargInfoTrig.InitalTrig); //Master 状态 SysRunStateMachine.Configure(ESChargInfo.Master) .Permit(ESChargInfoTrig.SlaveTrig, ESChargInfo.Slave) .Permit(ESChargInfoTrig.Night_MasterTrig, ESChargInfo.Night_Master) .Permit(ESChargInfoTrig.Night_SlaveTrig, ESChargInfo.Night_Slave) .Permit(ESChargInfoTrig.WaitTrig, ESChargInfo.Wait) .Permit(ESChargInfoTrig.NoSolarTrig, ESChargInfo.NoSolar) .Ignore(ESChargInfoTrig.MasterTrig) .OnEntry(() => EntryMaster()) .OnExit(() => ExitMaster()); //Slave 状态 SysRunStateMachine.Configure(ESChargInfo.Slave) .Permit(ESChargInfoTrig.MasterTrig, ESChargInfo.Master) .Permit(ESChargInfoTrig.Night_MasterTrig, ESChargInfo.Night_Master) .Permit(ESChargInfoTrig.Night_SlaveTrig, ESChargInfo.Night_Slave) .Permit(ESChargInfoTrig.WaitTrig, ESChargInfo.Wait) .Permit(ESChargInfoTrig.NoSolarTrig, ESChargInfo.NoSolar) .Ignore(ESChargInfoTrig.SlaveTrig) .OnEntry(() => EntrySlave()) .OnExit(() => ExitSlave()); //Night_Master 状态 SysRunStateMachine.Configure(ESChargInfo.Night_Master) .Permit(ESChargInfoTrig.MasterTrig, ESChargInfo.Master) .Permit(ESChargInfoTrig.Night_SlaveTrig, ESChargInfo.Night_Slave) .Permit(ESChargInfoTrig.SlaveTrig, ESChargInfo.Slave) .Permit(ESChargInfoTrig.WaitTrig, ESChargInfo.Wait) .Permit(ESChargInfoTrig.NoSolarTrig, ESChargInfo.NoSolar) .Ignore(ESChargInfoTrig.Night_MasterTrig) .OnEntry(() => EntryNight_Master()) .OnExit(() => ExitNight_Master()); //Night_Slave 状态 SysRunStateMachine.Configure(ESChargInfo.Night_Slave) .Permit(ESChargInfoTrig.MasterTrig, ESChargInfo.Master) .Permit(ESChargInfoTrig.SlaveTrig, ESChargInfo.Slave) .Permit(ESChargInfoTrig.WaitTrig, ESChargInfo.Wait) .Permit(ESChargInfoTrig.NoSolarTrig, ESChargInfo.NoSolar) .Permit(ESChargInfoTrig.Night_MasterTrig, ESChargInfo.Night_Master) .Ignore(ESChargInfoTrig.Night_SlaveTrig) .OnEntry(() => EntryNight_Slave()) .OnExit(() => ExitNight_Slave()); //Wait 状态 SysRunStateMachine.Configure(ESChargInfo.Wait) .Permit(ESChargInfoTrig.MasterTrig, ESChargInfo.Master) .Permit(ESChargInfoTrig.SlaveTrig, ESChargInfo.Slave) .Permit(ESChargInfoTrig.Night_MasterTrig, ESChargInfo.Night_Master) .Permit(ESChargInfoTrig.Night_SlaveTrig, ESChargInfo.Night_Slave) .Permit(ESChargInfoTrig.NoSolarTrig, ESChargInfo.NoSolar) .Ignore(ESChargInfoTrig.WaitTrig) .OnEntry(() => EntryWait()) .OnExit(() => ExitWait()); //NoSolar 状态 SysRunStateMachine.Configure(ESChargInfo.NoSolar) .Permit(ESChargInfoTrig.MasterTrig, ESChargInfo.Master) .Permit(ESChargInfoTrig.SlaveTrig, ESChargInfo.Slave) .Permit(ESChargInfoTrig.Night_MasterTrig, ESChargInfo.Night_Master) .Permit(ESChargInfoTrig.Night_SlaveTrig, ESChargInfo.Night_Slave) .Permit(ESChargInfoTrig.WaitTrig, ESChargInfo.Wait) .Ignore(ESChargInfoTrig.NoSolarTrig) .OnEntry(() => EntryNoSolar()) .OnExit(() => ExitNoSolar()); PreESChargInfo = ESChargInfo.Master; SysRunStateMachine.Fire(ESChargInfoTrig.WaitTrig); } /// /// 进入 NoSolar /// /// private void EntryNoSolar() { CurESChargInfo = ESChargInfo.NoSolar; Console.WriteLine($"时间:{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}-【月浦】- 运行进入【NoSolar】模式 "); NoSolarTokenSource = new CancellationTokenSource(); LogService.Info($"时间:{DateTime.Now.ToString()}-【动作】-进入【EntryNoSolar】模式"); Task.Run(() => { //判断状态是否正确 while (CurESChargInfo == ESChargInfo.NoSolar) { try { Thread.Sleep(1000); if (NoSolarTokenSource.IsCancellationRequested) { break; } if (MasterClient == null)//防止未加载导致错误 { continue; } if (!YuPuAutoHand) { continue; } //关闭光伏 if (CheckSolarState() == true) { CloseSolar(); } TaxRealPw = SolarEleMeter5.AvePw - SolarEleMeter3.AvePw;//光伏和市电供应给税务大楼的负载 //循环执行方法 //让储能给管理大楼放电,腾出空间,让光伏参与进来 //优先让主储能给管理大楼供电,因为涉及到电操,让主储能柜体放电 1 4 ON 2 3OFF if (MasterClient.IsCanDisCharg()) { ///////////////////Master主控制柜给管理大楼供电 //注意方向 //主储能箱体放电的控制 ManageRealPw = EsEleMeter4.AvePw - EsEleMeter1.AvePw; if (ManageRealPw > ManageTargetPw)//真实的负载大于一个值的时候才介入,或者控制关口电表的数据维持在一个水平,两个不同的控制方式 { //Master 主储能箱根据负载情况给管理大楼放电 //可以放电的话,则 if (MasterClient.IsCanDisCharg()) { MasterClient.ServerCmd.CmdType = "DisCharg"; ManageDifLoad = ManageRealPw - ManageTargetPw; if (ManageDifLoad < MasterClient.ClientInfo.MaxDisChargPw) { //储能发送功率 补全总功率数据 //DisChargPwCmd(AcrelMeters.Psum - EleMeterActionLimitValue.MidValue); MasterClient.ServerCmd.CmdPw = ManageDifLoad; MasterControlMsg = "【主储能】放电【管理大楼】:【主储能】放电补全目标差值,【主储能】正常放电"; } else //储能不能满足总的差值 { //满功率放电 //储能全功率放电,不足的部分电网自动补全 MasterClient.ServerCmd.CmdPw = (MasterClient.ClientInfo.MaxDisChargPw * ConfigDataService.energyStorageRunConfig.MaxBatDisChargRatio); MasterControlMsg = "【主储能】放电【管理大楼】:【主储能】全功率放电无法补全差值,【主储能】全功率放电"; } } else { //储能处于待机状态,防止其他的状态残留 MasterClient.ServerCmd.CmdType = "DisCharg";//功率为0,充放电的指令类型无所谓 MasterClient.ServerCmd.CmdPw = 0; MasterControlMsg = "【主储能】放电【管理大楼】:【主储能】无法放电,【主储能】不动作"; } } else { //真实的负载功率比较小,防止储能放电导致逆流,就不需要输出动作了,等真实的负载大于一个值后才开始介入 MasterClient.ServerCmd.CmdType = "DisCharg"; //功率为0,充放电的指令类型无所谓 MasterClient.ServerCmd.CmdPw = 0; MasterControlMsg = "【主储能】放电【管理大楼】:【管理大楼】负载较小,为防止放电导致逆流,【主储能】不动作"; } //只能一个放电,另一个停止放电 SlaveClient.ServerCmd.CmdPw = 0; } else { ManageRealPw = EsEleMeter4.AvePw - EsEleMeter1.AvePw; /////////////////////Slave从控制柜给管理大楼供电 ////注意方向 ////从储能箱体放电的控制 //ManageRealPw = EsEleMeter4.AvePw - EsEleMeter1.AvePw; //if (ManageRealPw > ManageTargetPw)//真实的负载大于一个值的时候才介入,或者控制关口电表的数据维持在一个水平,两个不同的控制方式 //{ // //Slave 从储能箱根据负载情况给管理大楼放电 // //可以放电的话,则 // if (SlaveClient.IsCanDisCharg()) // { // SlaveClient.ServerCmd.CmdType = "DisCharg"; // ManageDifLoad = ManageRealPw - ManageTargetPw; // if (ManageDifLoad < SlaveClient.ClientInfo!.MaxDisChargPw) // { // //储能发送功率 补全总功率数据 // //DisChargPwCmd(AcrelMeters.Psum - EleMeterActionLimitValue.MidValue); // SlaveClient.ServerCmd.CmdPw = ManageDifLoad; // SlaveControlMsg = "【从储能】放电【管理大楼】:【从储能】放电补全目标差值,【从储能】正常放电"; // } // else //储能不能满足总的差值 // { // //满功率放电 // //储能全功率放电,不足的部分电网自动补全 // SlaveClient.ServerCmd.CmdPw = (SlaveClient.ClientInfo.MaxDisChargPw * ConfigDataService.energyStorageRunConfig.MaxBatDisChargRatio); // SlaveControlMsg = "【从储能】放电【管理大楼】:【从储能】全功率放电无法补全差值,【从储能】全功率放电"; // } // } // else // { // //储能处于待机状态,防止其他的状态残留 // SlaveClient.ServerCmd.CmdType = "DisCharg";//功率为0,充放电的指令类型无所谓 // SlaveClient.ServerCmd.CmdPw = 0; // SlaveControlMsg = "【从储能】放电【管理大楼】:【从储能】无法放电,【从储能】不动作"; // } //} //else //{ // //真实的负载功率比较小,防止储能放电导致逆流,就不需要输出动作了,等真实的负载大于一个值后才开始介入 // SlaveClient.ServerCmd.CmdType = "DisCharg"; //功率为0,充放电的指令类型无所谓 // SlaveClient.ServerCmd.CmdPw = 0; // SlaveControlMsg = "【从储能】放电【管理大楼】:【管理大楼】负载较小,为防止放电导致逆流,【从储能】不动作"; //} ////只能一个放电,另一个停止放电 //MasterClient.ServerCmd.CmdPw = 0; } } catch (Exception ex) { LogService.Info($"时间:{DateTime.Now.ToString()}-【EntryNoSolar】-{ex.Message}"); } } }, NoSolarTokenSource.Token); } /// /// 推出 NoSolar /// /// private void ExitNoSolar() { NoSolarTokenSource.Cancel(); LogService.Info($"时间:{DateTime.Now.ToString()}-【动作】-离开【EntryNoSolar】模式"); } /// /// 进入 Wait 等待状态 /// /// private void EntryWait() { CurESChargInfo = ESChargInfo.Wait; Console.WriteLine($"时间:{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}-【月浦】- 运行进入【Wait】模式 "); WaitTokenSource = new CancellationTokenSource(); LogService.Info($"时间:{DateTime.Now.ToString()}-【动作】-进入【EntryWait】模式"); Task.Run(() => { //判断状态是否正确 while (CurESChargInfo == ESChargInfo.Wait) { try { if (WaitTokenSource.IsCancellationRequested) { break; } if (MasterClient == null)//防止未加载导致错误 { continue; } //等待期间 关闭两个储能柜的输出 SlaveClient.ServerCmd.CmdPw = 0; MasterClient.ServerCmd.CmdPw = 0; //等待期间 两个储能柜关闭液冷 SlaveClient.ServerCmd.CoolOnOffCmd = false; MasterClient.ServerCmd.CoolOnOffCmd = false; Thread.Sleep(30000); //WaitEnvActionResult //循环执行方法 // 1) 关闭光伏输出,为了确保安全,防止光伏这个时候比较还输出导致逆流产生 //确认光伏是否立刻断开 //当通信失败时,则认为光伏关闭,不需要通信确认光伏关闭 if (CheckSolarState() == false && GetTotalRtSolarPw() < 5) { LogService.Info($"时间:{DateTime.Now.ToString()}-【动作】-【Wait】状态-确认光伏是否立刻断开,确认等待光伏结果值是OK的"); WaitSolarActionResult = true; } else { WaitSolarActionResult = false; //不满足条件的话,可能还在动作中,功率还没有降低到快为0,需要判断是否关闭,如果关闭了就需要等待,不需要再关闭了 if (CheckSolarState() == true) { LogService.Info($"时间:{DateTime.Now.ToString()}-【动作】-【Wait】状态-光伏不满足条件,关闭光伏"); CloseSolar(); } } // 2) 监控开关状态和合闸 确保电闸符合要求 //先检查状态 if (CheckSwitchState(PreESChargInfo)) { LogService.Info($"时间:{DateTime.Now.ToString()}-【动作】-【Wait】状态-检查电闸状态,状态符合要求"); WaitEleSwitchActionResult = true; } else//不满足状态,则先关闭电操,然后开启对应的电操 { LogService.Info($"时间:{DateTime.Now.ToString()}-【动作】-【Wait】状态-检查电闸状态,不状态符合要求,关闭所有电操,重新合闸需要的电操"); //关电操 QFSwitch1.SetRtSwitch(SwitchEm.Off); QFSwitch2.SetRtSwitch(SwitchEm.Off); QFSwitch3.SetRtSwitch(SwitchEm.Off); QFSwitch4.SetRtSwitch(SwitchEm.Off); //等待2秒 Thread.Sleep(3000); SetControlModelNoBeforClose(PreESChargInfo); } //准备满足条件,可以切换状态 if (WaitSolarActionResult && WaitEleSwitchActionResult) { LogService.Info($"时间:{DateTime.Now.ToString()}-【动作】-【Wait】状态-所有的状态都满足,开始进行预设的模式切换"); switch (PreESChargInfo) { case ESChargInfo.Inital: break; case ESChargInfo.Master: SysRunStateMachine.Fire(ESChargInfoTrig.MasterTrig); break; case ESChargInfo.Slave: SysRunStateMachine.Fire(ESChargInfoTrig.SlaveTrig); break; case ESChargInfo.Night_Master: SysRunStateMachine.Fire(ESChargInfoTrig.Night_MasterTrig); break; case ESChargInfo.Night_Slave: SysRunStateMachine.Fire(ESChargInfoTrig.Night_SlaveTrig); break; case ESChargInfo.Wait: SysRunStateMachine.Fire(ESChargInfoTrig.WaitTrig); break; case ESChargInfo.NoSolar: SysRunStateMachine.Fire(ESChargInfoTrig.NoSolarTrig); break; default: break; } } } catch (Exception ex) { LogService.Info($"时间:{DateTime.Now.ToString()}-【EntryWait】-{ex.Message}"); } } }, WaitTokenSource.Token); } /// /// 等待的光伏动作结果值 /// private bool WaitSolarActionResult { get; set; } = false; /// /// 等待环境切换结果值 PCS 液冷 /// 先关掉PCS和液冷 /// 直接切换电操长时间会导致PCS和液冷报错。 /// private bool WaitEnvActionResult { get; set; } = false; /// /// 等待的电操动作结果值 /// private bool WaitEleSwitchActionResult { get; set; } = false; /// /// 退出 Wait 等待状态 /// /// private void ExitWait() { WaitTokenSource.Cancel(); //开启液冷 SlaveClient.ServerCmd.CoolOnOffCmd = true; MasterClient.ServerCmd.CoolOnOffCmd = true; LogService.Info($"时间:{DateTime.Now.ToString()}-【动作】-离开【EntryWait】模式"); } /// /// 进入 Night_Slave /// /// private void EntryNight_Slave() { CurESChargInfo = ESChargInfo.Night_Slave; Console.WriteLine($"时间:{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}-【月浦】- 运行进入【Night_Slave】模式 "); Night_SlaveTokenSource = new CancellationTokenSource(); LogService.Info($"时间:{DateTime.Now.ToString()}-【动作】-进入【EntryNight_Slave】模式"); Task.Run(() => { double? lastSlaveNightTargetSoc = null; bool? lastSlaveShouldCharge = null; bool? lastSlaveInWeekendTime = null; //判断状态是否正确 while (CurESChargInfo == ESChargInfo.Night_Slave) { try { Thread.Sleep(1000); if (Night_SlaveTokenSource.IsCancellationRequested) { break; } if (MasterClient == null)//防止未加载导致错误 { continue; } if (!YuPuAutoHand) { continue; } ////打开光伏 //if (CheckSolarState() == false) //{ // OpenSolar(); //} bool inWeekendTime = IsInWeekendTime(); var slaveNightTargetSoc = Math.Min(NightSlave_ToSlaveFullSoc, ConfigDataService.energyStorageRunConfig.BMSSocUpSignLimitValue); bool shouldCharge = !inWeekendTime && SlaveClient.ClientInfo!.SOC < slaveNightTargetSoc; if (lastSlaveNightTargetSoc == null || Math.Abs(lastSlaveNightTargetSoc.Value - slaveNightTargetSoc) > 0.0001 || lastSlaveInWeekendTime == null || lastSlaveInWeekendTime.Value != inWeekendTime) { LogService.Info($"时间:{DateTime.Now}-【夜间充电目标】-模式:Night_Slave,从储能SOC:{SlaveClient.ClientInfo!.SOC:0.##},配置目标SOC:{NightSlave_ToSlaveFullSoc:0.##},BMS上限:{ConfigDataService.energyStorageRunConfig.BMSSocUpSignLimitValue:0.##},采用目标SOC:{slaveNightTargetSoc:0.##},是否周末禁充:{inWeekendTime}"); lastSlaveNightTargetSoc = slaveNightTargetSoc; lastSlaveInWeekendTime = inWeekendTime; } if (lastSlaveShouldCharge == null || lastSlaveShouldCharge.Value != shouldCharge) { if (shouldCharge) { LogService.Info($"时间:{DateTime.Now}-【夜间充电开始】-模式:Night_Slave,从储能SOC:{SlaveClient.ClientInfo!.SOC:0.##},目标SOC:{slaveNightTargetSoc:0.##}"); } else { LogService.Info($"时间:{DateTime.Now}-【夜间充电停止】-模式:Night_Slave,从储能SOC:{SlaveClient.ClientInfo!.SOC:0.##},目标SOC:{slaveNightTargetSoc:0.##},是否周末禁充:{inWeekendTime}"); } lastSlaveShouldCharge = shouldCharge; } if (!inWeekendTime) { //展示用 ManageRealPw = EsEleMeter4.AvePw - EsEleMeter1.AvePw; TaxRealPw = SolarEleMeter5.AvePw - SolarEleMeter3.AvePw;//光伏和市电供应给税务大楼的负载 //循环执行方法 //从储能箱充满为止 if (SlaveClient.ClientInfo!.SOC < slaveNightTargetSoc) { //主储能箱充电 SlaveClient.ServerCmd.CmdType = "Charg"; SlaveClient.ServerCmd.CmdPw = SlaveClient.ClientInfo!.MaxDisChargPw * ConfigDataService.energyStorageRunConfig.MaxBatDisChargRatio; } else { //从储能箱充电 SlaveClient.ServerCmd.CmdType = "Charg"; SlaveClient.ServerCmd.CmdPw = 0; } //主储能箱不运行 MasterClient.ServerCmd.CmdType = "Charg"; MasterClient.ServerCmd.CmdPw = 0; } else { //周五或者周六 //主从都不充电 MasterClient.ServerCmd.CmdType = "Charg"; MasterClient.ServerCmd.CmdPw = 0; //MasterControlMsg = "【主储能】周五周六晚上不充电,【主储能】不动作"; SlaveClient.ServerCmd.CmdType = "Charg"; SlaveClient.ServerCmd.CmdPw = 0; //SlaveControlMsg = "【从储能】周五周六晚上不充电,【从储能】不动作"; } } catch (Exception ex) { LogService.Info($"时间:{DateTime.Now.ToString()}-【EntryAlarm】-{ex.Message}"); } } }, Night_SlaveTokenSource.Token); } /// /// 退出 Night_Slave /// /// private void ExitNight_Slave() { Night_SlaveTokenSource.Cancel(); //退出Night_Slave状态,关闭输出 SlaveClient.ServerCmd.CmdPw = 0; MasterClient.ServerCmd.CmdPw = 0; LogService.Info($"时间:{DateTime.Now.ToString()}-【动作】-离开【EntryNight_Slave】模式"); } /// /// 进入 Night_Master状态 /// /// private void EntryNight_Master() { CurESChargInfo = ESChargInfo.Night_Master; Console.WriteLine($"时间:{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}-【月浦】- 运行进入【Night_Master】模式 "); Night_MasterTokenSource = new CancellationTokenSource(); LogService.Info($"时间:{DateTime.Now.ToString()}-【动作】-进入【EntryNight_Master】模式"); Task.Run(() => { double? lastMasterNightTargetSoc = null; bool? lastMasterShouldCharge = null; bool? lastMasterInWeekendTime = null; //判断状态是否正确 while (CurESChargInfo == ESChargInfo.Night_Master) { try { Thread.Sleep(1000); if (Night_MasterTokenSource.IsCancellationRequested) { break; } if (MasterClient == null)//防止未加载导致错误 { continue; } if (!YuPuAutoHand) { continue; } ////打开光伏 //if (CheckSolarState() == false) //{ // OpenSolar(); //} bool inWeekendTime = IsInWeekendTime(); var masterNightTargetSoc = Math.Min(NightMaster_ToMasterFullSoc, ConfigDataService.energyStorageRunConfig.BMSSocUpSignLimitValue); bool shouldCharge = !inWeekendTime && MasterClient.ClientInfo!.SOC < masterNightTargetSoc; if (lastMasterNightTargetSoc == null || Math.Abs(lastMasterNightTargetSoc.Value - masterNightTargetSoc) > 0.0001 || lastMasterInWeekendTime == null || lastMasterInWeekendTime.Value != inWeekendTime) { LogService.Info($"时间:{DateTime.Now}-【夜间充电目标】-模式:Night_Master,主储能SOC:{MasterClient.ClientInfo!.SOC:0.##},配置目标SOC:{NightMaster_ToMasterFullSoc:0.##},BMS上限:{ConfigDataService.energyStorageRunConfig.BMSSocUpSignLimitValue:0.##},采用目标SOC:{masterNightTargetSoc:0.##},是否周末禁充:{inWeekendTime}"); lastMasterNightTargetSoc = masterNightTargetSoc; lastMasterInWeekendTime = inWeekendTime; } if (lastMasterShouldCharge == null || lastMasterShouldCharge.Value != shouldCharge) { if (shouldCharge) { LogService.Info($"时间:{DateTime.Now}-【夜间充电开始】-模式:Night_Master,主储能SOC:{MasterClient.ClientInfo!.SOC:0.##},目标SOC:{masterNightTargetSoc:0.##}"); } else { LogService.Info($"时间:{DateTime.Now}-【夜间充电停止】-模式:Night_Master,主储能SOC:{MasterClient.ClientInfo!.SOC:0.##},目标SOC:{masterNightTargetSoc:0.##},是否周末禁充:{inWeekendTime}"); } lastMasterShouldCharge = shouldCharge; } if (!inWeekendTime) { //展示用 ManageRealPw = EsEleMeter4.AvePw - EsEleMeter1.AvePw; TaxRealPw = SolarEleMeter5.AvePw - SolarEleMeter3.AvePw;//光伏和市电供应给税务大楼的负载 //循环执行方法 //主储能箱充满为止 if (MasterClient.ClientInfo!.SOC < masterNightTargetSoc) { //主储能箱充电 MasterClient.ServerCmd.CmdType = "Charg"; MasterClient.ServerCmd.CmdPw = MasterClient.ClientInfo!.MaxDisChargPw * ConfigDataService.energyStorageRunConfig.MaxBatDisChargRatio; } else { //主储能箱充电 MasterClient.ServerCmd.CmdType = "Charg"; MasterClient.ServerCmd.CmdPw = 0; } //从储能箱不运行 SlaveClient.ServerCmd.CmdType = "Charg"; SlaveClient.ServerCmd.CmdPw = 0; } else { //周五或者周六 //主从都不充电 MasterClient.ServerCmd.CmdType = "Charg"; MasterClient.ServerCmd.CmdPw = 0; //MasterControlMsg = "【主储能】周五周六晚上不充电,【主储能】不动作"; SlaveClient.ServerCmd.CmdType = "Charg"; SlaveClient.ServerCmd.CmdPw = 0; //SlaveControlMsg = "【从储能】周五周六晚上不充电,【从储能】不动作"; } } catch (Exception ex) { LogService.Info($"时间:{DateTime.Now.ToString()}-【EntryAlarm】-{ex.Message}"); } } }, Night_MasterTokenSource.Token); } /// /// 推出 Night_Master状态 /// /// private void ExitNight_Master() { Night_MasterTokenSource.Cancel(); //退出Night_Master状态,关闭输出 SlaveClient.ServerCmd.CmdPw = 0; MasterClient.ServerCmd.CmdPw = 0; LogService.Info($"时间:{DateTime.Now.ToString()}-【动作】-离开【EntryNight_Master】模式"); } /// /// 进入 Slave状态 /// /// private void EntrySlave() { CurESChargInfo = ESChargInfo.Slave; Console.WriteLine($"时间:{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}-【月浦】- 运行进入【Slave】模式 "); SlaveTokenSource = new CancellationTokenSource(); LogService.Info($"时间:{DateTime.Now.ToString()}-【动作】-进入【EntrySlave】模式"); Task.Run(() => { //判断状态是否正确 while (CurESChargInfo == ESChargInfo.Slave) { try { Thread.Sleep(1000); if (SlaveTokenSource.IsCancellationRequested) { break; } if (MasterClient == null)//防止未加载导致错误 { continue; } if (!YuPuAutoHand) { continue; } //打开光伏 if (CheckSolarState() == false) { OpenSolar(); } //循环执行方法 //在峰的时候放电 //if (PeakValleySglModel == ElePVEnum.TopPeak || PeakValleySglModel == ElePVEnum.Peak) if (ConfigDataService.IsTimeCanDischarge()) { ///////////////////Slave从控制柜给管理大楼供电 //注意方向 //从储能箱体放电的控制 ManageRealPw = EsEleMeter4.AvePw - EsEleMeter1.AvePw; if (ManageRealPw > ManageTargetPw)//真实的负载大于一个值的时候才介入,或者控制关口电表的数据维持在一个水平,两个不同的控制方式 { //Slave 从储能箱根据负载情况给管理大楼放电 //可以放电的话,则 if (SlaveClient.IsCanDisCharg()) { SlaveClient.ServerCmd.CmdType = "DisCharg"; ManageDifLoad = ManageRealPw - ManageTargetPw; if (ManageDifLoad < SlaveClient.ClientInfo!.MaxDisChargPw) { //储能发送功率 补全总功率数据 //DisChargPwCmd(AcrelMeters.Psum - EleMeterActionLimitValue.MidValue); SlaveClient.ServerCmd.CmdPw = ManageDifLoad; SlaveControlMsg = "【从储能】放电【管理大楼】:【从储能】放电补全目标差值,【从储能】正常放电"; } else //储能不能满足总的差值 { //满功率放电 //储能全功率放电,不足的部分电网自动补全 SlaveClient.ServerCmd.CmdPw = (SlaveClient.ClientInfo.MaxDisChargPw * ConfigDataService.energyStorageRunConfig.MaxBatDisChargRatio); SlaveControlMsg = "【从储能】放电【管理大楼】:【从储能】全功率放电无法补全差值,【从储能】全功率放电"; } } else { //储能处于待机状态,防止其他的状态残留 SlaveClient.ServerCmd.CmdType = "DisCharg";//功率为0,充放电的指令类型无所谓 SlaveClient.ServerCmd.CmdPw = 0; SlaveControlMsg = "【从储能】放电【管理大楼】:【从储能】无法放电,【从储能】不动作"; } } else { //真实的负载功率比较小,防止储能放电导致逆流,就不需要输出动作了,等真实的负载大于一个值后才开始介入 SlaveClient.ServerCmd.CmdType = "DisCharg"; //功率为0,充放电的指令类型无所谓 SlaveClient.ServerCmd.CmdPw = 0; SlaveControlMsg = "【从储能】放电【管理大楼】:【管理大楼】负载较小,为防止放电导致逆流,【从储能】不动作"; } } else { //展示用 ManageRealPw = EsEleMeter4.AvePw - EsEleMeter1.AvePw; SlaveClient.ServerCmd.CmdType = "DisCharg"; //功率为0,充放电的指令类型无所谓 SlaveClient.ServerCmd.CmdPw = 0; SlaveControlMsg = "【从储能】放电【管理大楼】:当前未处于峰和尖峰时刻,【从储能】不动作"; } ///////////////主储能箱体充电 吸收光伏能量的控制 //注意方向 TaxRealPw = SolarEleMeter5.AvePw - SolarEleMeter3.AvePw;//光伏和市电供应给税务大楼的负载 TotalSolarPw = Math.Abs(SolarEleMeter2.AvePw + SolarEleMeter3.AvePw); //调节储能吸收光伏能量来调节光伏到税务大楼的能量,根据真实负载的值来判断是否调控介入 if (TaxRealPw > TaxTargetPwBigSmallUp) { ////////////////////调节使光伏尽可能满足税务大楼//////////////////// if (MasterClient.IsCanCharg()) { //差值 这个差值是3号电表已经补充进去之后差值 TaxDifLoad = TaxRealPw - TaxTargetPw; //*************** 储能和光伏综合调节达到这个差值 TaxDifLoad *************** if (TaxDifLoad < TotalSolarPw)//光伏的现在能力能达到这个差值,就是可以调节 { //差值 var difValue1 = TotalSolarPw - TaxDifLoad; // 储能吸收这个差值,剩余的TaxDifLoad 光伏值给税务大楼,以达到要求 MasterClient.ServerCmd.CmdPw = difValue1; MasterClient.ServerCmd.CmdType = "Charg"; MasterControlMsg = "【主储能】充电吸收【光伏】电能:【主储能】充电满足差值,【主储能】正常充电"; } else { //当前光伏的所有能量无法满足需要这个差值,那么全部光伏给税务大楼,储能不进行充电 MasterClient.ServerCmd.CmdPw = 0; MasterClient.ServerCmd.CmdType = "Charg"; MasterControlMsg = "【主储能】充电吸收【光伏】电能:【主储能】不需要充电,把光伏都给税务大楼,【主储能】不动作"; } } else { MasterControlMsg = "【主储能】充电吸收【光伏】电能:【主储能】无法充电,【主储能】停止工作"; } } else { /////////////真实的负载小于目标值 //让主储能尽力充电吸走光伏的能力,为了防止逆流, if (MasterClient.IsCanCharg()) { if (TotalSolarPw < MasterClient.ClientInfo!.MaxChargPw) { //光伏发电能力小于储能充电的能力,储能把光伏吸收完全 MasterClient.ServerCmd.CmdType = "Charg"; MasterClient.ServerCmd.CmdPw = TotalSolarPw; MasterControlMsg = "【主储能】充电吸收【光伏】电能:【主储能】完全吸收光伏电能,【主储能】正常充电"; } else { //光伏发电能力大于储能充电能力,则储能尽可能充电吧,存在逆流可能了,储能尽力了 【存在逆流的可能性】 MasterClient.ServerCmd.CmdType = "Charg"; MasterClient.ServerCmd.CmdPw = (MasterClient.ClientInfo!.MaxChargPw * ConfigDataService.energyStorageRunConfig.MaxBatChargRatio); MasterControlMsg = "【主储能】充电吸收【光伏】电能:税务大楼负载小,【主储能】最大功率充电吸收光伏电能,存在【逆流】可能性!!!!!!!!!!!!!!!!!!"; } } else { MasterControlMsg = "【主储能】充电吸收【光伏】电能:【主储能】无法充电,【主储能】停止工作"; } } } catch (Exception ex) { LogService.Info($"时间:{DateTime.Now.ToString()}-【EntryAlarm】-{ex.Message}"); } } }, SlaveTokenSource.Token); } /// /// 退出 Slave状态 /// /// private void ExitSlave() { SlaveTokenSource.Cancel(); //退出Slave状态,关闭输出 SlaveClient.ServerCmd.CmdPw = 0; MasterClient.ServerCmd.CmdPw = 0; LogService.Info($"时间:{DateTime.Now.ToString()}-【动作】-离开【EntrySlave】模式"); } /// /// 进入 Master状态 /// /// private void EntryMaster() { CurESChargInfo = ESChargInfo.Master; Console.WriteLine($"时间:{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}-【月浦】- 运行进入【Master】模式 "); MasterTokenSource = new CancellationTokenSource(); LogService.Info($"时间:{DateTime.Now.ToString()}-【动作】-进入【EntryMaster】模式"); Task.Run(() => { //判断状态是否正确 while (CurESChargInfo == ESChargInfo.Master) { try { Thread.Sleep(1000); if (MasterTokenSource.IsCancellationRequested) { break; } if (MasterClient == null)//防止未加载导致错误 { continue; } if (!YuPuAutoHand) { continue; } //打开光伏 if (CheckSolarState() == false) { OpenSolar(); } //循环执行方法 //在峰的时候放电 //if (PeakValleySglModel == ElePVEnum.TopPeak || PeakValleySglModel == ElePVEnum.Peak) if (ConfigDataService.IsTimeCanDischarge()) { ///////////////////Master主控制柜给管理大楼供电 //注意方向 //主储能箱体放电的控制 ManageRealPw = EsEleMeter4.AvePw - EsEleMeter1.AvePw; if (ManageRealPw > ManageTargetPw)//真实的负载大于一个值的时候才介入,或者控制关口电表的数据维持在一个水平,两个不同的控制方式 { //Master 主储能箱根据负载情况给管理大楼放电 //可以放电的话,则 if (MasterClient.IsCanDisCharg()) { MasterClient.ServerCmd.CmdType = "DisCharg"; ManageDifLoad = ManageRealPw - ManageTargetPw; if (ManageDifLoad < MasterClient.ClientInfo.MaxDisChargPw) { //储能发送功率 补全总功率数据 //DisChargPwCmd(AcrelMeters.Psum - EleMeterActionLimitValue.MidValue); MasterClient.ServerCmd.CmdPw = ManageDifLoad; MasterControlMsg = "【主储能】放电【管理大楼】:【主储能】放电补全目标差值,【主储能】正常放电"; } else //储能不能满足总的差值 { //满功率放电 //储能全功率放电,不足的部分电网自动补全 MasterClient.ServerCmd.CmdPw = (MasterClient.ClientInfo.MaxDisChargPw * ConfigDataService.energyStorageRunConfig.MaxBatDisChargRatio); MasterControlMsg = "【主储能】放电【管理大楼】:【主储能】全功率放电无法补全差值,【主储能】全功率放电"; } } else { //储能处于待机状态,防止其他的状态残留 MasterClient.ServerCmd.CmdType = "DisCharg";//功率为0,充放电的指令类型无所谓 MasterClient.ServerCmd.CmdPw = 0; MasterControlMsg = "【主储能】放电【管理大楼】:【主储能】无法放电,【主储能】不动作"; } } else { //真实的负载功率比较小,防止储能放电导致逆流,就不需要输出动作了,等真实的负载大于一个值后才开始介入 MasterClient.ServerCmd.CmdType = "DisCharg"; //功率为0,充放电的指令类型无所谓 MasterClient.ServerCmd.CmdPw = 0; MasterControlMsg = "【主储能】放电【管理大楼】:【管理大楼】负载较小,为防止放电导致逆流,【主储能】不动作"; } } else { ManageRealPw = EsEleMeter4.AvePw - EsEleMeter1.AvePw; MasterClient.ServerCmd.CmdType = "DisCharg"; //功率为0,充放电的指令类型无所谓 MasterClient.ServerCmd.CmdPw = 0; MasterControlMsg = "【主储能】放电【管理大楼】:当前未处于峰和尖峰时刻,【主储能】不放电"; } ///////////////从储能箱体充电 吸收光伏能量的控制 //注意方向 TaxRealPw = SolarEleMeter5.AvePw - SolarEleMeter3.AvePw;//光伏和市电供应给税务大楼的负载 TotalSolarPw = Math.Abs(SolarEleMeter2.AvePw + SolarEleMeter3.AvePw); //调节储能吸收光伏能量来调节光伏到税务大楼的能量,根据真实负载的值来判断是否调控介入 //税务真实的负载比较大的时候,储能参与调控吸收能量,真实的负载比较小的时候,则调动储能吸收全部光伏能量。 //这个点的判断跟其他的标准的不一样 if (TaxRealPw >= TaxTargetPwBigSmallUp)//TaxTargetPwBigSmall则是认为税务负载大和小的值 { //有负载,并且认为负载较大,则参与调控 ////////////////////调节使光伏尽可能满足税务大楼//////////////////// if (SlaveClient.IsCanCharg()) { //差值 这个差值是3号电表已经补充进去之后差值 TaxDifLoad = TaxRealPw - TaxTargetPw; //*************** 储能和光伏综合调节使3号电表给税务大楼供电达到这个差值 TaxDifLoad *************** if (TaxDifLoad < TotalSolarPw)//光伏的现在能力能达到这个差值,就是可以调节 { //差值 var difValue1 = TotalSolarPw - TaxDifLoad; // 储能吸收这个差值,剩余的TaxDifLoad 光伏值给税务大楼,以达到要求 SlaveClient.ServerCmd.CmdPw = difValue1; SlaveClient.ServerCmd.CmdType = "Charg"; SlaveControlMsg = "【从储能】充电吸收【光伏】电能:【从储能】充电满足差值1,【从储能】正常充电"; } else { //当前光伏的所有能量无法满足需要这个差值,那么全部光伏给税务大楼,储能不进行充电 SlaveClient.ServerCmd.CmdPw = 0; SlaveClient.ServerCmd.CmdType = "Charg"; SlaveControlMsg = "【从储能】充电吸收【光伏】电能:【从储能】不需要充电,把光伏都给税务大楼2,【从储能】不动作"; } } else { //从储能无法充电 SlaveClient.ServerCmd.CmdType = "Charg"; SlaveClient.ServerCmd.CmdPw = 0; SlaveControlMsg = "【从储能】充电吸收【光伏】电能:【从储能】无法充电3,【从储能】停止工作"; } } else//真实的负载比较小的时候,认为负载很小,则考虑调动储能吸收光伏能量。 { //观测3号电表的情况,维持3号电表的数据就相当于控制光伏输出到税务大楼的能量 //这样控制会比较直接,否则波动有点大 if (SlaveClient.IsCanCharg()) { if (TotalSolarPw < SlaveClient.ClientInfo!.MaxChargPw) { //光伏发电能力小于储能充电的能力,储能把光伏吸收完全 SlaveClient.ServerCmd.CmdType = "Charg"; SlaveClient.ServerCmd.CmdPw = TotalSolarPw; SlaveControlMsg = "【从储能】充电吸收【光伏】电能:【从储能】完全吸收光伏电能4,【从储能】正常充电"; } else { //光伏发电能力大于储能充电能力,则储能尽可能充电吧,存在逆流可能了,储能尽力了 【存在逆流的可能性】 SlaveClient.ServerCmd.CmdType = "Charg"; SlaveClient.ServerCmd.CmdPw = (SlaveClient.ClientInfo!.MaxChargPw * ConfigDataService.energyStorageRunConfig.MaxBatChargRatio); SlaveControlMsg = "【从储能】充电吸收【光伏】电能:税务大楼负载小5,【从储能】最大功率充电吸收光伏电能,存在【逆流】可能性"; } } else { //从储能无法充电 SlaveClient.ServerCmd.CmdType = "Charg"; SlaveClient.ServerCmd.CmdPw = 0; SlaveControlMsg = "【从储能】充电吸收【光伏】电能:【从储能】无法充电,【从储能】停止工作"; } } } catch (Exception ex) { LogService.Info($"时间:{DateTime.Now.ToString()}-【EntryAlarm】-{ex.Message}"); } } }, MasterTokenSource.Token); } /// /// 退出 Master状态 /// /// private void ExitMaster() { MasterTokenSource.Cancel(); //退出Master状态,关闭输出 SlaveClient.ServerCmd.CmdPw = 0; MasterClient.ServerCmd.CmdPw = 0; LogService.Info($"时间:{DateTime.Now.ToString()}-【动作】-推出【EntryMaster】模式"); } /// /// 获取运行状态 /// private void GetRunState() { ///////////////////确认控制模式//////////////////////////////////////// //在白天的情况下,主储能箱和从储能储能箱切换的标准:以不浪费光伏为主 //依据当前的状态判断是否需要切换到其他状态 switch (CurESChargInfo) { case ESChargInfo.Master: //判断从储能箱是否被光伏充满,以不浪费光伏为主 if (SlaveClient.ClientInfo!.SOC >= 85) { //从储能箱充满,那么具备切换的条件,还需要判断一下,主储能箱体是否被消耗掉 if (MasterClient.ClientInfo!.SOC <= 80) { if (PreCheckIsESChargInfoChange(ESChargInfo.Slave)) { if (!IsESChargInfoChange) { //模式改变的话,先关闭输出,继续下一个循环,给开关动作预留时间 SlaveClient.ServerCmd.CmdPw = 0; MasterClient.ServerCmd.CmdPw = 0; IsESChargInfoChange = true; return; } } //从储能箱充满,主储能箱未满,则可以切换 CurESChargInfo = ESChargInfo.Slave; IsESChargInfoChange = false; //SetControlModel(CurESChargInfo); } else { //从储能箱充满,主储能箱并没有完全释放完毕,即是两个储能箱体都满了, //从储能箱停止光伏充电,直到主储能箱释放能量后再切换 //那么此时如果光伏发电很大,税务大楼负载很小,触发光伏并网柜的逆流保护,切换光伏并网柜的市电,光伏停止输出,从储能箱PCS无市电会报错(交流欠压)。 //还能切换回来吗?如何恢复? //当主储能继续放电,那么后面可以切换, // 1)从储能箱体切到管理大楼(从储能箱从刚才的报错中恢复正常),主储能箱切换到税务大楼并网柜(已经停止市电连接了),也会PCS无市电主储能箱报错(交流欠压),需要人过来切换3号电表附近的那个开关 // 2) 切到晚上了,主储能没有影响,从储能本身就满了,然后继续无市电,报错 SlaveClient.ServerCmd.CmdType = "Charg"; SlaveClient.ServerCmd.CmdPw = 0; } } else { //以不浪费光伏为主 //从储能箱光伏没有充满,维持当前的状态,可能主储能箱在放电中或者放电到SOC低后停止放电 } break; case ESChargInfo.Slave: //判断主储能箱是否被光伏充满,以不浪费光伏为主 if (MasterClient.ClientInfo!.SOC >= 85) { //主储能箱充满,那么具备切换的条件,还需要判断一下,从储能箱体是否被消耗掉 if (SlaveClient.ClientInfo!.SOC <= 80) { if (PreCheckIsESChargInfoChange(ESChargInfo.Master)) { if (!IsESChargInfoChange) { //模式改变的话,先关闭输出,继续下一个循环,给开关动作预留时间 SlaveClient.ServerCmd.CmdPw = 0; MasterClient.ServerCmd.CmdPw = 0; IsESChargInfoChange = true; return; } } //主储能箱充满,从储能箱未满,则可以切换 CurESChargInfo = ESChargInfo.Master; IsESChargInfoChange = false; //SetControlModel(CurESChargInfo); } else { //主储能箱充满,从储能箱并没有完全释放完毕,即是两个储能箱体都满了, //主储能箱停止光伏充电,直到从储能箱释放能量后再切换 // 那么此时如果光伏发电很大,税务大楼负载很小,触发光伏并网柜的逆流保护,切换光伏并网柜的市电,光伏停止输出,主储能箱PCS无市电会报错(交流欠压)。 //还能切换回来吗?如何恢复? //当从储能继续放电,那么后面可以切换, // 1)主储能箱体切到管理大楼(主储能箱从刚才的报错中恢复正常),从储能箱切换到税务大楼并网柜(已经停止市电连接了),也会PCS无市电从储能箱报错,需要人过来切换3号电表附近的那个开关 // 2) 切到晚上了,主储能切换到管理大楼,无市电导致PCS报错消失,正常运行,但是已经满了,晚上也不会充电了,从储能开始无市电,PCS报错(交流欠压) SlaveClient.ServerCmd.CmdType = "Charg"; SlaveClient.ServerCmd.CmdPw = 0; } } else { //主储能箱光伏没有充满,维持当前的状态 } break; //为什么白天有Night_Master的判断,因为从晚上切换到白天时,需要由晚上作为旧的基础模型然后切换到Master或者Slaver //先判断白天晚上,然后再具体操作模式,从晚上到白天后,具体模式还是晚上,需要从晚上的模式到白天的Master或者Slaver模式 case ESChargInfo.Night_Master: //从晚上切换到白天切换,默认晚上给主储能箱充电满了,直接到Master模式 //判断主储能箱是否被光伏充满,以不浪费光伏为主 //还需要判断吗?直接到Master不就行了嘛 if (MasterClient.ClientInfo!.SOC >= 70) { //主储能箱充满,那么具备切换的条件,还需要判断一下,从储能箱体是否被消耗掉 if (SlaveClient.ClientInfo!.SOC <= 60) { if (PreCheckIsESChargInfoChange(ESChargInfo.Master)) { if (!IsESChargInfoChange) { //模式改变的话,先关闭输出,继续下一个循环,给开关动作预留时间 SlaveClient.ServerCmd.CmdPw = 0; MasterClient.ServerCmd.CmdPw = 0; IsESChargInfoChange = true; return; } } //主储能箱充满,从储能箱未满,则可以切换 CurESChargInfo = ESChargInfo.Master; IsESChargInfoChange = false; //SetControlModel(CurESChargInfo); } else { //主储能箱充满,从储能箱并没有完全释放完毕,即是两个储能箱体都满了, //主储能箱停止光伏充电,直到从储能箱释放能量后再切换 // 那么此时如果光伏发电很大,税务大楼负载很小,触发光伏并网柜的逆流保护,切换光伏并网柜的市电,光伏停止输出,主储能箱PCS无市电会报错(交流欠压)。 //还能切换回来吗?如何恢复? //当从储能继续放电,那么后面可以切换, // 1)主储能箱体切到管理大楼(主储能箱从刚才的报错中恢复正常),从储能箱切换到税务大楼并网柜(已经停止市电连接了),也会PCS无市电从储能箱报错,需要人过来切换3号电表附近的那个开关 // 2) 切到晚上了,主储能切换到管理大楼,无市电导致PCS报错消失,正常运行,但是已经满了,晚上也不会充电了,从储能开始无市电,PCS报错(交流欠压) SlaveClient.ServerCmd.CmdType = "Charg"; SlaveClient.ServerCmd.CmdPw = 0; } } else { //主储能箱光伏没有充满,维持当前的状态 } break; default: break; } } /// /// 手动运行模式切换 /// public void HandRunModel(ESChargInfo Value) { switch (Value) { case ESChargInfo.Inital: break; case ESChargInfo.Master: PreESChargInfo = ESChargInfo.Master; SysRunStateMachine.Fire(ESChargInfoTrig.WaitTrig); break; case ESChargInfo.Slave: PreESChargInfo = ESChargInfo.Slave; SysRunStateMachine.Fire(ESChargInfoTrig.WaitTrig); break; case ESChargInfo.Night_Master: PreESChargInfo = ESChargInfo.Night_Master; SysRunStateMachine.Fire(ESChargInfoTrig.WaitTrig); break; case ESChargInfo.Wait: break; default: break; } } /// /// Master的线程取消操作 /// private CancellationTokenSource MasterTokenSource { get; set; } /// /// Slave的线程取消操作 /// private CancellationTokenSource SlaveTokenSource { get; set; } /// /// Night_Master的线程取消操作 /// private CancellationTokenSource Night_MasterTokenSource { get; set; } /// /// Night_Slave的线程取消操作 /// private CancellationTokenSource Night_SlaveTokenSource { get; set; } /// /// Wait的线程取消操作 /// private CancellationTokenSource WaitTokenSource { get; set; } /// /// NoSolar的线程取消操作 /// private CancellationTokenSource NoSolarTokenSource { get; set; } /// /// 关闭系统:停止线程循环并断开所有通信连接 /// public void CloseSystem() { // 先关闭扫描线程使能,令 while(ThreadEnable) 循环尽快退出 try { ThreadEnable = false; } catch { } try { MasterTokenSource?.Cancel(); } catch { } try { SlaveTokenSource?.Cancel(); } catch { } try { Night_MasterTokenSource?.Cancel(); } catch { } try { Night_SlaveTokenSource?.Cancel(); } catch { } try { WaitTokenSource?.Cancel(); } catch { } try { NoSolarTokenSource?.Cancel(); } catch { } // 停止输出,置零功率 try { if (SlaveClient != null) { SlaveClient.ServerCmd.CmdPw = 0; } } catch { } try { if (MasterClient != null) { MasterClient.ServerCmd.CmdPw = 0; } } catch { } // 断开设备通信 try { SolarMdDrive?.Close(); } catch { } try { MeterMdDrive?.Close(); } catch { } try { ModbusTcpNetDrive?.ConnectClose(); } catch { } try { BmsDataService.CloseDrive(); } catch { } try { FFService.CloseDrive(); } catch { } try { InPowerPCSDataService.CloseModbusRtu(); } catch { } } /// /// 预先状态 /// 就是确定目标状态,先进入准备状态时这个预先的状态信息,等条件具备后再进行操作 /// private ESChargInfo PreESChargInfo { get; set; } #endregion private bool _YuPuAutoHand = true; /// /// 手自动信息 /// 0未手动 false /// 1为自动 true /// public bool YuPuAutoHand { get { return _YuPuAutoHand; } set { _YuPuAutoHand = value; RaisePropertyChanged(); } } /// /// 客户端集合 /// public List ListDistClients { get; set; } /// /// 主客户端 /// public DistClient MasterClient { get; set; } /// /// Slave客户端 /// public DistClient SlaveClient { get; set; } /// /// 扫描线程 /// private void MeterScanDeviceStart() { ScanDeviceTask = Task.Run(async () => { while (ThreadEnable) { await Task.Delay(20); //DiagnosticsTime.Reset(); //DiagnosticsTime.Start(); try { //1号电表 站号1 OperateResultBytes1 = MeterMdDrive.Read($"s=1;0", 35); if (OperateResultBytes1.IsSuccess) { //有符号功率 二次有功功率数据,单位 W,转换成一次功率需乘上电流、电压变比 地址 07H //转换成Kw /1000 CT=80 EsEleMeter1.RtPw = MeterMdDrive.ByteTransform.TransInt16(OperateResultBytes1.Content, 14) * 80.0 / 1000.0;// EsEleMeter1.EleQ_P = MeterMdDrive.ByteTransform.TransUInt32(OperateResultBytes1.Content, 58) * 80.0 / 100.0;// EsEleMeter1.EleQ_N = MeterMdDrive.ByteTransform.TransUInt32(OperateResultBytes1.Content, 62) * 80.0 / 100.0;// //通信正常 EsEleMeter1.MeterLinkState = true; } else { //通信正常 EsEleMeter1.MeterLinkState = false; } //2号电表 站号2 OperateResultBytes2 = MeterMdDrive.Read($"s=2;0", 35); if (OperateResultBytes2.IsSuccess) { //有符号功率 二次有功功率数据,单位 W,转换成一次功率需乘上电流、电压变比 地址 07H //转换成Kw /1000 CT=20 SolarEleMeter2.RtPw = MeterMdDrive.ByteTransform.TransInt16(OperateResultBytes2.Content, 14) * 20.0 / 1000.0;// SolarEleMeter2.EleQ_P = MeterMdDrive.ByteTransform.TransUInt32(OperateResultBytes2.Content, 58) * 20.0 / 100.0;// SolarEleMeter2.EleQ_N = MeterMdDrive.ByteTransform.TransUInt32(OperateResultBytes2.Content, 62) * 20.0 / 100.0;// //通信正常 SolarEleMeter2.MeterLinkState = true; } else { //通信正常 SolarEleMeter2.MeterLinkState = false; } //3号电表 站号3 OperateResultBytes3 = MeterMdDrive.Read($"s=3;0", 35); if (OperateResultBytes3.IsSuccess) { //有符号功率 二次有功功率数据,单位 W,转换成一次功率需乘上电流、电压变比 地址 07H //转换成Kw /1000 CT=80 SolarEleMeter3.RtPw = MeterMdDrive.ByteTransform.TransInt16(OperateResultBytes3.Content, 14) * 80.0 / 1000.0;// SolarEleMeter3.EleQ_P = MeterMdDrive.ByteTransform.TransUInt32(OperateResultBytes3.Content, 58) * 80.0 / 100.0;// SolarEleMeter3.EleQ_N = MeterMdDrive.ByteTransform.TransUInt32(OperateResultBytes3.Content, 62) * 80.0 / 100.0;// //通信正常 SolarEleMeter3.MeterLinkState = true; } else { //通信正常 SolarEleMeter3.MeterLinkState = false; } //4号电表 站号4 OperateResultBytes4 = MeterMdDrive.Read($"s=4;0", 35); if (OperateResultBytes4.IsSuccess) { //有符号功率 二次有功功率数据,单位 W,转换成一次功率需乘上电流、电压变比 地址 07H //转换成Kw /1000 CT=80 EsEleMeter4.RtPw = MeterMdDrive.ByteTransform.TransInt16(OperateResultBytes4.Content, 14) * 80.0 / 1000.0;// EsEleMeter4.EleQ_P = MeterMdDrive.ByteTransform.TransUInt32(OperateResultBytes4.Content, 58) * 80.0 / 100.0;// EsEleMeter4.EleQ_N = MeterMdDrive.ByteTransform.TransUInt32(OperateResultBytes4.Content, 62) * 80.0 / 100.0;// //通信正常 EsEleMeter4.MeterLinkState = true; } else { //通信正常 EsEleMeter4.MeterLinkState = false; } //5号电表 站号5 OperateResultBytes5 = MeterMdDrive.Read($"s=5;x=3;2048", 28); if (OperateResultBytes5.IsSuccess) { //081AH 总有功功率 4 R 081CH A 相无功功 //本身单位Kw SolarEleMeter5.RtPw = MeterMdDrive.ByteTransform.TransSingle(OperateResultBytes5.Content, 52);// //Psum = MeterMdDrive.ByteTransform.TransSingle(OperateResultBytes1.Content, 52);//BaseIndex1 + 0x81A //Qsum = MeterMdDrive.ByteTransform.TransSingle(OperateResultBytes1.Content, 68);//BaseIndex1 + 0x822 //Ua = MeterMdDrive.ByteTransform.TransSingle(OperateResultBytes1.Content, 0);//BaseIndex1 + 0x800 //Ub = MeterMdDrive.ByteTransform.TransSingle(OperateResultBytes1.Content, 4);//BaseIndex1 + 0x802 //Uc = MeterMdDrive.ByteTransform.TransSingle(OperateResultBytes1.Content, 8);//BaseIndex1 + 0x804 //Ia = MeterMdDrive.ByteTransform.TransSingle(OperateResultBytes1.Content, 24);//BaseIndex1 + 0x80C //Ib = MeterMdDrive.ByteTransform.TransSingle(OperateResultBytes1.Content, 28);//BaseIndex1 + 0x80E //Ic = MeterMdDrive.ByteTransform.TransSingle(OperateResultBytes1.Content, 32);//BaseIndex1 + 0x810 //cosQ = MeterMdDrive.ByteTransform.TransSingle(OperateResultBytes1.Content, 100);//BaseIndex1 + 0x832 //通信正常 SolarEleMeter5.MeterLinkState = true; } else { //通信正常 SolarEleMeter5.MeterLinkState = false; } //两个合并的电能减去晚上储能空调的电能 SolarTotalPw = SolarEleMeter3.EleQ_N + SolarEleMeter2.EleQ_N - SolarEleMeter3.EleQ_P; } catch (Exception ex) { LogService.Info($"时间:{DateTime.Now.ToString()}-【Meter】-{ex.Message}"); } //DiagnosticsTime.Stop(); //ScanRtTimeinfo = $"电表:{DiagnosticsTime.Elapsed.TotalMilliseconds.ToString()}"; } }); } /// /// 远程IO扫描线程 /// private void ReIOScanDeviceStart() { ReIOScanDeviceTask = Task.Run(async () => { while (ThreadEnable) { await Task.Delay(100); //DiagnosticsTime.Reset(); //DiagnosticsTime.Start(); try { ReIOOperateResultBytes1 = ModbusTcpNetDrive.ReadBool($"x=1;1", 8); if (ReIOOperateResultBytes1.IsSuccess) { QFSwitch1.CurSwtichState = GetSwitch(ReIOOperateResultBytes1.Content[0]);//DI1 if (QFSwitch1.CurSwtichState == SwitchEm.On) { QFSwitch1.CurSwitchStateInfo = SwitchStateInfo.On; } else { QFSwitch1.CurSwitchStateInfo = SwitchStateInfo.Off; } QFSwitch2.CurSwtichState = GetSwitch(ReIOOperateResultBytes1.Content[2]);//DI3 if (QFSwitch2.CurSwtichState == SwitchEm.On) { QFSwitch2.CurSwitchStateInfo = SwitchStateInfo.On; } else { QFSwitch2.CurSwitchStateInfo = SwitchStateInfo.Off; } QFSwitch3.CurSwtichState = GetSwitch(ReIOOperateResultBytes1.Content[4]);//DI5 if (QFSwitch3.CurSwtichState == SwitchEm.On) { QFSwitch3.CurSwitchStateInfo = SwitchStateInfo.On; } else { QFSwitch3.CurSwitchStateInfo = SwitchStateInfo.Off; } QFSwitch4.CurSwtichState = GetSwitch(ReIOOperateResultBytes1.Content[6]);//DI7 if (QFSwitch4.CurSwtichState == SwitchEm.On) { QFSwitch4.CurSwitchStateInfo = SwitchStateInfo.On; } else { QFSwitch4.CurSwitchStateInfo = SwitchStateInfo.Off; } QFSwitch1.CurSwtichErrInfo = GetSwitchErr(ReIOOperateResultBytes1.Content[1]);//DI2 if (QFSwitch1.CurSwtichErrInfo == SwitchErr.NG) { QFSwitch1.CurSwitchStateInfo = SwitchStateInfo.Err; } QFSwitch2.CurSwtichErrInfo = GetSwitchErr(ReIOOperateResultBytes1.Content[3]);//DI4 if (QFSwitch2.CurSwtichErrInfo == SwitchErr.NG) { QFSwitch2.CurSwitchStateInfo = SwitchStateInfo.Err; } QFSwitch3.CurSwtichErrInfo = GetSwitchErr(ReIOOperateResultBytes1.Content[5]);//DI6 if (QFSwitch3.CurSwtichErrInfo == SwitchErr.NG) { QFSwitch3.CurSwitchStateInfo = SwitchStateInfo.Err; } QFSwitch4.CurSwtichErrInfo = GetSwitchErr(ReIOOperateResultBytes1.Content[7]);//DI8 if (QFSwitch4.CurSwtichErrInfo == SwitchErr.NG) { QFSwitch4.CurSwitchStateInfo = SwitchStateInfo.Err; } //通信正常 ReIOLinkState = true; } else { //通信失败 ReIOLinkState = false; } } catch (Exception ex) { LogService.Info($"时间:{DateTime.Now.ToString()}-【Meter】-{ex.Message}"); } //DiagnosticsTime.Stop(); //ScanRtTimeinfo = $"电表:{DiagnosticsTime.Elapsed.TotalMilliseconds.ToString()}"; } }); } /// /// 光伏设备的扫描 YuPuSolarTask /// private void SolarScanDeviceStart() { YuPuSolarTask = Task.Run(async () => { while (ThreadEnable) { await Task.Delay(400); //DiagnosticsTime.Reset(); //DiagnosticsTime.Start(); try { SolarOperateResultBytes1 = SolarMdDrive.Read($"s=2;x=4;5000", 35); if (SolarOperateResultBytes1.IsSuccess) { SolarRtPw1 = SolarMdDrive.ByteTransform.TransUInt16(SolarOperateResultBytes1.Content, 62) * 1.0 / 1000;// //通信正常 SolarLinkState = true; SolarLinkTrigTimeModel.UpdateByBool(true); } else { //通信失败 SolarLinkState = false; SolarLinkTrigTimeModel.UpdateByBool(false); } SolarOperateResultBytes2 = SolarMdDrive.Read($"s=3;x=4;5000", 35); if (SolarOperateResultBytes2.IsSuccess) { //跟手册不太一样 TransUInt32 SolarRtPw2 = SolarMdDrive.ByteTransform.TransUInt16(SolarOperateResultBytes2.Content, 62) * 1.0 / 1000;// //通信正常 SolarLinkState = true; SolarLinkTrigTimeModel.UpdateByBool(true); SolarTotalRt = SolarRtPw1 + SolarRtPw2; } else { //通信失败 SolarLinkState = false; SolarLinkTrigTimeModel.UpdateByBool(false); //同时 SolarTotalRt = 0; } SolarOperateResultBytes3 = SolarMdDrive.Read($"s=2;5000", 10); if (SolarOperateResultBytes3.IsSuccess) { if (SolarMdDrive.ByteTransform.TransUInt16(SolarOperateResultBytes3.Content, 12) == 0xCE) { SolarRtState1 = false; } else if (SolarMdDrive.ByteTransform.TransUInt16(SolarOperateResultBytes3.Content, 12) == 0xCF) { SolarRtState1 = true; } //通信正常 SolarLinkState = true; SolarLinkTrigTimeModel.UpdateByBool(true); } else { //通信失败 SolarLinkState = false; SolarLinkTrigTimeModel.UpdateByBool(false); } SolarOperateResultBytes4 = SolarMdDrive.Read($"s=3;5000", 10); if (SolarOperateResultBytes4.IsSuccess) { if (SolarMdDrive.ByteTransform.TransUInt16(SolarOperateResultBytes4.Content, 12) == 0xCE) { SolarRtState2 = false; } else if (SolarMdDrive.ByteTransform.TransUInt16(SolarOperateResultBytes4.Content, 12) == 0xCF) { SolarRtState2 = true; } //通信正常 SolarLinkState = true; SolarLinkTrigTimeModel.UpdateByBool(true); } else { //通信失败 SolarLinkState = false; SolarLinkTrigTimeModel.UpdateByBool(false); } } catch (Exception ex) { LogService.Info($"时间:{DateTime.Now.ToString()}-【Meter】-{ex.Message}"); } //DiagnosticsTime.Stop(); //ScanRtTimeinfo = $"电表:{DiagnosticsTime.Elapsed.TotalMilliseconds.ToString()}"; } }); } private double _EsSoc1; /// /// 储能1的SOC /// public double EsSoc1 { get { return _EsSoc1; } set { _EsSoc1 = value; RaisePropertyChanged(); } } private double _EsSoc2; /// /// 储能2的SOC /// public double EsSoc2 { get { return _EsSoc2; } set { _EsSoc2 = value; RaisePropertyChanged(); } } private double _EsSoe2; /// /// 储能2的SOE /// public double EsSoe2 { get { return _EsSoe2; } set { _EsSoe2 = value; RaisePropertyChanged(); } } private double _EsSoe1; /// /// 储能1的SOE /// public double EsSoe1 { get { return _EsSoe1; } set { _EsSoe1 = value; RaisePropertyChanged(); } } private double _EsSoh1; /// /// 储能1的SOH /// public double EsSoh1 { get { return _EsSoh1; } set { _EsSoh1 = value; RaisePropertyChanged(); } } private double _EsSoh2; /// /// 储能2的SOH /// public double EsSoh2 { get { return _EsSoh2; } set { _EsSoh2 = value; RaisePropertyChanged(); } } private double _SolarRtPw1; /// /// 光伏1 实时功率 /// 来自于光伏 /// public double SolarRtPw1 { get { return _SolarRtPw1; } set { _SolarRtPw1 = value; RaisePropertyChanged(); } } private double _SolarRtPw2; /// /// 光伏1 实时功率 /// 来自于光伏 /// public double SolarRtPw2 { get { return _SolarRtPw2; } set { _SolarRtPw2 = value; RaisePropertyChanged(); } } private bool _SolarRtState1; /// /// 光伏1的状态 /// 开关的状态 /// True 开 False 关 /// public bool SolarRtState1 { get { return _SolarRtState1; } set { _SolarRtState1 = value; RaisePropertyChanged(); } } private bool _SolarRtState2; /// /// 光伏2的状态 /// 开关的状态 /// True 开 False 关 /// public bool SolarRtState2 { get { return _SolarRtState2; } set { _SolarRtState2 = value; RaisePropertyChanged(); } } private double _SolarTotalPw; /// ///光伏总电能 ///两个仪表合并得到 /// public double SolarTotalPw { get { return _SolarTotalPw; } set { _SolarTotalPw = value; RaisePropertyChanged(); } } private double _SolarTotalRt; /// ///光伏的实时功率 ///两个仪表合并得到 /// public double SolarTotalRt { get { return _SolarTotalRt; } set { _SolarTotalRt = value; RaisePropertyChanged(); } } /// /// 关闭光伏 /// 两个光伏 /// /// public bool CloseSolar() { var result1 = SolarMdDrive.Write("s=2;5006", (ushort)0xCE); var result2 = SolarMdDrive.Write("s=3;5006", (ushort)0xCE); if (result1.IsSuccess && result2.IsSuccess) { return true; } return false; } /// /// 开启光伏 /// 两个光伏 /// /// public bool OpenSolar() { var result1 = SolarMdDrive.Write("s=2;5006", (ushort)0xCF); var result2 = SolarMdDrive.Write("s=3;5006", (ushort)0xCF); if (result1.IsSuccess && result2.IsSuccess) { return true; } return false; } /// /// 设置光伏输出百分比 /// 两个光伏 /// ratio是比值,1.1 0.5 0.1等 /// /// public bool SetSolarRatioValue(double ratio) { if (ratio > 1.1) { ratio = 1.1; } else if (ratio < 0) { ratio = 0; } //好像两个光伏板的功率不一样 short Value = (short)(ratio * 1000); var result1 = SolarMdDrive.Write("s=2;5008", Value); var result2 = SolarMdDrive.Write("s=3;5008", Value); if (result1.IsSuccess && result2.IsSuccess) { return true; } return false; } /// /// 获取光伏的运行状态 /// True 是开 /// False 是关 /// /// private bool CheckSolarState() { if (!SolarLinkState)//通信失败,则认为光伏关闭了 { return false; } if (SolarRtState1 && SolarRtState2) { return true; } return false; } /// /// 获取光伏的实时功率 /// 来自于光伏 /// /// public double GetTotalRtSolarPw() { if (SolarLinkState)//如果通信正常,则可以使用光伏的数据 { return SolarRtPw1 + SolarRtPw2; } //否则使用电表的数据 return Math.Abs(SolarEleMeter3.RtPw); } /// /// 获取状态值 /// /// /// private SwitchEm GetSwitch(bool value) { if (value) { return SwitchEm.On; } return SwitchEm.Off; } /// /// 获取Err信息 /// /// /// private SwitchErr GetSwitchErr(bool value) { if (value) { return SwitchErr.NG; } return SwitchErr.OK; } private double _ManageRealPw; /// /// 管理大楼真实的负载 /// public double ManageRealPw { get { return _ManageRealPw; } set { _ManageRealPw = value; RaisePropertyChanged(); } } private double _TaxRealPw; /// /// 税务大楼的真实的负载 /// public double TaxRealPw { get { return _TaxRealPw; } set { _TaxRealPw = value; RaisePropertyChanged(); } } /// /// 管理大楼 实际负载和目标值的差值 /// public double ManageDifLoad { get; set; } private string _CurESChargMsg = "Master"; /// /// 消息内容 /// public string CurESChargMsg { get { return _CurESChargMsg; } set { _CurESChargMsg = value; RaisePropertyChanged(); } } private ESChargInfo _CurESChargInfo; /// ///两个储能箱体选择切换的功能状态 ///两个储能箱在管理大楼和税务大楼切换 /// public ESChargInfo CurESChargInfo { get { return _CurESChargInfo; } set { if (_CurESChargInfo != value)//状态改变,进行切换 { //改变后进行开关控制操作 //SetControlModel(value); _CurESChargInfo = value; CurESChargMsg = value.ToString(); } _CurESChargInfo = value; } } /// /// 预先检查状态是否会改变 /// /// private bool PreCheckIsESChargInfoChange(ESChargInfo value) { if (value != _CurESChargInfo) { return true; } return false; } /// /// 是否改变模式 /// public bool IsESChargInfoChange { get; set; } = false; private double _TotalSolarPw; /// /// 光伏的总的输出功率 /// public double TotalSolarPw { get { return _TotalSolarPw; } set { _TotalSolarPw = value; RaisePropertyChanged(); } } /// /// 税务大楼根据真实负载介入的阈值 上限值 /// public double TaxTargetPwUp { get; set; } = 10; /// /// 税务大楼根据真实负载介入的阈值 中间值 /// public double TaxTargetPwMid { get; set; } = 10; /// /// 税务大楼根据真实负载介入的阈值 中间值 /// public double TaxTargetPw { get; set; } = 6; /// /// 税务大楼根据真实负载介入的阈值 下限值 /// public double TaxTargetPwDown { get; set; } = 18; /// /// 税务大楼根据真实负载是大还是小的判断值 /// public double TaxTargetPwBigSmallUp { get; set; } = 8; /// /// 税务大楼根据真实负载是大还是小的判断值 /// public double TaxTargetPwBigSmallDown { get; set; } = 15; /// /// 管理大楼根据真实负载介入的阈值 /// public double ManageTargetPw { get; set; } = 8; /// /// 逆流的阀值 /// public double TaxCurBackFlow { get; set; } /// /// 税务大楼 负载和目标值的差值 /// public double TaxDifLoad { get; set; } private string _MasterControlMsg; /// /// Master控制逻辑消息文本 /// public string MasterControlMsg { get { return _MasterControlMsg; } set { _MasterControlMsg = value; RaisePropertyChanged(); } } private string _SlaveControlMsg; /// /// Slave控制逻辑消息文本 /// public string SlaveControlMsg { get { return _SlaveControlMsg; } 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 /// private void LogicScanOld() { YuPuLogicScanTask = Task.Run(async () => { await Task.Delay(5000); //依据光伏发电为主要 while (ThreadEnable) { await Task.Delay(2000); try { if (MasterClient == null)//防止未加载导致错误 { continue; } ///////////////////////////获取当前的是白天还是夜晚,依据时间判断,留有余量,有可能实际已经晚上了,但是负载全部没有了 if (IsBetweenTime(DateTime.Now.ToString(), SunriseTimeStr, SunsetTimeStr)) { //根据日落和日出时间判断当前是白天 DayNight = DayNightEnum.Day; } else { DayNight = DayNightEnum.Night; //var SolarCur = SolarEleMeter3.RtPw + SolarEleMeter2.RtPw; //根据日落和日出时间判断当前是晚上 } if (!YuPuAutoHand) { continue; } switch (DayNight) { //白天 case DayNightEnum.Day: ///////////////////////////主从储能箱如何分配 依据光伏发电为主要,不浪费光伏发电为主线 //白天光伏发电:QF4断路器合闸(QF3分闸),主储能箱给管理大楼供电。QF1断路器合闸,光伏给从储能箱充电(QF2分闸) //若从储能箱满电后,QF2、QF3断路器合闸,QF1、QF4分闸,光伏给主储能箱充电,从储能箱给管理大楼供电。 ///////////////////////根据控制模式进行储能输出信息///////////////////////// if (CurESChargInfo == ESChargInfo.Master)//当前是主连接管理大楼作为放电使用,从作为充电吸收光伏 { ///////////////////Master主控制柜给管理大楼供电 //注意方向 //主储能箱体放电的控制 ManageRealPw = EsEleMeter4.AvePw - EsEleMeter1.AvePw; if (ManageRealPw > ManageTargetPw)//真实的负载大于一个值的时候才介入,或者控制关口电表的数据维持在一个水平,两个不同的控制方式 { //Master 主储能箱根据负载情况给管理大楼放电 //可以放电的话,则 if (MasterClient.IsCanDisCharg()) { MasterClient.ServerCmd.CmdType = "DisCharg"; ManageDifLoad = ManageRealPw - ManageTargetPw; if (ManageDifLoad < MasterClient.ClientInfo.MaxDisChargPw) { //储能发送功率 补全总功率数据 //DisChargPwCmd(AcrelMeters.Psum - EleMeterActionLimitValue.MidValue); MasterClient.ServerCmd.CmdPw = ManageDifLoad; MasterControlMsg = "【主储能】放电【管理大楼】:【主储能】放电补全目标差值,【主储能】正常放电"; } else //储能不能满足总的差值 { //满功率放电 //储能全功率放电,不足的部分电网自动补全 MasterClient.ServerCmd.CmdPw = (MasterClient.ClientInfo.MaxDisChargPw * ConfigDataService.energyStorageRunConfig.MaxBatDisChargRatio); MasterControlMsg = "【主储能】放电【管理大楼】:【主储能】全功率放电无法补全差值,【主储能】全功率放电"; } } else { //储能处于待机状态,防止其他的状态残留 MasterClient.ServerCmd.CmdType = "DisCharg";//功率为0,充放电的指令类型无所谓 MasterClient.ServerCmd.CmdPw = 0; MasterControlMsg = "【主储能】放电【管理大楼】:【主储能】无法放电,【主储能】不动作"; } } else { //真实的负载功率比较小,防止储能放电导致逆流,就不需要输出动作了,等真实的负载大于一个值后才开始介入 MasterClient.ServerCmd.CmdType = "DisCharg"; //功率为0,充放电的指令类型无所谓 MasterClient.ServerCmd.CmdPw = 0; MasterControlMsg = "【主储能】放电【管理大楼】:【管理大楼】负载较小,为防止放电导致逆流,【主储能】不动作"; } ///////////////从储能箱体充电 吸收光伏能量的控制 //注意方向 TaxRealPw = SolarEleMeter5.AvePw - SolarEleMeter3.AvePw;//光伏和市电供应给税务大楼的负载 TotalSolarPw = Math.Abs(SolarEleMeter2.AvePw + SolarEleMeter3.AvePw); //调节储能吸收光伏能量来调节光伏到税务大楼的能量,根据真实负载的值来判断是否调控介入 //税务真实的负载比较大的时候,储能参与调控吸收能量,真实的负载比较小的时候,则调动储能吸收全部光伏能量。 //这个点的判断跟其他的标准的不一样 if (TaxRealPw >= TaxTargetPwBigSmallUp)//TaxTargetPwBigSmall则是认为税务负载大和小的值 { //有负载,并且认为负载较大,则参与调控 ////////////////////调节使光伏尽可能满足税务大楼//////////////////// if (SlaveClient.IsCanCharg()) { //差值 这个差值是3号电表已经补充进去之后差值 TaxDifLoad = TaxRealPw - TaxTargetPw; //*************** 储能和光伏综合调节使3号电表给税务大楼供电达到这个差值 TaxDifLoad *************** if (TaxDifLoad < TotalSolarPw)//光伏的现在能力能达到这个差值,就是可以调节 { //差值 var difValue1 = TotalSolarPw - TaxDifLoad; // 储能吸收这个差值,剩余的TaxDifLoad 光伏值给税务大楼,以达到要求 SlaveClient.ServerCmd.CmdPw = difValue1; SlaveClient.ServerCmd.CmdType = "Charg"; SlaveControlMsg = "【从储能】充电吸收【光伏】电能:【从储能】充电满足差值1,【从储能】正常充电"; } else { //当前光伏的所有能量无法满足需要这个差值,那么全部光伏给税务大楼,储能不进行充电 SlaveClient.ServerCmd.CmdPw = 0; SlaveClient.ServerCmd.CmdType = "Charg"; SlaveControlMsg = "【从储能】充电吸收【光伏】电能:【从储能】不需要充电,把光伏都给税务大楼2,【从储能】不动作"; } } else { //从储能无法充电 SlaveClient.ServerCmd.CmdType = "Charg"; SlaveClient.ServerCmd.CmdPw = 0; SlaveControlMsg = "【从储能】充电吸收【光伏】电能:【从储能】无法充电3,【从储能】停止工作"; } } else//真实的负载比较小的时候,认为负载很小,则考虑调动储能吸收光伏能量。 { //观测3号电表的情况,维持3号电表的数据就相当于控制光伏输出到税务大楼的能量 //这样控制会比较直接,否则波动有点大 if (SlaveClient.IsCanCharg()) { if (TotalSolarPw < SlaveClient.ClientInfo!.MaxChargPw) { //光伏发电能力小于储能充电的能力,储能把光伏吸收完全 SlaveClient.ServerCmd.CmdType = "Charg"; SlaveClient.ServerCmd.CmdPw = TotalSolarPw; SlaveControlMsg = "【从储能】充电吸收【光伏】电能:【从储能】完全吸收光伏电能4,【从储能】正常充电"; } else { //光伏发电能力大于储能充电能力,则储能尽可能充电吧,存在逆流可能了,储能尽力了 【存在逆流的可能性】 SlaveClient.ServerCmd.CmdType = "Charg"; SlaveClient.ServerCmd.CmdPw = (SlaveClient.ClientInfo!.MaxChargPw * ConfigDataService.energyStorageRunConfig.MaxBatChargRatio); SlaveControlMsg = "【从储能】充电吸收【光伏】电能:税务大楼负载小5,【从储能】最大功率充电吸收光伏电能,存在【逆流】可能性"; } } else { //从储能无法充电 SlaveClient.ServerCmd.CmdType = "Charg"; SlaveClient.ServerCmd.CmdPw = 0; SlaveControlMsg = "【从储能】充电吸收【光伏】电能:【从储能】无法充电,【从储能】停止工作"; } } } else//当前是从储能箱作为放电,主储能箱作为充电吸收光伏 { ///////////////////Slave从控制柜给管理大楼供电 //注意方向 //从储能箱体放电的控制 ManageRealPw = EsEleMeter4.AvePw - EsEleMeter1.AvePw; if (ManageRealPw > ManageTargetPw)//真实的负载大于一个值的时候才介入,或者控制关口电表的数据维持在一个水平,两个不同的控制方式 { //Slave 从储能箱根据负载情况给管理大楼放电 //可以放电的话,则 if (SlaveClient.IsCanDisCharg()) { SlaveClient.ServerCmd.CmdType = "DisCharg"; ManageDifLoad = ManageRealPw - ManageTargetPw; if (ManageDifLoad < SlaveClient.ClientInfo!.MaxDisChargPw) { //储能发送功率 补全总功率数据 //DisChargPwCmd(AcrelMeters.Psum - EleMeterActionLimitValue.MidValue); SlaveClient.ServerCmd.CmdPw = ManageDifLoad; SlaveControlMsg = "【从储能】放电【管理大楼】:【从储能】放电补全目标差值,【从储能】正常放电"; } else //储能不能满足总的差值 { //满功率放电 //储能全功率放电,不足的部分电网自动补全 SlaveClient.ServerCmd.CmdPw = (SlaveClient.ClientInfo.MaxDisChargPw * ConfigDataService.energyStorageRunConfig.MaxBatDisChargRatio); SlaveControlMsg = "【从储能】放电【管理大楼】:【从储能】全功率放电无法补全差值,【从储能】全功率放电"; } } else { //储能处于待机状态,防止其他的状态残留 SlaveClient.ServerCmd.CmdType = "DisCharg";//功率为0,充放电的指令类型无所谓 SlaveClient.ServerCmd.CmdPw = 0; SlaveControlMsg = "【从储能】放电【管理大楼】:【从储能】无法放电,【从储能】不动作"; } } else { //真实的负载功率比较小,防止储能放电导致逆流,就不需要输出动作了,等真实的负载大于一个值后才开始介入 SlaveClient.ServerCmd.CmdType = "DisCharg"; //功率为0,充放电的指令类型无所谓 SlaveClient.ServerCmd.CmdPw = 0; SlaveControlMsg = "【从储能】放电【管理大楼】:【管理大楼】负载较小,为防止放电导致逆流,【从储能】不动作"; } ///////////////主储能箱体充电 吸收光伏能量的控制 //注意方向 TaxRealPw = SolarEleMeter5.AvePw - SolarEleMeter3.AvePw;//光伏和市电供应给税务大楼的负载 TotalSolarPw = Math.Abs(SolarEleMeter2.AvePw + SolarEleMeter3.AvePw); //调节储能吸收光伏能量来调节光伏到税务大楼的能量,根据真实负载的值来判断是否调控介入 if (TaxRealPw > TaxTargetPwBigSmallUp) { ////////////////////调节使光伏尽可能满足税务大楼//////////////////// if (MasterClient.IsCanCharg()) { //差值 这个差值是3号电表已经补充进去之后差值 TaxDifLoad = TaxRealPw - TaxTargetPw; //*************** 储能和光伏综合调节达到这个差值 TaxDifLoad *************** if (TaxDifLoad < TotalSolarPw)//光伏的现在能力能达到这个差值,就是可以调节 { //差值 var difValue1 = TotalSolarPw - TaxDifLoad; // 储能吸收这个差值,剩余的TaxDifLoad 光伏值给税务大楼,以达到要求 MasterClient.ServerCmd.CmdPw = difValue1; MasterClient.ServerCmd.CmdType = "Charg"; MasterControlMsg = "【主储能】充电吸收【光伏】电能:【主储能】充电满足差值,【主储能】正常充电"; } else { //当前光伏的所有能量无法满足需要这个差值,那么全部光伏给税务大楼,储能不进行充电 MasterClient.ServerCmd.CmdPw = 0; MasterClient.ServerCmd.CmdType = "Charg"; MasterControlMsg = "【主储能】充电吸收【光伏】电能:【主储能】不需要充电,把光伏都给税务大楼,【主储能】不动作"; } } else { MasterControlMsg = "【主储能】充电吸收【光伏】电能:【主储能】无法充电,【主储能】停止工作"; } } else { /////////////真实的负载小于目标值 //让主储能尽力充电吸走光伏的能力,为了防止逆流, if (MasterClient.IsCanCharg()) { if (TotalSolarPw < MasterClient.ClientInfo!.MaxChargPw) { //光伏发电能力小于储能充电的能力,储能把光伏吸收完全 MasterClient.ServerCmd.CmdType = "Charg"; MasterClient.ServerCmd.CmdPw = TotalSolarPw; MasterControlMsg = "【主储能】充电吸收【光伏】电能:【主储能】完全吸收光伏电能,【主储能】正常充电"; } else { //光伏发电能力大于储能充电能力,则储能尽可能充电吧,存在逆流可能了,储能尽力了 【存在逆流的可能性】 MasterClient.ServerCmd.CmdType = "Charg"; MasterClient.ServerCmd.CmdPw = (MasterClient.ClientInfo!.MaxChargPw * ConfigDataService.energyStorageRunConfig.MaxBatChargRatio); MasterControlMsg = "【主储能】充电吸收【光伏】电能:税务大楼负载小,【主储能】最大功率充电吸收光伏电能,存在【逆流】可能性!!!!!!!!!!!!!!!!!!"; } } else { MasterControlMsg = "【主储能】充电吸收【光伏】电能:【主储能】无法充电,【主储能】停止工作"; } } } //从储能箱接受电量来自于光伏 从储能箱的光伏充电功率,采用逐步的方法,主要是吸收光伏的电量,不可以吸收税务大楼的电量过来,怎么分配 break; //晚上 case DayNightEnum.Night: //进来这个模式时有用,走出Night_Master模式时靠白天的那里的逻辑判断,切换到白天时这里就不走到这里了 CurESChargInfo = ESChargInfo.Night_Master; //SetControlModel(ESChargInfo.Night_Master); //主储能箱充满为止 var masterNightTargetSoc = Math.Min(NightMaster_ToMasterFullSoc, ConfigDataService.energyStorageRunConfig.BMSSocUpSignLimitValue); if (MasterClient.ClientInfo!.SOC < masterNightTargetSoc) { //主储能箱充电 MasterClient.ServerCmd.CmdType = "Charg"; MasterClient.ServerCmd.CmdPw = MasterClient.ClientInfo!.MaxDisChargPw * ConfigDataService.energyStorageRunConfig.MaxBatDisChargRatio; } else { //主储能箱充电 MasterClient.ServerCmd.CmdType = "Charg"; MasterClient.ServerCmd.CmdPw = 0; } //从储能箱不运行 SlaveClient.ServerCmd.CmdType = "Charg"; SlaveClient.ServerCmd.CmdPw = 0; break; default: break; } } catch (Exception ex) { LogService.Error(String.Format("ErrSource : {0} ErrMsg : {1}", ex.StackTrace.ToString(), ex.Message.ToString())); } } }); } /// /// 逻辑扫描 /// 模式切换的模式更改的逻辑扫描 /// private void LogicScan() { YuPuLogicScanTask = Task.Run(async () => { await Task.Delay(5000); //依据光伏发电为主要 while (ThreadEnable) { await Task.Delay(2000); try { //实时把税务大楼的并网点的仪表数据给 SolarCurBackFlow.UpdateByCompareLess(SolarEleMeter5.RtPw); if (MasterClient == null)//防止未加载导致错误 { continue; } EsSoc1 = MasterClient.ClientInfo.SOC; EsSoe1 = MasterClient.ClientInfo.SOE; EsSoh1 = MasterClient.ClientInfo.SOH; EsSoc2 = SlaveClient.ClientInfo.SOC; EsSoe2 = SlaveClient.ClientInfo.SOE; EsSoh2 = SlaveClient.ClientInfo.SOH; if (!YuPuAutoHand) { continue; } //实时给当前的SOC数据给夜电白放的模型 //CurNightChargEleModel.MasterSoc = MasterClient.ClientInfo!.SOC; //CurNightChargEleModel.SlaveSoc = SlaveClient.ClientInfo!.SOC; ///////////////////////////主从储能箱如何分配 依据光伏发电为主要,不浪费光伏发电为主线 ///////////////////确认控制模式//////////////////////////////////////// //在白天的情况下,主储能箱和从储能储能箱切换的标准:以不浪费光伏为主 //依据当前的状态判断是否需要切换到其他状态 switch (CurESChargInfo) { case ESChargInfo.Master://当前是白天Master状态 ///////////////////////////获取当前的是白天还是夜晚,依据时间判断,留有余量,有可能实际已经晚上了,但是负载全部没有了 if (IsBetweenTime(DateTime.Now.ToString(), SunriseTimeStr, SunsetTimeStr)) { //根据日落和日出时间判断当前是白天 DayNight = DayNightEnum.Day; } else { //CurNightChargEleModel.SetDayEndState(); 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; } //?????? 如果昨晚充满了,今天的话,早上就要进入到无光伏的模式了,因为两个都满了 ,设置的数据不要太冲突 ???????? //从储能箱晚上充电的SOC NightSlave_ToSlaveFullSoc 90 > SolarToEsAsFullSoc 97 // //能到这里说明是白天,也要判断光伏把储能是否充满,充满的话进入无光伏阶段 新策略 是否可以切换 if (SlaveClient.ClientInfo!.SOC >= SolarToEsAsFullSoc) { //光伏把从储能充满,此时需要判断主储能是否满,满的话就是都满了进入无光伏阶段 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 <= Master_ToSlaveByMasterSoc) { //此时切换到从储能箱体需要查看从储能箱体的SOC if (SlaveClient.ClientInfo!.SOC >= Master_ToSlaveBySlaveSoc) { LogService.Info($"时间:{DateTime.Now.ToString()}-【动作】-当前是【Master】-主储能箱体放完SOC:{MasterClient.ClientInfo!.SOC},从储能箱体还有电SOC:{SlaveClient.ClientInfo!.SOC},切换到Slave"); //主储能箱体放完,从储能箱体还有电,则可以切换 //先进入等待状态,为切换准备条件 PreESChargInfo = ESChargInfo.Slave; SysRunStateMachine.Fire(ESChargInfoTrig.WaitTrig); continue; } else { LogicMsg = "主储能箱电放完,从储能箱还有电,暂不切换,维持当前状态"; //主储能箱体放完,从储能箱体也没有电,维持当前状态 } } else { //主储能箱维持当前的状态, LogicMsg = "主储能箱电没有放完,维持当前状态"; } ////原有的Master逻辑判断 ////判断从储能箱是否被光伏充满,以不浪费光伏为主 //if (SlaveClient.ClientInfo!.SOC >= 95) //{ // //从储能箱充满,那么具备切换的条件,还需要判断一下,主储能箱体是否被消耗掉 // if (MasterClient.ClientInfo!.SOC <= 80) // { // //从储能箱充满,主储能箱未满,则可以切换 // //先进入等待状态,为切换准备条件 // PreESChargInfo = ESChargInfo.Slave; // SysRunStateMachine.Fire(ESChargInfoTrig.WaitTrig); // } // else // { // //从储能箱充满,主储能箱并没有完全释放完毕,即是两个储能箱体都满了, // //从储能箱停止光伏充电,直到主储能箱释放能量后再切换 // //两个储能箱都充满了,无论出于什么原因,都不应该让光伏发电了 // //1)可能储能报错,无法放电,2)可能节假日,负载很小 // PreESChargInfo = ESChargInfo.NoSolar; // SysRunStateMachine.Fire(ESChargInfoTrig.WaitTrig); // } //} //else //{ // //以不浪费光伏为主 // //从储能箱光伏没有充满,维持当前的状态,可能主储能箱在放电中或者放电到SOC低后停止放电 //} break; case ESChargInfo.Slave: ///////////////////////////获取当前的是白天还是夜晚,依据时间判断,留有余量,有可能实际已经晚上了,但是负载全部没有了 if (IsBetweenTime(DateTime.Now.ToString(), SunriseTimeStr, SunsetTimeStr)) { //根据日落和日出时间判断当前是白天 DayNight = DayNightEnum.Day; } else { //CurNightChargEleModel.SetDayEndState(); 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 >= SolarToEsAsFullSoc) { //判断从储能箱放电程度 if (SlaveClient.ClientInfo!.SOC >= Slave_SolarToMasterEsFullBySlaverSoc) { //主从储能箱都充满了,无论出于什么原因,都不应该让光伏发电了,进入无光伏阶段 LogService.Info($"时间:{DateTime.Now.ToString()}-【动作】-当前是【Slave】-光伏把主储能充满SOC:{MasterClient.ClientInfo!.SOC},充满的话进入无光伏阶段 新策略"); 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 <= Slave_ToMasterBySlaveSoc) { //此时切换到从储能箱体需要查看从储能箱体的SOC if (MasterClient.ClientInfo!.SOC >= Slave_ToMasterByMasterSoc) { LogService.Info($"时间:{DateTime.Now.ToString()}-【动作】-当前是【Slave】-从储能箱体放完SOC:{SlaveClient.ClientInfo!.SOC},主储能箱体还有电SOC:{MasterClient.ClientInfo!.SOC},切换到Master"); //主储能箱体放完,从储能箱体还有电,则可以切换 //先进入等待状态,为切换准备条件 PreESChargInfo = ESChargInfo.Master; SysRunStateMachine.Fire(ESChargInfoTrig.WaitTrig); continue; } else { //主储能箱体放完,从储能箱体也没有电,维持当前状态 LogicMsg = "从储能箱电放完,主储能箱还有电,暂不切换,维持当前状态"; } } else { //主储能箱维持当前的状态 LogicMsg = "从储能箱电没有放完,维持当前状态"; } ////原有的Slave逻辑判断 ////判断主储能箱是否被光伏充满,以不浪费光伏为主 //if (MasterClient.ClientInfo!.SOC >= 95) //{ // //主储能箱充满,那么具备切换的条件,还需要判断一下,从储能箱体是否被消耗掉 // if (SlaveClient.ClientInfo!.SOC <= 80) // { // //主储能箱充满,从储能箱未满,则可以切换 // PreESChargInfo = ESChargInfo.Master; // SysRunStateMachine.Fire(ESChargInfoTrig.WaitTrig); // } // else // { // //主储能箱充满,从储能箱并没有完全释放完毕,即是两个储能箱体都满了, // //主储能箱停止光伏充电,直到从储能箱释放能量后再切换 // // 那么此时如果光伏发电很大,税务大楼负载很小,触发光伏并网柜的逆流保护,切换光伏并网柜的市电,光伏停止输出,主储能箱PCS无市电会报错(交流欠压)。 // //两个储能箱都充满了,无论出于什么原因,都不应该让光伏发电了 // //1)可能储能报错,无法放电,2)可能节假日,负载很小 // PreESChargInfo = ESChargInfo.NoSolar; // SysRunStateMachine.Fire(ESChargInfoTrig.WaitTrig); // } //} //else //{ // //主储能箱光伏没有充满,维持当前的状态 //} break; //为什么白天有Night_Master的判断,因为从晚上切换到白天时,需要由晚上作为旧的基础模型然后切换到Master或者Slaver //先判断白天晚上,然后再具体操作模式,从晚上到白天后,具体模式还是晚上,需要从晚上的模式到白天的Master或者Slaver模式 case ESChargInfo.Night_Master: ///////////////////////////获取当前的是白天还是夜晚,依据时间判断,留有余量,有可能实际已经晚上了,但是负载全部没有了 if (IsBetweenTime(DateTime.Now.ToString(), SunriseTimeStr, SunsetTimeStr)) { //根据日落和日出时间判断当前是白天 DayNight = DayNightEnum.Day; LogService.Info($"时间:{DateTime.Now.ToString()}-【动作】-当前是【Night_Master】-Night_Master->Master 晚上Night_Master切换到白天的Master"); //晚上转到白天,标记每天初始没有完成 //CurNightChargEleModel.MasterCheck = DischargInfo.NoComplete; //CurNightChargEleModel.SlaveCheck = DischargInfo.NoComplete; //CurNightChargEleModel.SetDayInitState(MasterClient.ClientInfo!.SOC, SlaveClient.ClientInfo!.SOC); //晚上切换到白天的话,直接到Master模式 PreESChargInfo = ESChargInfo.Master; SysRunStateMachine.Fire(ESChargInfoTrig.WaitTrig); continue; } else { //可能转到Night_Slave模式,本身充满,Night没有充满 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"); //切换到Night_Slave模式 PreESChargInfo = ESChargInfo.Night_Slave; SysRunStateMachine.Fire(ESChargInfoTrig.WaitTrig); continue; } else { //继续维持当前的状态 MasterControlMsg = "【主储能】夜晚充电"; SlaveControlMsg = "【从储能】夜晚待机"; LogicMsg = "【主储能】夜晚充电,【从储能】夜晚待机"; } } break; case ESChargInfo.Night_Slave: ///////////////////////////获取当前的是白天还是夜晚,依据时间判断,留有余量,有可能实际已经晚上了,但是负载全部没有了 if (IsBetweenTime(DateTime.Now.ToString(), SunriseTimeStr, SunsetTimeStr)) { LogService.Info($"时间:{DateTime.Now.ToString()}-【动作】-当前是【Night_Slave】-Night_Slave->Master 晚上Night_Slave切换到白天的Master"); //根据日落和日出时间判断当前是白天 DayNight = DayNightEnum.Day; //晚上转到白天,标记每天初始没有完成 //CurNightChargEleModel.MasterCheck = DischargInfo.NoComplete; //CurNightChargEleModel.SlaveCheck = DischargInfo.NoComplete; //CurNightChargEleModel.SetDayInitState(MasterClient.ClientInfo!.SOC, SlaveClient.ClientInfo!.SOC); //晚上切换到白天的话,直接到Master模式 PreESChargInfo = ESChargInfo.Master; SysRunStateMachine.Fire(ESChargInfoTrig.WaitTrig); } else { //可能转到Night_Master模式,本身充到SOC=60算充满 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模式 PreESChargInfo = ESChargInfo.Night_Master; SysRunStateMachine.Fire(ESChargInfoTrig.WaitTrig); } else { //继续维持当前的状态 MasterControlMsg = "【主储能】夜晚待机"; SlaveControlMsg = "【从储能】夜晚充电"; LogicMsg = "【主储能】夜晚待机,【从储能】夜晚充电"; } } break; case ESChargInfo.Wait: LogicMsg = "模式切换中"; //切换中 break; case ESChargInfo.NoSolar: //无光伏参与 //其中一个储能柜没有满就可以投入到Master/Slave中间 if (IsBetweenTime(DateTime.Now.ToString(), SunriseTimeStr, SunsetTimeStr)) { //根据日落和日出时间判断当前是白天 DayNight = DayNightEnum.Day; } else { //CurNightChargEleModel.SetDayEndState(); 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; } //用Soc小的吸收光伏能量 if (SlaveClient.ClientInfo!.SOC <= (SolarToEsAsFullSoc - 10)) { LogService.Info($"时间:{DateTime.Now.ToString()}-【动作】-当前是【NoSolar】-从储能箱体SOC:{SlaveClient.ClientInfo!.SOC},主储能箱体SOC:{MasterClient.ClientInfo!.SOC},切换到Master"); //主给管理大楼放电,从吸收光伏 PreESChargInfo = ESChargInfo.Master; SysRunStateMachine.Fire(ESChargInfoTrig.WaitTrig); continue; } if (MasterClient.ClientInfo!.SOC <= (SolarToEsAsFullSoc - 10)) { LogService.Info($"时间:{DateTime.Now.ToString()}-【动作】-当前是【NoSolar】-从储能箱体SOC:{SlaveClient.ClientInfo!.SOC},主储能箱体SOC:{MasterClient.ClientInfo!.SOC},切换到Slave"); //从给管理大楼放电,主吸收光伏 PreESChargInfo = ESChargInfo.Slave; SysRunStateMachine.Fire(ESChargInfoTrig.WaitTrig); continue; } break; default: break; } } catch (Exception ex) { LogService.Error(String.Format("ErrSource : {0} ErrMsg : {1}", ex.StackTrace.ToString(), ex.Message.ToString())); } } }); } /// /// 设置控制模式 /// 电操操作之后 要进行储能的状态检查,因为极短时间内储能是断电的 /// 12 34不能同时合闸,互锁的功能,切换开关时先断然后合闸,中间有时间差 ///因为有互锁的功能,在一组里面所以要先动作要关闭的电操,然后再操作打开的电操 /// public void SetControlModel(ESChargInfo value) { switch (value) { case ESChargInfo.Master: SetSwitchClose(); //主储能给管理大楼供电 QFSwitch3.SetRtSwitch(SwitchEm.Off); QFSwitch4.SetRtSwitch(SwitchEm.On); QFSwitch2.SetRtSwitch(SwitchEm.Off); QFSwitch1.SetRtSwitch(SwitchEm.On); break; case ESChargInfo.Slave: SetSwitchClose(); //从储能给管理大楼供电 QFSwitch4.SetRtSwitch(SwitchEm.Off); QFSwitch3.SetRtSwitch(SwitchEm.On); QFSwitch1.SetRtSwitch(SwitchEm.Off); QFSwitch2.SetRtSwitch(SwitchEm.On); break; case ESChargInfo.Night_Master: SetSwitchClose(); //晚上,给主储能箱供电 QFSwitch3.SetRtSwitch(SwitchEm.Off); QFSwitch2.SetRtSwitch(SwitchEm.Off); QFSwitch1.SetRtSwitch(SwitchEm.On); QFSwitch4.SetRtSwitch(SwitchEm.On); break; default: break; } } /// /// 设置模式 /// 无先关后设置的命令 /// /// public void SetControlModelNoBeforClose(ESChargInfo value) { switch (value) { case ESChargInfo.Master: //主储能给管理大楼供电 QFSwitch3.SetRtSwitch(SwitchEm.Off); QFSwitch4.SetRtSwitch(SwitchEm.On); QFSwitch2.SetRtSwitch(SwitchEm.Off); QFSwitch1.SetRtSwitch(SwitchEm.On); break; case ESChargInfo.Slave: //从储能给管理大楼供电 QFSwitch4.SetRtSwitch(SwitchEm.Off); QFSwitch3.SetRtSwitch(SwitchEm.On); QFSwitch1.SetRtSwitch(SwitchEm.Off); QFSwitch2.SetRtSwitch(SwitchEm.On); break; case ESChargInfo.Night_Master: //晚上,给主储能箱供电 QFSwitch3.SetRtSwitch(SwitchEm.Off); QFSwitch2.SetRtSwitch(SwitchEm.Off); QFSwitch1.SetRtSwitch(SwitchEm.On); QFSwitch4.SetRtSwitch(SwitchEm.On); break; case ESChargInfo.Night_Slave: //晚上,给从储能箱供电 QFSwitch4.SetRtSwitch(SwitchEm.Off); QFSwitch3.SetRtSwitch(SwitchEm.On); QFSwitch1.SetRtSwitch(SwitchEm.Off); QFSwitch2.SetRtSwitch(SwitchEm.On); break; case ESChargInfo.NoSolar: //主储能给管理大楼供电 QFSwitch3.SetRtSwitch(SwitchEm.Off); QFSwitch4.SetRtSwitch(SwitchEm.On); QFSwitch2.SetRtSwitch(SwitchEm.Off); QFSwitch1.SetRtSwitch(SwitchEm.On); break; default: break; } } /// /// 关闭所有的开关 /// /// public bool SetSwitchClose() { QFSwitch1.SetRtSwitch(SwitchEm.Off); QFSwitch2.SetRtSwitch(SwitchEm.Off); QFSwitch3.SetRtSwitch(SwitchEm.Off); QFSwitch4.SetRtSwitch(SwitchEm.Off); Thread.Sleep(2000); if (QFSwitch1.CurSwtichState == SwitchEm.Off && QFSwitch2.CurSwtichState == SwitchEm.Off && QFSwitch3.CurSwtichState == SwitchEm.Off && QFSwitch4.CurSwtichState == SwitchEm.Off ) { return true; } else { return false; } } /// /// 只关闭所有的电操 /// public void OnlyCloseSwitch() { QFSwitch1.SetRtSwitch(SwitchEm.Off); QFSwitch2.SetRtSwitch(SwitchEm.Off); QFSwitch3.SetRtSwitch(SwitchEm.Off); QFSwitch4.SetRtSwitch(SwitchEm.Off); } /// /// 检查电操的状态 /// /// public bool CheckSwitchState(ESChargInfo ESChargInfoData) { switch (ESChargInfoData) { case ESChargInfo.Inital: return true; case ESChargInfo.Master: // 1 4ON / 2 3OFF if (QFSwitch1.CurSwtichState == SwitchEm.On && QFSwitch2.CurSwtichState == SwitchEm.Off && QFSwitch3.CurSwtichState == SwitchEm.Off && QFSwitch4.CurSwtichState == SwitchEm.On ) { return true; } else { return false; } case ESChargInfo.Slave: // 1 4Off / 2 3ON if (QFSwitch1.CurSwtichState == SwitchEm.Off && QFSwitch2.CurSwtichState == SwitchEm.On && QFSwitch3.CurSwtichState == SwitchEm.On && QFSwitch4.CurSwtichState == SwitchEm.Off ) { return true; } else { return false; } case ESChargInfo.Night_Master: // 1 4ON / 2 3OFF if (QFSwitch1.CurSwtichState == SwitchEm.On && QFSwitch2.CurSwtichState == SwitchEm.Off && QFSwitch3.CurSwtichState == SwitchEm.Off && QFSwitch4.CurSwtichState == SwitchEm.On ) { return true; } else { return false; } case ESChargInfo.Night_Slave: // 1 4Off / 2 3ON if (QFSwitch1.CurSwtichState == SwitchEm.Off && QFSwitch2.CurSwtichState == SwitchEm.On && QFSwitch3.CurSwtichState == SwitchEm.On && QFSwitch4.CurSwtichState == SwitchEm.Off ) { return true; } else { return false; } case ESChargInfo.Wait: return true; case ESChargInfo.NoSolar: // 1 4ON / 2 3OFF if (QFSwitch1.CurSwtichState == SwitchEm.On && QFSwitch2.CurSwtichState == SwitchEm.Off && QFSwitch3.CurSwtichState == SwitchEm.Off && QFSwitch4.CurSwtichState == SwitchEm.On ) { return true; } else { return false; } default: return true; } } /// /// 检查PCS的状态 /// /// public bool CheckPCSState() { return true; } /// /// 是周五还是周六 /// 在晚上判断是周五晚上还是周六晚上,腾出储能给光伏充电用 /// /// private bool IsSpecialWeekDay() { return (DateTime.Now.DayOfWeek == DayOfWeek.Friday && IsPm()) || (DateTime.Now.DayOfWeek == DayOfWeek.Saturday && IsPm()); } /// /// 是周五还是周六 /// 在晚上判断是周五晚上还是周六晚上,腾出储能给光伏充电用 /// /// public bool IsInWeekendTime() { DateTime now = DateTime.Now; DayOfWeek currentDay = now.DayOfWeek; TimeSpan currentTime = now.TimeOfDay; // 周五情况:时间 >= 23:00 if (currentDay == DayOfWeek.Friday && currentTime.Hours >= 23) { return true; } // 周六情况:全天有效 else if (currentDay == DayOfWeek.Saturday) { return true; } // 周日情况:时间 < 07:00 else if (currentDay == DayOfWeek.Sunday && currentTime.Hours < 7) { return true; } return false; } /// /// 是否时下午 /// /// private bool IsPm() { return DateTime.Now.Hour >= 23; } /// /// 读取的字节数据集合 /// 1号电表 /// public OperateResult OperateResultBytes1 { get; set; } /// /// 读取的字节数据集合 /// 2号电表 /// public OperateResult OperateResultBytes2 { get; set; } /// /// 读取的字节数据集合 /// 3号电表 /// public OperateResult OperateResultBytes3 { get; set; } /// /// 读取的字节数据集合 /// 4号电表 /// public OperateResult OperateResultBytes4 { get; set; } /// /// 读取的字节数据集合 /// 5号电表 /// public OperateResult OperateResultBytes5 { get; set; } /// /// 读取的字节数据集合 /// public OperateResult ReIOOperateResultBytes1 { get; set; } /// /// 读取的字节数据集合 /// public OperateResult SolarOperateResultBytes1 { get; set; } /// /// 读取的字节数据集合 /// public OperateResult SolarOperateResultBytes2 { get; set; } /// /// 读取的字节数据集合 /// public OperateResult SolarOperateResultBytes3 { get; set; } /// /// 读取的字节数据集合 /// public OperateResult SolarOperateResultBytes4 { get; set; } /// /// ScanDevice扫描Task /// static Task ScanDeviceTask { get; set; } /// /// ScanDevice扫描Task /// static Task ReIOScanDeviceTask { get; set; } /// /// YuPuLogicScanTask扫描Task /// static Task YuPuLogicScanTask { get; set; } /// /// YuPuSolarTask扫描Task /// 光伏通信的任务 /// static Task YuPuSolarTask { get; set; } /// /// Meter 电表通信连接状态 /// public bool MeterLinkState { get; set; } = true; /// /// ReIO通信连接状态 /// public bool ReIOLinkState { get; set; } = true; /// /// Solar 电表通信连接状态 /// 这个状态很重要,很多状态判断使用 /// public bool SolarLinkState { get; set; } = true; /// /// 扫描线程使能 /// public bool ThreadEnable { get; set; } = true; /// /// 仪表数据驱动 /// public ModbusRtu MeterMdDrive { set; get; } /// /// 光伏数据驱动 /// public ModbusRtu SolarMdDrive { set; get; } /// /// 远程IO ModbusTcp驱动 /// private ModbusTcpNet ModbusTcpNetDrive { get; set; } = new ModbusTcpNet(); /// /// 日出 /// private string SunriseTimeStr { get; set; } = "5:00"; /// /// 日落 /// private string SunsetTimeStr { get; set; } = "23:00"; //public DayNightEnum DayNight { get; set; } private DayNightEnum _DayNight; /// /// 白天和晚上 /// Day /// Night /// public DayNightEnum DayNight { get { return _DayNight; } set { if (_DayNight != value) { LogService.Info($"时间:{DateTime.Now.ToString()}-【动作】-【DayNight】-【{_DayNight.ToString()}】->【{value.ToString()}】 白天和晚上的切换"); _DayNight = value; } } } /// /// 开关1 /// public SwitchModel QFSwitch1 { get; set; } /// /// 开关2 /// public SwitchModel QFSwitch2 { get; set; } /// /// 开关3 /// public SwitchModel QFSwitch3 { get; set; } /// /// 开关4 /// public SwitchModel QFSwitch4 { get; set; } /// /// 光伏并网柜 5号 /// 税务大楼的负载 /// 注意方向 /// public EleMeter SolarEleMeter5 { get; set; } = new EleMeter(); /// /// 光伏并网柜 2号 /// 并网到储能并网柜 /// 注意方向 /// public EleMeter SolarEleMeter2 { get; set; } = new EleMeter(); /// /// 光伏并网柜 3号 /// 输出到税务大楼 /// 注意方向 /// public EleMeter SolarEleMeter3 { get; set; } = new EleMeter(); /// /// 储能并网柜 1号 /// 输出到管理大楼 /// 注意方向 /// public EleMeter EsEleMeter1 { get; set; } = new EleMeter(); /// /// 储能并网柜 4号 /// 管理大楼的市电 /// 无论电量来自于主储能柜还是来自于从储能柜,都是输出或者输入到管理大楼的的能量 /// 注意方向 /// public EleMeter EsEleMeter4 { get; set; } = new EleMeter(); ///// ///// 光伏并网柜 5号 ///// 税务大楼的市电 ///// 注意方向 ///// //public EleMeter EsEleMeter5 { get; set; } = new EleMeter(); /// /// 日志服务 /// public ILogService LogService { get; } /// /// 配置服务信息 /// public ConfigDataService ConfigDataService { get; } public BmsDataService BmsDataService { get; } public FFService FFService { get; } public InPowerPCSDataService InPowerPCSDataService { get; } /// /// 判断时间是否在预定的时间区间内 /// /// /// /// /// public static bool IsBetweenTime(string timeStr, string startTime, string endTime) { //判断当前时间是否在工作时间段内 try { TimeSpan startSpan = DateTime.Parse(startTime).TimeOfDay; TimeSpan endSpan = DateTime.Parse(endTime).TimeOfDay; //string time1 = "2017-2-17 8:10:00"; DateTime t1 = Convert.ToDateTime(timeStr); TimeSpan dspNow = t1.TimeOfDay; if (dspNow > startSpan && dspNow < endSpan) { return true; } return false; } catch (Exception e) { return false; } } } /// /// 白天还是晚上 /// public enum DayNightEnum { Day = 1, Night = 2 } public enum ESChargInfo { /// /// 初始状态 /// Inital = 111, /// /// 主储能箱体放电给管理大楼 /// 从储能接受光伏的充电 /// Master = 1, /// /// 从储能箱体放电给管理大楼 /// 主储能接受光伏的充电 /// Slave = 2, /// /// 晚上 /// 主储能箱体从管理大楼充电 /// Night_Master = 3, /// /// 晚上 /// 从储能箱体从管理大楼充电 /// Night_Slave = 31, /// /// 等待 /// 各个状态切换时中间的等待状态 /// Wait = 4, /// /// 无光伏参与 /// 负载很小,光伏比较大,并且两个储能柜体已经充满时停止光伏的参与发电,让储能给负载放电 /// NoSolar = 5, } public enum ESChargInfoTrig { /// /// 初始状态 /// InitalTrig = 111, /// /// 主储能箱体放电给管理大楼 触发 /// 从储能接受光伏的充电 /// MasterTrig = 1, /// /// 从储能箱体放电给管理大楼 触发 /// 主储能接受光伏的充电 /// SlaveTrig = 2, /// /// 晚上 触发 /// 主储能箱体从管理大楼充电 /// Night_MasterTrig = 3, /// /// 晚上 触发 /// 从储能箱体从管理大楼充电 /// Night_SlaveTrig = 31, /// /// 等待 /// 各个状态切换时中间的等待状态 /// WaitTrig = 4, /// /// 无光伏参与 /// 负载很小,光伏比较大,并且两个储能柜体已经充满时停止光伏的参与发电,让储能给负载放电 /// NoSolarTrig = 5, } /// /// 手自动 /// public enum YuPuAutoHand { Hand = 0, Auto = 1 } }