4013 lines
186 KiB
C#
4013 lines
186 KiB
C#
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
|
||
{
|
||
/// <summary>
|
||
/// 月浦储能柜运行模型
|
||
/// </summary>
|
||
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<PeakValleyTimeEvent>().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;
|
||
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 削峰填谷的时间信息
|
||
/// </summary>
|
||
/// <param name="enum"></param>
|
||
/// <exception cref="NotImplementedException"></exception>
|
||
private void OnPeakValleyTimeEventMsgReceived(ElePVEnum data)
|
||
{
|
||
PeakValleySglModel = data;
|
||
}
|
||
|
||
private ElePVEnum _PeakValleySglModel = ElePVEnum.Peak;
|
||
/// <summary>
|
||
/// 削峰填谷的信号模型
|
||
/// 由其他的线程轮训赋值监控
|
||
/// </summary>
|
||
public ElePVEnum PeakValleySglModel
|
||
{
|
||
get { return _PeakValleySglModel; }
|
||
set
|
||
{
|
||
//if (_PeakValleySglModel != value)//监听线程不停的赋值,变化时触发执行
|
||
//{
|
||
_PeakValleySglModel = value;//削峰填谷不使用状态机
|
||
|
||
//}
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 当前的放电时间模型
|
||
/// </summary>
|
||
public DischargeTimeModel CurDischargeTimeModel { get; set; }
|
||
|
||
/// <summary>
|
||
/// 税务大楼光伏逆流触发模型
|
||
/// </summary>
|
||
public TrigTimeModel SolarCurBackFlow { get; set; }
|
||
|
||
/// <summary>
|
||
/// 事件聚合器
|
||
/// </summary>
|
||
private readonly IEventAggregator _eventAggregator;
|
||
|
||
/// <summary>
|
||
/// 光伏通信失败触发
|
||
/// </summary>
|
||
/// <param name="sender"></param>
|
||
/// <param name="e"></param>
|
||
/// <exception cref="NotImplementedException"></exception>
|
||
private void SolarLinkTrigTimeModel_TrigTimeOutHandler(object? sender, EventArgs e)
|
||
{
|
||
Console.WriteLine($"时间:{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}-【月浦】- 光伏通信失败 ");
|
||
}
|
||
|
||
/// <summary>
|
||
/// 税务大楼光伏通信失败触发模型
|
||
/// </summary>
|
||
public TrigTimeModel SolarLinkTrigTimeModel { get; set; }
|
||
|
||
/// <summary>
|
||
/// 税务大楼的光伏逆流触发事件
|
||
/// </summary>
|
||
/// <param name="sender"></param>
|
||
/// <param name="e"></param>
|
||
/// <exception cref="NotImplementedException"></exception>
|
||
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 系统运行状态机
|
||
|
||
/// <summary>
|
||
/// 系统运行的状态机
|
||
/// </summary>
|
||
public StateMachine<ESChargInfo, ESChargInfoTrig> SysRunStateMachine { get; set; }
|
||
|
||
///// <summary>
|
||
///// 夜间充电模型信息
|
||
///// 标记白天放电信息
|
||
///// </summary>
|
||
//public NightChargEleModel CurNightChargEleModel; /*= new NightChargEleModel();*/
|
||
|
||
/// <summary>
|
||
/// 系统运行的状态机
|
||
/// </summary>
|
||
private void StateMachineInitial()
|
||
{
|
||
//初始状态是这个状态,不然同一个状态会重复进入导致状态机报错
|
||
CurESChargInfo = ESChargInfo.Inital;
|
||
|
||
SysRunStateMachine = new StateMachine<ESChargInfo, ESChargInfoTrig>(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);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 进入 NoSolar
|
||
/// </summary>
|
||
/// <exception cref="NotImplementedException"></exception>
|
||
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);
|
||
|
||
}
|
||
|
||
/// <summary>
|
||
/// 推出 NoSolar
|
||
/// </summary>
|
||
/// <exception cref="NotImplementedException"></exception>
|
||
private void ExitNoSolar()
|
||
{
|
||
NoSolarTokenSource.Cancel();
|
||
|
||
LogService.Info($"时间:{DateTime.Now.ToString()}-【动作】-离开【EntryNoSolar】模式");
|
||
}
|
||
|
||
/// <summary>
|
||
/// 进入 Wait 等待状态
|
||
/// </summary>
|
||
/// <exception cref="NotImplementedException"></exception>
|
||
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);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 等待的光伏动作结果值
|
||
/// </summary>
|
||
private bool WaitSolarActionResult { get; set; } = false;
|
||
|
||
/// <summary>
|
||
/// 等待环境切换结果值 PCS 液冷
|
||
/// 先关掉PCS和液冷
|
||
/// 直接切换电操长时间会导致PCS和液冷报错。
|
||
/// </summary>
|
||
private bool WaitEnvActionResult { get; set; } = false;
|
||
|
||
/// <summary>
|
||
/// 等待的电操动作结果值
|
||
/// </summary>
|
||
private bool WaitEleSwitchActionResult { get; set; } = false;
|
||
|
||
/// <summary>
|
||
/// 退出 Wait 等待状态
|
||
/// </summary>
|
||
/// <exception cref="NotImplementedException"></exception>
|
||
private void ExitWait()
|
||
{
|
||
WaitTokenSource.Cancel();
|
||
|
||
//开启液冷
|
||
SlaveClient.ServerCmd.CoolOnOffCmd = true;
|
||
MasterClient.ServerCmd.CoolOnOffCmd = true;
|
||
|
||
LogService.Info($"时间:{DateTime.Now.ToString()}-【动作】-离开【EntryWait】模式");
|
||
}
|
||
|
||
|
||
|
||
/// <summary>
|
||
/// 进入 Night_Slave
|
||
/// </summary>
|
||
/// <exception cref="NotImplementedException"></exception>
|
||
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);
|
||
}
|
||
|
||
|
||
/// <summary>
|
||
/// 退出 Night_Slave
|
||
/// </summary>
|
||
/// <exception cref="NotImplementedException"></exception>
|
||
private void ExitNight_Slave()
|
||
{
|
||
Night_SlaveTokenSource.Cancel();
|
||
//退出Night_Slave状态,关闭输出
|
||
SlaveClient.ServerCmd.CmdPw = 0;
|
||
MasterClient.ServerCmd.CmdPw = 0;
|
||
|
||
LogService.Info($"时间:{DateTime.Now.ToString()}-【动作】-离开【EntryNight_Slave】模式");
|
||
}
|
||
|
||
|
||
|
||
/// <summary>
|
||
/// 进入 Night_Master状态
|
||
/// </summary>
|
||
/// <exception cref="NotImplementedException"></exception>
|
||
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);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 推出 Night_Master状态
|
||
/// </summary>
|
||
/// <exception cref="NotImplementedException"></exception>
|
||
private void ExitNight_Master()
|
||
{
|
||
Night_MasterTokenSource.Cancel();
|
||
//退出Night_Master状态,关闭输出
|
||
SlaveClient.ServerCmd.CmdPw = 0;
|
||
MasterClient.ServerCmd.CmdPw = 0;
|
||
|
||
LogService.Info($"时间:{DateTime.Now.ToString()}-【动作】-离开【EntryNight_Master】模式");
|
||
}
|
||
|
||
|
||
/// <summary>
|
||
/// 进入 Slave状态
|
||
/// </summary>
|
||
/// <exception cref="NotImplementedException"></exception>
|
||
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);
|
||
|
||
}
|
||
|
||
|
||
/// <summary>
|
||
/// 退出 Slave状态
|
||
/// </summary>
|
||
/// <exception cref="NotImplementedException"></exception>
|
||
private void ExitSlave()
|
||
{
|
||
SlaveTokenSource.Cancel();
|
||
//退出Slave状态,关闭输出
|
||
SlaveClient.ServerCmd.CmdPw = 0;
|
||
MasterClient.ServerCmd.CmdPw = 0;
|
||
|
||
LogService.Info($"时间:{DateTime.Now.ToString()}-【动作】-离开【EntrySlave】模式");
|
||
}
|
||
|
||
|
||
/// <summary>
|
||
/// 进入 Master状态
|
||
/// </summary>
|
||
/// <exception cref="NotImplementedException"></exception>
|
||
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);
|
||
|
||
}
|
||
|
||
/// <summary>
|
||
/// 退出 Master状态
|
||
/// </summary>
|
||
/// <exception cref="NotImplementedException"></exception>
|
||
private void ExitMaster()
|
||
{
|
||
MasterTokenSource.Cancel();
|
||
//退出Master状态,关闭输出
|
||
SlaveClient.ServerCmd.CmdPw = 0;
|
||
MasterClient.ServerCmd.CmdPw = 0;
|
||
|
||
LogService.Info($"时间:{DateTime.Now.ToString()}-【动作】-推出【EntryMaster】模式");
|
||
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取运行状态
|
||
/// </summary>
|
||
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;
|
||
}
|
||
|
||
}
|
||
|
||
/// <summary>
|
||
/// 手动运行模式切换
|
||
/// </summary>
|
||
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;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// Master的线程取消操作
|
||
/// </summary>
|
||
private CancellationTokenSource MasterTokenSource { get; set; }
|
||
|
||
/// <summary>
|
||
/// Slave的线程取消操作
|
||
/// </summary>
|
||
private CancellationTokenSource SlaveTokenSource { get; set; }
|
||
|
||
/// <summary>
|
||
/// Night_Master的线程取消操作
|
||
/// </summary>
|
||
private CancellationTokenSource Night_MasterTokenSource { get; set; }
|
||
|
||
/// <summary>
|
||
/// Night_Slave的线程取消操作
|
||
/// </summary>
|
||
private CancellationTokenSource Night_SlaveTokenSource { get; set; }
|
||
|
||
|
||
/// <summary>
|
||
/// Wait的线程取消操作
|
||
/// </summary>
|
||
private CancellationTokenSource WaitTokenSource { get; set; }
|
||
|
||
/// <summary>
|
||
/// NoSolar的线程取消操作
|
||
/// </summary>
|
||
private CancellationTokenSource NoSolarTokenSource { get; set; }
|
||
|
||
/// <summary>
|
||
/// 关闭系统:停止线程循环并断开所有通信连接
|
||
/// </summary>
|
||
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 { }
|
||
|
||
}
|
||
/// <summary>
|
||
/// 预先状态
|
||
/// 就是确定目标状态,先进入准备状态时这个预先的状态信息,等条件具备后再进行操作
|
||
/// </summary>
|
||
private ESChargInfo PreESChargInfo { get; set; }
|
||
|
||
#endregion
|
||
|
||
private bool _YuPuAutoHand = true;
|
||
/// <summary>
|
||
/// 手自动信息
|
||
/// 0未手动 false
|
||
/// 1为自动 true
|
||
/// </summary>
|
||
public bool YuPuAutoHand
|
||
{
|
||
get { return _YuPuAutoHand; }
|
||
set { _YuPuAutoHand = value; RaisePropertyChanged(); }
|
||
}
|
||
|
||
/// <summary>
|
||
/// 客户端集合
|
||
/// </summary>
|
||
public List<DistClient> ListDistClients { get; set; }
|
||
|
||
/// <summary>
|
||
/// 主客户端
|
||
/// </summary>
|
||
public DistClient MasterClient { get; set; }
|
||
|
||
/// <summary>
|
||
/// Slave客户端
|
||
/// </summary>
|
||
public DistClient SlaveClient { get; set; }
|
||
|
||
/// <summary>
|
||
/// 扫描线程
|
||
/// </summary>
|
||
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()}";
|
||
}
|
||
});
|
||
}
|
||
|
||
/// <summary>
|
||
/// 远程IO扫描线程
|
||
/// </summary>
|
||
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()}";
|
||
}
|
||
});
|
||
}
|
||
|
||
/// <summary>
|
||
/// 光伏设备的扫描 YuPuSolarTask
|
||
/// </summary>
|
||
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;
|
||
/// <summary>
|
||
/// 储能1的SOC
|
||
/// </summary>
|
||
public double EsSoc1
|
||
{
|
||
get { return _EsSoc1; }
|
||
set { _EsSoc1 = value; RaisePropertyChanged(); }
|
||
}
|
||
|
||
|
||
private double _EsSoc2;
|
||
/// <summary>
|
||
/// 储能2的SOC
|
||
/// </summary>
|
||
public double EsSoc2
|
||
{
|
||
get { return _EsSoc2; }
|
||
set { _EsSoc2 = value; RaisePropertyChanged(); }
|
||
}
|
||
|
||
|
||
private double _EsSoe2;
|
||
/// <summary>
|
||
/// 储能2的SOE
|
||
/// </summary>
|
||
public double EsSoe2
|
||
{
|
||
get { return _EsSoe2; }
|
||
set { _EsSoe2 = value; RaisePropertyChanged(); }
|
||
}
|
||
|
||
private double _EsSoe1;
|
||
/// <summary>
|
||
/// 储能1的SOE
|
||
/// </summary>
|
||
public double EsSoe1
|
||
{
|
||
get { return _EsSoe1; }
|
||
set { _EsSoe1 = value; RaisePropertyChanged(); }
|
||
}
|
||
|
||
private double _EsSoh1;
|
||
/// <summary>
|
||
/// 储能1的SOH
|
||
/// </summary>
|
||
public double EsSoh1
|
||
{
|
||
get { return _EsSoh1; }
|
||
set { _EsSoh1 = value; RaisePropertyChanged(); }
|
||
}
|
||
|
||
private double _EsSoh2;
|
||
/// <summary>
|
||
/// 储能2的SOH
|
||
/// </summary>
|
||
public double EsSoh2
|
||
{
|
||
get { return _EsSoh2; }
|
||
set { _EsSoh2 = value; RaisePropertyChanged(); }
|
||
}
|
||
|
||
|
||
private double _SolarRtPw1;
|
||
/// <summary>
|
||
/// 光伏1 实时功率
|
||
/// 来自于光伏
|
||
/// </summary>
|
||
public double SolarRtPw1
|
||
{
|
||
get { return _SolarRtPw1; }
|
||
set { _SolarRtPw1 = value; RaisePropertyChanged(); }
|
||
}
|
||
|
||
private double _SolarRtPw2;
|
||
/// <summary>
|
||
/// 光伏1 实时功率
|
||
/// 来自于光伏
|
||
/// </summary>
|
||
public double SolarRtPw2
|
||
{
|
||
get { return _SolarRtPw2; }
|
||
set { _SolarRtPw2 = value; RaisePropertyChanged(); }
|
||
}
|
||
|
||
private bool _SolarRtState1;
|
||
/// <summary>
|
||
/// 光伏1的状态
|
||
/// 开关的状态
|
||
/// True 开 False 关
|
||
/// </summary>
|
||
public bool SolarRtState1
|
||
{
|
||
get { return _SolarRtState1; }
|
||
set { _SolarRtState1 = value; RaisePropertyChanged(); }
|
||
}
|
||
|
||
private bool _SolarRtState2;
|
||
/// <summary>
|
||
/// 光伏2的状态
|
||
/// 开关的状态
|
||
/// True 开 False 关
|
||
/// </summary>
|
||
public bool SolarRtState2
|
||
{
|
||
get { return _SolarRtState2; }
|
||
set { _SolarRtState2 = value; RaisePropertyChanged(); }
|
||
}
|
||
|
||
private double _SolarTotalPw;
|
||
/// <summary>
|
||
///光伏总电能
|
||
///两个仪表合并得到
|
||
/// </summary>
|
||
public double SolarTotalPw
|
||
{
|
||
get { return _SolarTotalPw; }
|
||
set { _SolarTotalPw = value; RaisePropertyChanged(); }
|
||
}
|
||
|
||
private double _SolarTotalRt;
|
||
/// <summary>
|
||
///光伏的实时功率
|
||
///两个仪表合并得到
|
||
/// </summary>
|
||
public double SolarTotalRt
|
||
{
|
||
get { return _SolarTotalRt; }
|
||
set { _SolarTotalRt = value; RaisePropertyChanged(); }
|
||
|
||
}
|
||
/// <summary>
|
||
/// 关闭光伏
|
||
/// 两个光伏
|
||
/// </summary>
|
||
/// <returns></returns>
|
||
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;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 开启光伏
|
||
/// 两个光伏
|
||
/// </summary>
|
||
/// <returns></returns>
|
||
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;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 设置光伏输出百分比
|
||
/// 两个光伏
|
||
/// ratio是比值,1.1 0.5 0.1等
|
||
/// </summary>
|
||
/// <returns></returns>
|
||
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;
|
||
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取光伏的运行状态
|
||
/// True 是开
|
||
/// False 是关
|
||
/// </summary>
|
||
/// <returns></returns>
|
||
private bool CheckSolarState()
|
||
{
|
||
if (!SolarLinkState)//通信失败,则认为光伏关闭了
|
||
{
|
||
return false;
|
||
}
|
||
|
||
if (SolarRtState1 && SolarRtState2)
|
||
{
|
||
return true;
|
||
}
|
||
return false;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取光伏的实时功率
|
||
/// 来自于光伏
|
||
/// </summary>
|
||
/// <returns></returns>
|
||
public double GetTotalRtSolarPw()
|
||
{
|
||
if (SolarLinkState)//如果通信正常,则可以使用光伏的数据
|
||
{
|
||
return SolarRtPw1 + SolarRtPw2;
|
||
}
|
||
//否则使用电表的数据
|
||
return Math.Abs(SolarEleMeter3.RtPw);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取状态值
|
||
/// </summary>
|
||
/// <param name="value"></param>
|
||
/// <returns></returns>
|
||
private SwitchEm GetSwitch(bool value)
|
||
{
|
||
if (value)
|
||
{
|
||
return SwitchEm.On;
|
||
}
|
||
return SwitchEm.Off;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取Err信息
|
||
/// </summary>
|
||
/// <param name="value"></param>
|
||
/// <returns></returns>
|
||
private SwitchErr GetSwitchErr(bool value)
|
||
{
|
||
if (value)
|
||
{
|
||
return SwitchErr.NG;
|
||
}
|
||
return SwitchErr.OK;
|
||
}
|
||
|
||
private double _ManageRealPw;
|
||
/// <summary>
|
||
/// 管理大楼真实的负载
|
||
/// </summary>
|
||
public double ManageRealPw
|
||
{
|
||
get { return _ManageRealPw; }
|
||
set { _ManageRealPw = value; RaisePropertyChanged(); }
|
||
}
|
||
|
||
|
||
private double _TaxRealPw;
|
||
/// <summary>
|
||
/// 税务大楼的真实的负载
|
||
/// </summary>
|
||
public double TaxRealPw
|
||
{
|
||
get { return _TaxRealPw; }
|
||
set { _TaxRealPw = value; RaisePropertyChanged(); }
|
||
}
|
||
|
||
|
||
/// <summary>
|
||
/// 管理大楼 实际负载和目标值的差值
|
||
/// </summary>
|
||
public double ManageDifLoad { get; set; }
|
||
|
||
|
||
private string _CurESChargMsg = "Master";
|
||
/// <summary>
|
||
/// 消息内容
|
||
/// </summary>
|
||
public string CurESChargMsg
|
||
{
|
||
get { return _CurESChargMsg; }
|
||
set { _CurESChargMsg = value; RaisePropertyChanged(); }
|
||
}
|
||
|
||
|
||
|
||
private ESChargInfo _CurESChargInfo;
|
||
/// <summary>
|
||
///两个储能箱体选择切换的功能状态
|
||
///两个储能箱在管理大楼和税务大楼切换
|
||
/// </summary>
|
||
public ESChargInfo CurESChargInfo
|
||
{
|
||
get { return _CurESChargInfo; }
|
||
set
|
||
{
|
||
if (_CurESChargInfo != value)//状态改变,进行切换
|
||
{
|
||
//改变后进行开关控制操作
|
||
//SetControlModel(value);
|
||
_CurESChargInfo = value;
|
||
CurESChargMsg = value.ToString();
|
||
}
|
||
_CurESChargInfo = value;
|
||
}
|
||
}
|
||
|
||
|
||
|
||
/// <summary>
|
||
/// 预先检查状态是否会改变
|
||
/// </summary>
|
||
/// <returns></returns>
|
||
private bool PreCheckIsESChargInfoChange(ESChargInfo value)
|
||
{
|
||
if (value != _CurESChargInfo)
|
||
{
|
||
return true;
|
||
}
|
||
return false;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 是否改变模式
|
||
/// </summary>
|
||
public bool IsESChargInfoChange { get; set; } = false;
|
||
|
||
private double _TotalSolarPw;
|
||
/// <summary>
|
||
/// 光伏的总的输出功率
|
||
/// </summary>
|
||
public double TotalSolarPw
|
||
{
|
||
get { return _TotalSolarPw; }
|
||
set { _TotalSolarPw = value; RaisePropertyChanged(); }
|
||
}
|
||
|
||
/// <summary>
|
||
/// 税务大楼根据真实负载介入的阈值 上限值
|
||
/// </summary>
|
||
public double TaxTargetPwUp { get; set; } = 10;
|
||
|
||
/// <summary>
|
||
/// 税务大楼根据真实负载介入的阈值 中间值
|
||
/// </summary>
|
||
public double TaxTargetPwMid { get; set; } = 10;
|
||
|
||
/// <summary>
|
||
/// 税务大楼根据真实负载介入的阈值 中间值
|
||
/// </summary>
|
||
public double TaxTargetPw { get; set; } = 6;
|
||
|
||
/// <summary>
|
||
/// 税务大楼根据真实负载介入的阈值 下限值
|
||
/// </summary>
|
||
public double TaxTargetPwDown { get; set; } = 18;
|
||
|
||
/// <summary>
|
||
/// 税务大楼根据真实负载是大还是小的判断值
|
||
/// </summary>
|
||
public double TaxTargetPwBigSmallUp { get; set; } = 8;
|
||
|
||
/// <summary>
|
||
/// 税务大楼根据真实负载是大还是小的判断值
|
||
/// </summary>
|
||
public double TaxTargetPwBigSmallDown { get; set; } = 15;
|
||
|
||
/// <summary>
|
||
/// 管理大楼根据真实负载介入的阈值
|
||
/// </summary>
|
||
public double ManageTargetPw { get; set; } = 8;
|
||
|
||
|
||
/// <summary>
|
||
/// 逆流的阀值
|
||
/// </summary>
|
||
public double TaxCurBackFlow { get; set; }
|
||
|
||
/// <summary>
|
||
/// 税务大楼 负载和目标值的差值
|
||
/// </summary>
|
||
public double TaxDifLoad { get; set; }
|
||
|
||
|
||
private string _MasterControlMsg;
|
||
/// <summary>
|
||
/// Master控制逻辑消息文本
|
||
/// </summary>
|
||
public string MasterControlMsg
|
||
{
|
||
get { return _MasterControlMsg; }
|
||
set { _MasterControlMsg = value; RaisePropertyChanged(); }
|
||
}
|
||
|
||
|
||
private string _SlaveControlMsg;
|
||
/// <summary>
|
||
/// Slave控制逻辑消息文本
|
||
/// </summary>
|
||
public string SlaveControlMsg
|
||
{
|
||
get { return _SlaveControlMsg; }
|
||
set { _SlaveControlMsg = value; RaisePropertyChanged(); }
|
||
}
|
||
|
||
private double _SolarToEsAsFullSoc = 97;
|
||
/// <summary>
|
||
/// 光伏给储能充电 作为满的比值
|
||
/// 90-97
|
||
/// </summary>
|
||
public double SolarToEsAsFullSoc
|
||
{
|
||
get { return _SolarToEsAsFullSoc; }
|
||
set { _SolarToEsAsFullSoc = value; RaisePropertyChanged(); }
|
||
}
|
||
|
||
private double _Master_ToSlaveByMasterSoc = 5;
|
||
/// <summary>
|
||
/// Master模式,切换到Slave模式时MasterSOC的阀值
|
||
/// 两个同时考虑
|
||
/// </summary>
|
||
public double Master_ToSlaveByMasterSoc
|
||
{
|
||
get { return _Master_ToSlaveByMasterSoc; }
|
||
set { _Master_ToSlaveByMasterSoc = value; RaisePropertyChanged(); }
|
||
}
|
||
|
||
|
||
private double _Master_ToSlaveBySlaveSoc = 15;
|
||
/// <summary>
|
||
/// Master模式,切换到Slave模式时Slave SOC的阀值
|
||
/// 两个同时考虑
|
||
/// </summary>
|
||
public double Master_ToSlaveBySlaveSoc
|
||
{
|
||
get { return _Master_ToSlaveBySlaveSoc; }
|
||
set { _Master_ToSlaveBySlaveSoc = value; RaisePropertyChanged(); }
|
||
}
|
||
|
||
private double _Master_SolarToSlaveEsFullByMasterSoc = 95;
|
||
/// <summary>
|
||
/// Master模式,光伏给从储能充满了,是否切换到Slave模式,但是此时需要判断主储能MasterSOC是否满了(主储能也满的话,也无法接收光伏的电),否则不切换
|
||
/// 主要考虑尽可能的不浪费光伏的电,主从储能只要有余量就要接受光伏的电,也要防止频繁的切换
|
||
/// < SolarToEsAsFullSoc
|
||
/// </summary>
|
||
public double Master_SolarToSlaveEsFullByMasterSoc
|
||
{
|
||
get { return _Master_SolarToSlaveEsFullByMasterSoc; }
|
||
set { _Master_SolarToSlaveEsFullByMasterSoc = value; RaisePropertyChanged(); }
|
||
}
|
||
|
||
|
||
|
||
private double _Slave_ToMasterBySlaveSoc = 5;
|
||
/// <summary>
|
||
/// Slave模式,切换到Master模式时SOC的阀值
|
||
/// 两个同时考虑
|
||
/// </summary>
|
||
public double Slave_ToMasterBySlaveSoc
|
||
{
|
||
get { return _Slave_ToMasterBySlaveSoc; }
|
||
set { _Slave_ToMasterBySlaveSoc = value; RaisePropertyChanged(); }
|
||
}
|
||
|
||
|
||
private double _Slave_ToMasterByMasterSoc = 15;
|
||
/// <summary>
|
||
/// Slave模式,切换到Master模式时SOC的阀值
|
||
/// 两个同时考虑
|
||
/// </summary>
|
||
public double Slave_ToMasterByMasterSoc
|
||
{
|
||
get { return _Slave_ToMasterByMasterSoc; }
|
||
set { _Slave_ToMasterByMasterSoc = value; RaisePropertyChanged(); }
|
||
}
|
||
|
||
|
||
private double _Slave_SolarToMasterEsFullBySlaverSoc = 95;
|
||
/// <summary>
|
||
/// Slave模式,光伏给主储能充满了,是否切换到Master模式,但是此时需要判断从储能SOC是否满了(从储能也满的话,也无法接收光伏的电),否则不切换
|
||
/// 主要考虑尽可能的不浪费光伏的电,主从储能只要有余量就要接受光伏的电,也要防止频繁的切换
|
||
/// < SolarToEsAsFullSoc
|
||
/// </summary>
|
||
public double Slave_SolarToMasterEsFullBySlaverSoc
|
||
{
|
||
get { return _Slave_SolarToMasterEsFullBySlaverSoc; }
|
||
set { _Slave_SolarToMasterEsFullBySlaverSoc = value; RaisePropertyChanged(); }
|
||
}
|
||
|
||
private double _NightMaster_ToMasterFullSoc = 98;
|
||
/// <summary>
|
||
/// 晚上,主储能充满的标志,也是切换到从储能的控制标志
|
||
/// </summary>
|
||
public double NightMaster_ToMasterFullSoc
|
||
{
|
||
get { return _NightMaster_ToMasterFullSoc; }
|
||
set { _NightMaster_ToMasterFullSoc = value; RaisePropertyChanged(); }
|
||
}
|
||
|
||
|
||
private double _NightSlave_ToSlaveFullSoc = 98;
|
||
/// <summary>
|
||
/// 晚上,从储能充满的标志,也是切换到主储能的控制标志
|
||
/// </summary>
|
||
public double NightSlave_ToSlaveFullSoc
|
||
{
|
||
get { return _NightSlave_ToSlaveFullSoc; }
|
||
set { _NightSlave_ToSlaveFullSoc = value; RaisePropertyChanged(); }
|
||
}
|
||
|
||
|
||
|
||
private string _LogicMsg;
|
||
/// <summary>
|
||
/// 逻辑消息
|
||
/// </summary>
|
||
public string LogicMsg
|
||
{
|
||
get { return _LogicMsg; }
|
||
set
|
||
{
|
||
if (value != _LogicMsg)
|
||
{
|
||
RaisePropertyChanged();
|
||
_LogicMsg = value;
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
/// <summary>
|
||
/// 逻辑扫描1
|
||
/// </summary>
|
||
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()));
|
||
}
|
||
|
||
}
|
||
|
||
});
|
||
}
|
||
|
||
|
||
/// <summary>
|
||
/// 逻辑扫描
|
||
/// 模式切换的模式更改的逻辑扫描
|
||
/// </summary>
|
||
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()));
|
||
}
|
||
|
||
}
|
||
|
||
});
|
||
}
|
||
|
||
/// <summary>
|
||
/// 设置控制模式
|
||
/// 电操操作之后 要进行储能的状态检查,因为极短时间内储能是断电的
|
||
/// 12 34不能同时合闸,互锁的功能,切换开关时先断然后合闸,中间有时间差
|
||
///因为有互锁的功能,在一组里面所以要先动作要关闭的电操,然后再操作打开的电操
|
||
/// </summary>
|
||
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;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 设置模式
|
||
/// 无先关后设置的命令
|
||
/// </summary>
|
||
/// <param name="value"></param>
|
||
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;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 关闭所有的开关
|
||
/// </summary>
|
||
/// <returns></returns>
|
||
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;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 只关闭所有的电操
|
||
/// </summary>
|
||
public void OnlyCloseSwitch()
|
||
{
|
||
QFSwitch1.SetRtSwitch(SwitchEm.Off);
|
||
QFSwitch2.SetRtSwitch(SwitchEm.Off);
|
||
|
||
QFSwitch3.SetRtSwitch(SwitchEm.Off);
|
||
QFSwitch4.SetRtSwitch(SwitchEm.Off);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 检查电操的状态
|
||
/// </summary>
|
||
/// <returns></returns>
|
||
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;
|
||
}
|
||
}
|
||
|
||
|
||
/// <summary>
|
||
/// 检查PCS的状态
|
||
/// </summary>
|
||
/// <returns></returns>
|
||
public bool CheckPCSState()
|
||
{
|
||
return true;
|
||
}
|
||
|
||
|
||
/// <summary>
|
||
/// 是周五还是周六
|
||
/// 在晚上判断是周五晚上还是周六晚上,腾出储能给光伏充电用
|
||
/// </summary>
|
||
/// <returns></returns>
|
||
private bool IsSpecialWeekDay()
|
||
{
|
||
return (DateTime.Now.DayOfWeek == DayOfWeek.Friday && IsPm()) || (DateTime.Now.DayOfWeek == DayOfWeek.Saturday && IsPm());
|
||
}
|
||
|
||
/// <summary>
|
||
/// 是周五还是周六
|
||
/// 在晚上判断是周五晚上还是周六晚上,腾出储能给光伏充电用
|
||
/// </summary>
|
||
/// <returns></returns>
|
||
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;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 是否时下午
|
||
/// </summary>
|
||
/// <returns></returns>
|
||
private bool IsPm()
|
||
{
|
||
return DateTime.Now.Hour >= 23;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 读取的字节数据集合
|
||
/// 1号电表
|
||
/// </summary>
|
||
public OperateResult<byte[]> OperateResultBytes1 { get; set; }
|
||
|
||
/// <summary>
|
||
/// 读取的字节数据集合
|
||
/// 2号电表
|
||
/// </summary>
|
||
public OperateResult<byte[]> OperateResultBytes2 { get; set; }
|
||
|
||
/// <summary>
|
||
/// 读取的字节数据集合
|
||
/// 3号电表
|
||
/// </summary>
|
||
public OperateResult<byte[]> OperateResultBytes3 { get; set; }
|
||
|
||
/// <summary>
|
||
/// 读取的字节数据集合
|
||
/// 4号电表
|
||
/// </summary>
|
||
public OperateResult<byte[]> OperateResultBytes4 { get; set; }
|
||
|
||
/// <summary>
|
||
/// 读取的字节数据集合
|
||
/// 5号电表
|
||
/// </summary>
|
||
public OperateResult<byte[]> OperateResultBytes5 { get; set; }
|
||
|
||
|
||
/// <summary>
|
||
/// 读取的字节数据集合
|
||
/// </summary>
|
||
public OperateResult<bool[]> ReIOOperateResultBytes1 { get; set; }
|
||
|
||
/// <summary>
|
||
/// 读取的字节数据集合
|
||
/// </summary>
|
||
public OperateResult<byte[]> SolarOperateResultBytes1 { get; set; }
|
||
|
||
/// <summary>
|
||
/// 读取的字节数据集合
|
||
/// </summary>
|
||
public OperateResult<byte[]> SolarOperateResultBytes2 { get; set; }
|
||
|
||
/// <summary>
|
||
/// 读取的字节数据集合
|
||
/// </summary>
|
||
public OperateResult<byte[]> SolarOperateResultBytes3 { get; set; }
|
||
|
||
/// <summary>
|
||
/// 读取的字节数据集合
|
||
/// </summary>
|
||
public OperateResult<byte[]> SolarOperateResultBytes4 { get; set; }
|
||
|
||
/// <summary>
|
||
/// ScanDevice扫描Task
|
||
/// </summary>
|
||
static Task ScanDeviceTask { get; set; }
|
||
|
||
/// <summary>
|
||
/// ScanDevice扫描Task
|
||
/// </summary>
|
||
static Task ReIOScanDeviceTask { get; set; }
|
||
|
||
/// <summary>
|
||
/// YuPuLogicScanTask扫描Task
|
||
/// </summary>
|
||
static Task YuPuLogicScanTask { get; set; }
|
||
|
||
/// <summary>
|
||
/// YuPuSolarTask扫描Task
|
||
/// 光伏通信的任务
|
||
/// </summary>
|
||
static Task YuPuSolarTask { get; set; }
|
||
|
||
/// <summary>
|
||
/// Meter 电表通信连接状态
|
||
/// </summary>
|
||
public bool MeterLinkState { get; set; } = true;
|
||
|
||
/// <summary>
|
||
/// ReIO通信连接状态
|
||
/// </summary>
|
||
public bool ReIOLinkState { get; set; } = true;
|
||
|
||
/// <summary>
|
||
/// Solar 电表通信连接状态
|
||
/// 这个状态很重要,很多状态判断使用
|
||
/// </summary>
|
||
public bool SolarLinkState { get; set; } = true;
|
||
|
||
/// <summary>
|
||
/// 扫描线程使能
|
||
/// </summary>
|
||
public bool ThreadEnable { get; set; } = true;
|
||
|
||
/// <summary>
|
||
/// 仪表数据驱动
|
||
/// </summary>
|
||
public ModbusRtu MeterMdDrive { set; get; }
|
||
|
||
/// <summary>
|
||
/// 光伏数据驱动
|
||
/// </summary>
|
||
public ModbusRtu SolarMdDrive { set; get; }
|
||
|
||
/// <summary>
|
||
/// 远程IO ModbusTcp驱动
|
||
/// </summary>
|
||
private ModbusTcpNet ModbusTcpNetDrive { get; set; } = new ModbusTcpNet();
|
||
|
||
/// <summary>
|
||
/// 日出
|
||
/// </summary>
|
||
private string SunriseTimeStr { get; set; } = "5:00";
|
||
|
||
/// <summary>
|
||
/// 日落
|
||
/// </summary>
|
||
private string SunsetTimeStr { get; set; } = "23:00";
|
||
|
||
|
||
//public DayNightEnum DayNight { get; set; }
|
||
|
||
private DayNightEnum _DayNight;
|
||
/// <summary>
|
||
/// 白天和晚上
|
||
/// Day
|
||
/// Night
|
||
/// </summary>
|
||
public DayNightEnum DayNight
|
||
{
|
||
get { return _DayNight; }
|
||
set
|
||
{
|
||
if (_DayNight != value)
|
||
{
|
||
LogService.Info($"时间:{DateTime.Now.ToString()}-【动作】-【DayNight】-【{_DayNight.ToString()}】->【{value.ToString()}】 白天和晚上的切换");
|
||
_DayNight = value;
|
||
}
|
||
|
||
}
|
||
}
|
||
|
||
|
||
/// <summary>
|
||
/// 开关1
|
||
/// </summary>
|
||
public SwitchModel QFSwitch1 { get; set; }
|
||
|
||
/// <summary>
|
||
/// 开关2
|
||
/// </summary>
|
||
public SwitchModel QFSwitch2 { get; set; }
|
||
|
||
/// <summary>
|
||
/// 开关3
|
||
/// </summary>
|
||
public SwitchModel QFSwitch3 { get; set; }
|
||
|
||
/// <summary>
|
||
/// 开关4
|
||
/// </summary>
|
||
public SwitchModel QFSwitch4 { get; set; }
|
||
|
||
/// <summary>
|
||
/// 光伏并网柜 5号
|
||
/// 税务大楼的负载
|
||
/// 注意方向
|
||
/// </summary>
|
||
public EleMeter SolarEleMeter5 { get; set; } = new EleMeter();
|
||
|
||
/// <summary>
|
||
/// 光伏并网柜 2号
|
||
/// 并网到储能并网柜
|
||
/// 注意方向
|
||
/// </summary>
|
||
public EleMeter SolarEleMeter2 { get; set; } = new EleMeter();
|
||
|
||
/// <summary>
|
||
/// 光伏并网柜 3号
|
||
/// 输出到税务大楼
|
||
/// 注意方向
|
||
/// </summary>
|
||
public EleMeter SolarEleMeter3 { get; set; } = new EleMeter();
|
||
|
||
/// <summary>
|
||
/// 储能并网柜 1号
|
||
/// 输出到管理大楼
|
||
/// 注意方向
|
||
/// </summary>
|
||
public EleMeter EsEleMeter1 { get; set; } = new EleMeter();
|
||
|
||
/// <summary>
|
||
/// 储能并网柜 4号
|
||
/// 管理大楼的市电
|
||
/// 无论电量来自于主储能柜还是来自于从储能柜,都是输出或者输入到管理大楼的的能量
|
||
/// 注意方向
|
||
/// </summary>
|
||
public EleMeter EsEleMeter4 { get; set; } = new EleMeter();
|
||
|
||
///// <summary>
|
||
///// 光伏并网柜 5号
|
||
///// 税务大楼的市电
|
||
///// 注意方向
|
||
///// </summary>
|
||
//public EleMeter EsEleMeter5 { get; set; } = new EleMeter();
|
||
|
||
/// <summary>
|
||
/// 日志服务
|
||
/// </summary>
|
||
public ILogService LogService { get; }
|
||
|
||
/// <summary>
|
||
/// 配置服务信息
|
||
/// </summary>
|
||
public ConfigDataService ConfigDataService { get; }
|
||
public BmsDataService BmsDataService { get; }
|
||
public FFService FFService { get; }
|
||
public InPowerPCSDataService InPowerPCSDataService { get; }
|
||
|
||
|
||
/// <summary>
|
||
/// 判断时间是否在预定的时间区间内
|
||
/// </summary>
|
||
/// <param name="timeStr"></param>
|
||
/// <param name="startTime"></param>
|
||
/// <param name="endTime"></param>
|
||
/// <returns></returns>
|
||
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;
|
||
}
|
||
|
||
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 白天还是晚上
|
||
/// </summary>
|
||
public enum DayNightEnum
|
||
{
|
||
Day = 1,
|
||
Night = 2
|
||
}
|
||
|
||
public enum ESChargInfo
|
||
{
|
||
/// <summary>
|
||
/// 初始状态
|
||
/// </summary>
|
||
Inital = 111,
|
||
|
||
/// <summary>
|
||
/// 主储能箱体放电给管理大楼
|
||
/// 从储能接受光伏的充电
|
||
/// </summary>
|
||
Master = 1,
|
||
|
||
/// <summary>
|
||
/// 从储能箱体放电给管理大楼
|
||
/// 主储能接受光伏的充电
|
||
/// </summary>
|
||
Slave = 2,
|
||
|
||
/// <summary>
|
||
/// 晚上
|
||
/// 主储能箱体从管理大楼充电
|
||
/// </summary>
|
||
Night_Master = 3,
|
||
|
||
/// <summary>
|
||
/// 晚上
|
||
/// 从储能箱体从管理大楼充电
|
||
/// </summary>
|
||
Night_Slave = 31,
|
||
|
||
/// <summary>
|
||
/// 等待
|
||
/// 各个状态切换时中间的等待状态
|
||
/// </summary>
|
||
Wait = 4,
|
||
|
||
/// <summary>
|
||
/// 无光伏参与
|
||
/// 负载很小,光伏比较大,并且两个储能柜体已经充满时停止光伏的参与发电,让储能给负载放电
|
||
/// </summary>
|
||
NoSolar = 5,
|
||
}
|
||
|
||
|
||
public enum ESChargInfoTrig
|
||
{
|
||
/// <summary>
|
||
/// 初始状态
|
||
/// </summary>
|
||
InitalTrig = 111,
|
||
|
||
/// <summary>
|
||
/// 主储能箱体放电给管理大楼 触发
|
||
/// 从储能接受光伏的充电
|
||
/// </summary>
|
||
MasterTrig = 1,
|
||
|
||
/// <summary>
|
||
/// 从储能箱体放电给管理大楼 触发
|
||
/// 主储能接受光伏的充电
|
||
/// </summary>
|
||
SlaveTrig = 2,
|
||
|
||
/// <summary>
|
||
/// 晚上 触发
|
||
/// 主储能箱体从管理大楼充电
|
||
/// </summary>
|
||
Night_MasterTrig = 3,
|
||
|
||
/// <summary>
|
||
/// 晚上 触发
|
||
/// 从储能箱体从管理大楼充电
|
||
/// </summary>
|
||
Night_SlaveTrig = 31,
|
||
|
||
/// <summary>
|
||
/// 等待
|
||
/// 各个状态切换时中间的等待状态
|
||
/// </summary>
|
||
WaitTrig = 4,
|
||
|
||
/// <summary>
|
||
/// 无光伏参与
|
||
/// 负载很小,光伏比较大,并且两个储能柜体已经充满时停止光伏的参与发电,让储能给负载放电
|
||
/// </summary>
|
||
NoSolarTrig = 5,
|
||
}
|
||
|
||
/// <summary>
|
||
/// 手自动
|
||
/// </summary>
|
||
public enum YuPuAutoHand
|
||
{
|
||
Hand = 0,
|
||
Auto = 1
|
||
}
|
||
}
|