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
}
}