更改初版

This commit is contained in:
2025-12-25 11:13:13 +08:00
parent 51e4d5add4
commit 7c001e6396
15 changed files with 1450 additions and 100 deletions

View File

@@ -417,6 +417,7 @@ namespace OrpaonEMS.App.Services
/// </summary>
public void CloseDrive()
{
ThreadEnable = false;
ModbusTcpNetDrive.ConnectClose();
}

View File

@@ -11,8 +11,10 @@ using Prism.Mvvm;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;
using System.Windows.Documents;
@@ -68,6 +70,9 @@ namespace OrpaonEMS.App.Services
ListEnergyStoragePeakValleyTimeConfig = FreeSql.Select<PeakValleyConfig>().ToList();
//削峰填谷模式的时间监视线程
var ListenPeakValleyTimeTaskInfo = Task.Run(() => ListenPeakValleyTimeCycle());
//加载控制参数配置
LoadControlConfigValue();
}
@@ -427,5 +432,421 @@ namespace OrpaonEMS.App.Services
#endregion
#region
/// <summary>
/// 控制参数配置文件路径
/// </summary>
private readonly string _controlConfigFilePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Config", "ControlConfigValue.json");
/// <summary>
/// 控制参数配置值
/// </summary>
public ControlConfigValue ControlConfigValue { get; set; }
/// <summary>
/// 加载控制参数配置
/// </summary>
/// <returns>返回加载结果true表示成功false表示失败</returns>
public bool LoadControlConfigValue()
{
try
{
// 确保目录存在
string directory = Path.GetDirectoryName(_controlConfigFilePath);
if (!Directory.Exists(directory))
{
Directory.CreateDirectory(directory);
LogService.Info($"创建配置目录: {directory}");
}
// 检查文件是否存在
if (File.Exists(_controlConfigFilePath))
{
// 读取JSON文件内容
string jsonContent = File.ReadAllText(_controlConfigFilePath, Encoding.UTF8);
// 检查文件是否为空或只包含空对象
if (string.IsNullOrWhiteSpace(jsonContent) || jsonContent.Trim() == "{}" || jsonContent.Trim() == "{\r\n \r\n}")
{
// 文件为空,创建默认配置
LogService.Info("控制参数配置文件为空,创建默认配置");
ControlConfigValue = new ControlConfigValue();
SaveControlConfigValue();
}
else
{
// 反序列化JSON到对象
var options = new JsonSerializerOptions
{
PropertyNameCaseInsensitive = true,
WriteIndented = true,
Encoder = System.Text.Encodings.Web.JavaScriptEncoder.UnsafeRelaxedJsonEscaping
};
ControlConfigValue = JsonSerializer.Deserialize<ControlConfigValue>(jsonContent, options);
// 验证反序列化结果
if (ControlConfigValue == null)
{
LogService.Warn("控制参数配置反序列化失败,使用默认配置");
ControlConfigValue = new ControlConfigValue();
SaveControlConfigValue();
}
else
{
LogService.Info("成功加载控制参数配置");
// 兼容键名末尾带空格的 SelectedDisChargeModel
try
{
using (var doc = JsonDocument.Parse(jsonContent))
{
var root = doc.RootElement;
if (root.TryGetProperty("SelectedDisChargeModel ", out var selectedProp))
{
if (selectedProp.ValueKind == JsonValueKind.Number)
{
ControlConfigValue.SelectedDisChargeModel = selectedProp.GetInt32();
}
else if (selectedProp.ValueKind == JsonValueKind.String && int.TryParse(selectedProp.GetString(), out var selInt))
{
ControlConfigValue.SelectedDisChargeModel = selInt;
}
}
}
}
catch (Exception ex)
{
LogService.Warn($"SelectedDisChargeModel(带空格) 兼容解析失败: {ex.Message}");
}
}
}
}
else
{
// 文件不存在,创建默认配置
LogService.Info($"控制参数配置文件不存在,创建默认配置: {_controlConfigFilePath}");
ControlConfigValue = new ControlConfigValue();
SaveControlConfigValue();
}
// 兼容旧配置:确保 DisChargeModel 非空,并在为空列表时注入默认项
if (ControlConfigValue != null)
{
if (ControlConfigValue.DisChargeModel == null)
{
ControlConfigValue.DisChargeModel = new List<DisChargeModelItem>();
}
if (ControlConfigValue.DisChargeModel.Count == 0)
{
ControlConfigValue.DisChargeModel.Add(new DisChargeModelItem { Model = 1, DisChargeTime = "08:00" });
ControlConfigValue.DisChargeModel.Add(new DisChargeModelItem { Model = 2, DisChargeTime = "11:00" });
if (ControlConfigValue.SelectedDisChargeModel <= 0)
{
ControlConfigValue.SelectedDisChargeModel = 1;
}
// 保存注入的默认配置
SaveControlConfigValue();
}
}
return true;
}
catch (JsonException jsonEx)
{
// JSON解析错误
LogService.Error($"控制参数配置JSON解析错误: {jsonEx.Message}");
LogService.Error($"异常堆栈: {jsonEx.StackTrace}");
// 使用默认配置
ControlConfigValue = new ControlConfigValue();
// 尝试备份损坏的文件
try
{
if (File.Exists(_controlConfigFilePath))
{
string backupPath = $"{_controlConfigFilePath}.backup_{DateTime.Now:yyyyMMddHHmmss}";
File.Copy(_controlConfigFilePath, backupPath, true);
LogService.Info($"已备份损坏的配置文件到: {backupPath}");
}
}
catch (Exception backupEx)
{
LogService.Error($"备份损坏的配置文件失败: {backupEx.Message}");
}
// 保存默认配置
SaveControlConfigValue();
return false;
}
catch (Exception ex)
{
// 其他异常
LogService.Error($"加载控制参数配置时发生异常: {ex.Message}");
LogService.Error($"异常类型: {ex.GetType().Name}");
LogService.Error($"异常堆栈: {ex.StackTrace}");
// 使用默认配置
ControlConfigValue = new ControlConfigValue();
return false;
}
}
/// <summary>
/// 保存控制参数配置
/// </summary>
/// <returns>返回保存结果true表示成功false表示失败</returns>
public bool SaveControlConfigValue()
{
try
{
// 验证配置对象
if (ControlConfigValue == null)
{
LogService.Error("控制参数配置对象为空,无法保存");
return false;
}
// 确保目录存在
string directory = Path.GetDirectoryName(_controlConfigFilePath);
if (!Directory.Exists(directory))
{
Directory.CreateDirectory(directory);
LogService.Info($"创建配置目录: {directory}");
}
// 序列化对象到JSON
var options = new JsonSerializerOptions
{
WriteIndented = true,
Encoder = System.Text.Encodings.Web.JavaScriptEncoder.UnsafeRelaxedJsonEscaping
};
string jsonContent = JsonSerializer.Serialize(ControlConfigValue, options);
// 写入文件前先写入临时文件
string tempFilePath = $"{_controlConfigFilePath}.tmp";
File.WriteAllText(tempFilePath, jsonContent, Encoding.UTF8);
// 验证临时文件是否写入成功
if (File.Exists(tempFilePath))
{
// 备份原文件(如果存在)
if (File.Exists(_controlConfigFilePath))
{
string backupPath = $"{_controlConfigFilePath}.bak";
File.Copy(_controlConfigFilePath, backupPath, true);
}
// 用临时文件替换原文件
File.Copy(tempFilePath, _controlConfigFilePath, true);
File.Delete(tempFilePath);
LogService.Info($"成功保存控制参数配置到: {_controlConfigFilePath}");
return true;
}
else
{
LogService.Error("临时配置文件写入失败");
return false;
}
}
catch (IOException ioEx)
{
// 文件IO异常
LogService.Error($"保存控制参数配置时发生IO异常: {ioEx.Message}");
LogService.Error($"异常堆栈: {ioEx.StackTrace}");
return false;
}
catch (JsonException jsonEx)
{
// JSON序列化异常
LogService.Error($"控制参数配置JSON序列化错误: {jsonEx.Message}");
LogService.Error($"异常堆栈: {jsonEx.StackTrace}");
return false;
}
catch (Exception ex)
{
// 其他异常
LogService.Error($"保存控制参数配置时发生异常: {ex.Message}");
LogService.Error($"异常类型: {ex.GetType().Name}");
LogService.Error($"异常堆栈: {ex.StackTrace}");
return false;
}
}
/// <summary>
/// 更新控制参数配置的值并保存
/// </summary>
/// <param name="configValue">要更新的控制参数配置对象</param>
/// <returns>返回更新结果true表示成功false表示失败</returns>
public bool UpdateControlConfigValue(ControlConfigValue configValue)
{
try
{
if (configValue == null)
{
LogService.Error("传入的控制参数配置对象为空");
return false;
}
// 更新配置值
ControlConfigValue.SolarToEsAsFullSoc = configValue.SolarToEsAsFullSoc;
ControlConfigValue.Master_ToSlaveByMasterSoc = configValue.Master_ToSlaveByMasterSoc;
ControlConfigValue.Master_ToSlaveBySlaveSoc = configValue.Master_ToSlaveBySlaveSoc;
ControlConfigValue.Master_SolarToSlaveEsFullByMasterSoc = configValue.Master_SolarToSlaveEsFullByMasterSoc;
ControlConfigValue.Slave_ToMasterBySlaveSoc = configValue.Slave_ToMasterBySlaveSoc;
ControlConfigValue.Slave_ToMasterByMasterSoc = configValue.Slave_ToMasterByMasterSoc;
ControlConfigValue.Slave_SolarToMasterEsFullBySlaverSoc = configValue.Slave_SolarToMasterEsFullBySlaverSoc;
ControlConfigValue.NightMaster_ToMasterFullSoc = configValue.NightMaster_ToMasterFullSoc;
ControlConfigValue.NightSlave_ToSlaveFullSoc = configValue.NightSlave_ToSlaveFullSoc;
// 保存到文件
bool saveResult = SaveControlConfigValue();
if (saveResult)
{
LogService.Info("成功更新并保存控制参数配置");
}
else
{
LogService.Error("更新控制参数配置后保存失败");
}
return saveResult;
}
catch (Exception ex)
{
LogService.Error($"更新控制参数配置时发生异常: {ex.Message}");
LogService.Error($"异常堆栈: {ex.StackTrace}");
return false;
}
}
/// <summary>
/// 获取放电时间配置集合
/// </summary>
/// <returns>放电时间配置列表(永不返回 null</returns>
public List<DisChargeModelItem> GetDisChargeModel()
{
if (ControlConfigValue == null)
{
ControlConfigValue = new ControlConfigValue();
}
if (ControlConfigValue.DisChargeModel == null)
{
ControlConfigValue.DisChargeModel = new List<DisChargeModelItem>();
}
return ControlConfigValue.DisChargeModel;
}
/// <summary>
/// 保存放电时间配置集合
/// </summary>
/// <param name="items">放电时间配置列表</param>
/// <returns>保存结果</returns>
public bool SaveDisChargeModel(List<DisChargeModelItem> items)
{
if (ControlConfigValue == null)
{
ControlConfigValue = new ControlConfigValue();
}
// 基础校验
var list = items ?? new List<DisChargeModelItem>();
foreach (var it in list)
{
if (it == null)
{
LogService.Error("DisChargeModel 中存在空项");
return false;
}
if (string.IsNullOrWhiteSpace(it.DisChargeTime))
{
LogService.Error("DisChargeModel.DisChargeTime 为空");
return false;
}
if (!TimeSpan.TryParse(it.DisChargeTime, out _))
{
LogService.Error($"DisChargeModel.DisChargeTime 格式非法: {it.DisChargeTime},期望 HH:mm");
return false;
}
}
ControlConfigValue.DisChargeModel = list;
return SaveControlConfigValue();
}
/// <summary>
/// 时间上是否可以放电
/// </summary>
/// <returns></returns>
public bool IsTimeCanDischarge()
{
try
{
// 确保配置加载
if (ControlConfigValue == null)
{
LoadControlConfigValue();
}
// 获取放电时间集合(内部已保证非 null
var list = GetDisChargeModel();
// 选中模式 Id缺省=1
int selectedId = 1;
try
{
selectedId = 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];
}
}
// 解析时间HH:mm失败回退 08:00
TimeSpan target;
if (selected != null && !string.IsNullOrWhiteSpace(selected.DisChargeTime) && TimeSpan.TryParse(selected.DisChargeTime, out target))
{
return DateTime.Now.TimeOfDay >= target;
}
else
{
LogService.Warn("IsTimeCanDischarge: 配置时间缺失或格式非法,使用默认 08:00 进行比较");
target = new TimeSpan(8, 0, 0);
return DateTime.Now.TimeOfDay >= target;
}
}
catch (Exception ex)
{
LogService.Error($"IsTimeCanDischarge 执行失败: {ex.Message}");
return false;
}
}
#endregion
}
}

View File

@@ -166,6 +166,16 @@ namespace OrpaonEMS.App.Services
set { _LinkStateMsg = value; RaisePropertyChanged(); }
}
/// <summary>
/// 关闭连接
/// </summary>
public void CloseDrive()
{
ThreadEnable=false;
if (ModbusRtuDrive!=null) ModbusRtuDrive!.Close();
}
#region

View File

@@ -91,7 +91,7 @@ namespace OrpaonEMS.App.Services
MessageBox.Show("PCS 连接失败");
}
PwTranceCmdValues = new TranceCmdValue(0.7);
PwTranceCmdValues = new TranceCmdValue(2);
PwTranceCmdValues.CmdValueChanged += PwTranceCmdValues_CmdValueChanged;
//CurPcsAlarmModel = new PcsAlarmModel();
@@ -768,6 +768,7 @@ namespace OrpaonEMS.App.Services
public void CloseModbusRtu()
{
ThreadEnable = false;
CurTimer.Stop();
ModbusTcpNetDrive.ConnectClose();
}

View File

@@ -65,6 +65,7 @@ namespace OrpaonEMS.App.Services
MenuItems.Add(new NavigationItem("FireExtinguisher", "消防", "FFView"));
MenuItems.Add(new NavigationItem("CogRefreshOutline", "配置", "SysConfigView"));
MenuItems.Add(new NavigationItem("RefreshAuto", "手自动", "AutoHandView"));
MenuItems.Add(new NavigationItem("ControlConfigView", "控制设置", "ControlConfigView"));
}
}
}

View File

@@ -26,10 +26,13 @@ namespace OrpaonEMS.App.Services
{
///////
/////怎么认定为晚上,光伏发电功率低于一个阈值时可认为是晚上
public YuePuRunModelService(ILogService logService, ConfigDataService configDataService,
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);
@@ -131,6 +134,64 @@ namespace OrpaonEMS.App.Services
//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();
@@ -169,6 +230,11 @@ namespace OrpaonEMS.App.Services
}
}
/// <summary>
/// 当前的放电时间模型
/// </summary>
public DischargeTimeModel CurDischargeTimeModel { get; set; }
/// <summary>
/// 税务大楼光伏逆流触发模型
/// </summary>
@@ -911,7 +977,8 @@ namespace OrpaonEMS.App.Services
//循环执行方法
//在峰的时候放电
if (PeakValleySglModel == ElePVEnum.TopPeak || PeakValleySglModel == ElePVEnum.Peak)
//if (PeakValleySglModel == ElePVEnum.TopPeak || PeakValleySglModel == ElePVEnum.Peak)
if (ConfigDataService.IsTimeCanDischarge())
{
///////////////////Slave从控制柜给管理大楼供电
//注意方向
@@ -1118,9 +1185,9 @@ namespace OrpaonEMS.App.Services
//循环执行方法
//在峰的时候放电
if (PeakValleySglModel == ElePVEnum.TopPeak || PeakValleySglModel == ElePVEnum.Peak)
//if (PeakValleySglModel == ElePVEnum.TopPeak || PeakValleySglModel == ElePVEnum.Peak)
if (ConfigDataService.IsTimeCanDischarge())
{
///////////////////Master主控制柜给管理大楼供电
//注意方向
//主储能箱体放电的控制
@@ -1251,7 +1318,6 @@ namespace OrpaonEMS.App.Services
SlaveControlMsg = "【从储能】充电吸收【光伏】电能税务大楼负载小5【从储能】最大功率充电吸收光伏电能存在【逆流】可能性";
}
}
else
{
@@ -1261,11 +1327,7 @@ namespace OrpaonEMS.App.Services
SlaveControlMsg = "【从储能】充电吸收【光伏】电能:【从储能】无法充电,【从储能】停止工作";
}
}
}
catch (Exception ex)
{
@@ -1497,6 +1559,33 @@ namespace OrpaonEMS.App.Services
/// </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>
/// 预先状态
/// 就是确定目标状态,先进入准备状态时这个预先的状态信息,等条件具备后再进行操作
@@ -3173,7 +3262,7 @@ namespace OrpaonEMS.App.Services
//用Soc小的吸收光伏能量
if (SlaveClient.ClientInfo!.SOC <= (SolarToEsAsFullSoc-10))
if (SlaveClient.ClientInfo!.SOC <= (SolarToEsAsFullSoc - 10))
{
LogService.Info($"时间:{DateTime.Now.ToString()}-【动作】-当前是【NoSolar】-从储能箱体SOC:{SlaveClient.ClientInfo!.SOC}主储能箱体SOC:{MasterClient.ClientInfo!.SOC}切换到Master");
@@ -3711,6 +3800,9 @@ namespace OrpaonEMS.App.Services
/// 配置服务信息
/// </summary>
public ConfigDataService ConfigDataService { get; }
public BmsDataService BmsDataService { get; }
public FFService FFService { get; }
public InPowerPCSDataService InPowerPCSDataService { get; }
/// <summary>