From 7c001e63967ab74e0078ea1fa786fc90cfa39e6c Mon Sep 17 00:00:00 2001 From: Tyrone CT Date: Thu, 25 Dec 2025 11:13:13 +0800 Subject: [PATCH] =?UTF-8?q?=E6=9B=B4=E6=94=B9=E5=88=9D=E7=89=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- OrpaonEMS.App/App.xaml.cs | 7 +- OrpaonEMS.App/CANDrive/CAN.cs | 23 +- OrpaonEMS.App/Config/ControlConfigValue.json | 24 + OrpaonEMS.App/Models/ControlConfigValue.cs | 145 ++++++ OrpaonEMS.App/Models/DisChargeModelItem.cs | 10 + OrpaonEMS.App/Models/DischargeTimeModel.cs | 80 ++++ OrpaonEMS.App/Services/BmsDataService.cs | 1 + OrpaonEMS.App/Services/ConfigDataService.cs | 421 ++++++++++++++++++ OrpaonEMS.App/Services/FFService.cs | 10 + .../Services/InPowerPCSDataService.cs | 3 +- .../Services/NavigationMenuService.cs | 1 + .../Services/YuePuRunModelService.cs | 112 ++++- .../ViewModels/ControlConfigViewModel.cs | 347 +++++++++++++++ OrpaonEMS.App/Views/AutoHandView.xaml | 4 + OrpaonEMS.App/Views/ControlConfigView.xaml | 362 +++++++++++---- 15 files changed, 1450 insertions(+), 100 deletions(-) create mode 100644 OrpaonEMS.App/Config/ControlConfigValue.json create mode 100644 OrpaonEMS.App/Models/ControlConfigValue.cs create mode 100644 OrpaonEMS.App/Models/DisChargeModelItem.cs create mode 100644 OrpaonEMS.App/Models/DischargeTimeModel.cs create mode 100644 OrpaonEMS.App/ViewModels/ControlConfigViewModel.cs diff --git a/OrpaonEMS.App/App.xaml.cs b/OrpaonEMS.App/App.xaml.cs index fcfccc7..26d9d5d 100644 --- a/OrpaonEMS.App/App.xaml.cs +++ b/OrpaonEMS.App/App.xaml.cs @@ -1,6 +1,7 @@ using Example; using FreeSql; using OrpaonEMS.App.Services; +using OrpaonEMS.App.CANDrive; using OrpaonEMS.App.ViewModels; using OrpaonEMS.App.Views; using OrpaonEMS.Core; @@ -115,7 +116,8 @@ namespace OrpaonEMS.App services.RegisterForNavigation(); services.RegisterForNavigation(); services.RegisterForNavigation(); - + services.RegisterForNavigation(); + services.RegisterForNavigation(); services.RegisterForNavigation(); services.RegisterForNavigation(); @@ -193,6 +195,9 @@ namespace OrpaonEMS.App ContainerLocator.Container.Resolve().WebSocketThreadEnable = false; ContainerLocator.Container.Resolve().ThreadEnable = false; + // 确保停止 CAN 接收线程,避免前台线程阻塞进程退出 + CAN.Stop(); + energyStorageService.EnergyStorageStateMachine.Fire(Core.Enums.EnergyStorageStateTrig.HandTrig); base.OnExit(e); } diff --git a/OrpaonEMS.App/CANDrive/CAN.cs b/OrpaonEMS.App/CANDrive/CAN.cs index bf6e0da..443e424 100644 --- a/OrpaonEMS.App/CANDrive/CAN.cs +++ b/OrpaonEMS.App/CANDrive/CAN.cs @@ -274,6 +274,21 @@ namespace OrpaonEMS.App.CANDrive Console.WriteLine("\nEMUC reveice start ...\n"); } + /// + /// 停止接收线程并关闭设备 + /// + public static void Stop() + { + try { Global.StopRequested = true; } catch { } + try { + if (Global.TRDRecv != null && Global.TRDRecv.IsAlive) + { + Global.TRDRecv.Join(500); + } + } catch { } + try { if (Global.com_port >= 0) EMUCCloseDevice(Global.com_port); } catch { } + } + public void StartTest() { int i; @@ -550,7 +565,7 @@ namespace OrpaonEMS.App.CANDrive int i; int rtn; - while (true) + while (!Global.StopRequested) { rtn = EMUCReceive(Global.com_port, ref Global.frame_recv); @@ -677,6 +692,10 @@ namespace OrpaonEMS.App.CANDrive public static uint recv_cnt1 = 0; public static uint recv_cnt2 = 0; /// + /// 停止标志 + /// + public static volatile bool StopRequested = false; + /// /// 版本信息 /// public static VER_INFO ver_info = new VER_INFO(); @@ -688,7 +707,7 @@ namespace OrpaonEMS.App.CANDrive public static CAN_FRAME_INFO frame_send = new CAN_FRAME_INFO(); public static CAN_FRAME_INFO frame_recv = new CAN_FRAME_INFO(); - public static Thread TRDRecv = new Thread(TRDRecvFx); + public static Thread TRDRecv = new Thread(TRDRecvFx) { IsBackground = true }; } diff --git a/OrpaonEMS.App/Config/ControlConfigValue.json b/OrpaonEMS.App/Config/ControlConfigValue.json new file mode 100644 index 0000000..e1134fc --- /dev/null +++ b/OrpaonEMS.App/Config/ControlConfigValue.json @@ -0,0 +1,24 @@ +{ + "SolarToEsAsFullSoc": 97, + "Master_ToSlaveByMasterSoc" : 5, + "Master_ToSlaveBySlaveSoc": 15, + "Master_SolarToSlaveEsFullByMasterSoc": 95, + "Slave_ToMasterBySlaveSoc": 5, + "Slave_ToMasterByMasterSoc": 15, + "Slave_SolarToMasterEsFullBySlaverSoc": 95, + "NightMaster_ToMasterFullSoc": 98, + "NightSlave_ToSlaveFullSoc": 80, + + "DisChargeModel": [ + { + "Model": 1, + "DisChargeTime": "08:00" + }, + { + "Model": 2, + "DisChargeTime": "11:00" + } + ], + + "SelectedDisChargeModel ": 1 +} \ No newline at end of file diff --git a/OrpaonEMS.App/Models/ControlConfigValue.cs b/OrpaonEMS.App/Models/ControlConfigValue.cs new file mode 100644 index 0000000..8421db2 --- /dev/null +++ b/OrpaonEMS.App/Models/ControlConfigValue.cs @@ -0,0 +1,145 @@ +using Prism.Mvvm; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace OrpaonEMS.App.Models +{ + /// + /// 控制参数配置值 + /// + public class ControlConfigValue : BindableBase + { + /// + /// 实例化函数 + /// + public ControlConfigValue() + { + SolarToEsAsFullSoc = 97; + Master_ToSlaveByMasterSoc = 5; + Master_ToSlaveBySlaveSoc = 15; + Master_SolarToSlaveEsFullByMasterSoc = 95; + Slave_ToMasterBySlaveSoc = 5; + Slave_ToMasterByMasterSoc = 15; + Slave_SolarToMasterEsFullBySlaverSoc = 95; + NightMaster_ToMasterFullSoc = 98; + NightSlave_ToSlaveFullSoc = 98; + DisChargeModel = new List(); + SelectedDisChargeModel = 1; + } + + private double _SolarToEsAsFullSoc; + /// + /// 光伏给储能充电 作为满的比值 + /// 90-97 + /// + public double SolarToEsAsFullSoc + { + get { return _SolarToEsAsFullSoc; } + set { _SolarToEsAsFullSoc = value; RaisePropertyChanged(); } + } + + private double _Master_ToSlaveByMasterSoc; + /// + /// Master模式,切换到Slave模式时MasterSOC的阈值 + /// + public double Master_ToSlaveByMasterSoc + { + get { return _Master_ToSlaveByMasterSoc; } + set { _Master_ToSlaveByMasterSoc = value; RaisePropertyChanged(); } + } + + private double _Master_ToSlaveBySlaveSoc; + /// + /// Master模式,切换到Slave模式时Slave SOC的阀值 + /// 两个同时考虑 + /// + public double Master_ToSlaveBySlaveSoc + { + get { return _Master_ToSlaveBySlaveSoc; } + set { _Master_ToSlaveBySlaveSoc = value; RaisePropertyChanged(); } + } + + private double _Master_SolarToSlaveEsFullByMasterSoc; + /// + /// Master模式,光伏给从储能充满了,是否切换到Slave模式,但是此时需要判断主储能MasterSOC是否满了(主储能也满的话,也无法接收光伏的电),否则不切换 + /// 主要考虑尽可能的不浪费光伏的电,主从储能只要有余量就要接受光伏的电,也要防止频繁的切换 + /// < SolarToEsAsFullSoc + /// + public double Master_SolarToSlaveEsFullByMasterSoc + { + get { return _Master_SolarToSlaveEsFullByMasterSoc; } + set { _Master_SolarToSlaveEsFullByMasterSoc = value; RaisePropertyChanged(); } + } + + private double _Slave_ToMasterBySlaveSoc; + /// + /// Slave模式,切换到Master模式时SOC的阀值 + /// 两个同时考虑 + /// + public double Slave_ToMasterBySlaveSoc + { + get { return _Slave_ToMasterBySlaveSoc; } + set { _Slave_ToMasterBySlaveSoc = value; RaisePropertyChanged(); } + } + + private double _Slave_ToMasterByMasterSoc; + /// + /// Slave模式,切换到Master模式时SOC的阀值 + /// 两个同时考虑 + /// + public double Slave_ToMasterByMasterSoc + { + get { return _Slave_ToMasterByMasterSoc; } + set { _Slave_ToMasterByMasterSoc = value; RaisePropertyChanged(); } + } + + private double _Slave_SolarToMasterEsFullBySlaverSoc; + /// + /// Slave模式,光伏给主储能充满了,是否切换到Master模式,但是此时需要判断从储能SOC是否满了(从储能也满的话,也无法接收光伏的电),否则不切换 + /// 主要考虑尽可能的不浪费光伏的电,主从储能只要有余量就要接受光伏的电,也要防止频繁的切换 + /// < SolarToEsAsFullSoc + /// + public double Slave_SolarToMasterEsFullBySlaverSoc + { + get { return _Slave_SolarToMasterEsFullBySlaverSoc; } + set { _Slave_SolarToMasterEsFullBySlaverSoc = value; RaisePropertyChanged(); } + } + + private double _NightMaster_ToMasterFullSoc; + /// + /// 晚上,主储能充满的标志,也是切换到从储能的控制标志 + /// + public double NightMaster_ToMasterFullSoc + { + get { return _NightMaster_ToMasterFullSoc; } + set { _NightMaster_ToMasterFullSoc = value; RaisePropertyChanged(); } + } + + private double _NightSlave_ToSlaveFullSoc; + /// + /// 晚上,从储能充满的标志,也是切换到主储能的控制标志 + /// + public double NightSlave_ToSlaveFullSoc + { + get { return _NightSlave_ToSlaveFullSoc; } + set { _NightSlave_ToSlaveFullSoc = value; RaisePropertyChanged(); } + } + + private List _DisChargeModel; + public List DisChargeModel + { + get { return _DisChargeModel; } + set { _DisChargeModel = value; RaisePropertyChanged(); } + } + + private int _SelectedDisChargeModel; + public int SelectedDisChargeModel + { + get { return _SelectedDisChargeModel; } + set { _SelectedDisChargeModel = value; RaisePropertyChanged(); } + } + } +} diff --git a/OrpaonEMS.App/Models/DisChargeModelItem.cs b/OrpaonEMS.App/Models/DisChargeModelItem.cs new file mode 100644 index 0000000..c4a434c --- /dev/null +++ b/OrpaonEMS.App/Models/DisChargeModelItem.cs @@ -0,0 +1,10 @@ +using System; + +namespace OrpaonEMS.App.Models +{ + public class DisChargeModelItem + { + public int Model { get; set; } + public string DisChargeTime { get; set; } = string.Empty; + } +} diff --git a/OrpaonEMS.App/Models/DischargeTimeModel.cs b/OrpaonEMS.App/Models/DischargeTimeModel.cs new file mode 100644 index 0000000..74eeaa1 --- /dev/null +++ b/OrpaonEMS.App/Models/DischargeTimeModel.cs @@ -0,0 +1,80 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Windows.System.Power; + +namespace OrpaonEMS.App.Models +{ + /// + /// 放电时间模型 + /// 7、8月份11点放电 + /// 其他月份8点开始放点 + /// + public class DischargeTimeModel + { + public DischargeTimeModel(DisChargeType disChargeType) + { + + CurDisChargeType = disChargeType; + + } + + /// + /// 当前放电时间 + /// + public DateTime CurDischargeTime { get; set; } = new DateTime(DateTime.Now.Year, DateTime.Now.Month, DateTime.Now.Day, 8, 0, 0); + + /// + /// 可以放电吗? + /// + /// + public bool IsCanDischarge() + { + DateTime currentTime = DateTime.Now; + DateTime curDischargeTime = new DateTime(DateTime.Now.Year, DateTime.Now.Month, DateTime.Now.Day, CurDischargeTime.Hour, 0, 0); + + // Check if current time is after the configured discharge time + if (currentTime.TimeOfDay >= curDischargeTime.TimeOfDay) + { + return true; + } + + // Not yet time to discharge + return false; + } + + private DisChargeType _CurDisChargeType; + /// + /// 当前放电模式 + /// + public DisChargeType CurDisChargeType + { + get { return _CurDisChargeType; } + set + { + _CurDisChargeType = value; + } + } + + + } + + /// + /// 放电类型 + /// + public enum DisChargeType + { + /// + /// 放电模式A + /// + DischargeA = 1, + + /// + /// 放电模式B + /// + DischargeB = 2, + + } +} diff --git a/OrpaonEMS.App/Services/BmsDataService.cs b/OrpaonEMS.App/Services/BmsDataService.cs index 094d158..60d5c7d 100644 --- a/OrpaonEMS.App/Services/BmsDataService.cs +++ b/OrpaonEMS.App/Services/BmsDataService.cs @@ -417,6 +417,7 @@ namespace OrpaonEMS.App.Services /// public void CloseDrive() { + ThreadEnable = false; ModbusTcpNetDrive.ConnectClose(); } diff --git a/OrpaonEMS.App/Services/ConfigDataService.cs b/OrpaonEMS.App/Services/ConfigDataService.cs index 2ac4186..c78acca 100644 --- a/OrpaonEMS.App/Services/ConfigDataService.cs +++ b/OrpaonEMS.App/Services/ConfigDataService.cs @@ -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().ToList(); //削峰填谷模式的时间监视线程 var ListenPeakValleyTimeTaskInfo = Task.Run(() => ListenPeakValleyTimeCycle()); + + //加载控制参数配置 + LoadControlConfigValue(); } @@ -427,5 +432,421 @@ namespace OrpaonEMS.App.Services #endregion + + #region 控制参数配置 + + /// + /// 控制参数配置文件路径 + /// + private readonly string _controlConfigFilePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Config", "ControlConfigValue.json"); + + /// + /// 控制参数配置值 + /// + public ControlConfigValue ControlConfigValue { get; set; } + + /// + /// 加载控制参数配置 + /// + /// 返回加载结果,true表示成功,false表示失败 + 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(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(); + } + 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; + } + } + + /// + /// 保存控制参数配置 + /// + /// 返回保存结果,true表示成功,false表示失败 + 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; + } + } + + /// + /// 更新控制参数配置的值并保存 + /// + /// 要更新的控制参数配置对象 + /// 返回更新结果,true表示成功,false表示失败 + 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; + } + } + + /// + /// 获取放电时间配置集合 + /// + /// 放电时间配置列表(永不返回 null) + public List GetDisChargeModel() + { + if (ControlConfigValue == null) + { + ControlConfigValue = new ControlConfigValue(); + } + + if (ControlConfigValue.DisChargeModel == null) + { + ControlConfigValue.DisChargeModel = new List(); + } + + return ControlConfigValue.DisChargeModel; + } + + /// + /// 保存放电时间配置集合 + /// + /// 放电时间配置列表 + /// 保存结果 + public bool SaveDisChargeModel(List items) + { + if (ControlConfigValue == null) + { + ControlConfigValue = new ControlConfigValue(); + } + + // 基础校验 + var list = items ?? new List(); + 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(); + } + + /// + /// 时间上是否可以放电 + /// + /// + 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 + } } diff --git a/OrpaonEMS.App/Services/FFService.cs b/OrpaonEMS.App/Services/FFService.cs index 3d252f5..4242f11 100644 --- a/OrpaonEMS.App/Services/FFService.cs +++ b/OrpaonEMS.App/Services/FFService.cs @@ -166,6 +166,16 @@ namespace OrpaonEMS.App.Services set { _LinkStateMsg = value; RaisePropertyChanged(); } } + /// + /// 关闭连接 + /// + public void CloseDrive() + { + ThreadEnable=false; + if (ModbusRtuDrive!=null) ModbusRtuDrive!.Close(); + + } + #region 消防数据 diff --git a/OrpaonEMS.App/Services/InPowerPCSDataService.cs b/OrpaonEMS.App/Services/InPowerPCSDataService.cs index 577819e..4e45f01 100644 --- a/OrpaonEMS.App/Services/InPowerPCSDataService.cs +++ b/OrpaonEMS.App/Services/InPowerPCSDataService.cs @@ -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(); } diff --git a/OrpaonEMS.App/Services/NavigationMenuService.cs b/OrpaonEMS.App/Services/NavigationMenuService.cs index c4f8850..a53b3ef 100644 --- a/OrpaonEMS.App/Services/NavigationMenuService.cs +++ b/OrpaonEMS.App/Services/NavigationMenuService.cs @@ -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")); } } } diff --git a/OrpaonEMS.App/Services/YuePuRunModelService.cs b/OrpaonEMS.App/Services/YuePuRunModelService.cs index 1121809..906e4e6 100644 --- a/OrpaonEMS.App/Services/YuePuRunModelService.cs +++ b/OrpaonEMS.App/Services/YuePuRunModelService.cs @@ -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 } } + /// + /// 当前的放电时间模型 + /// + public DischargeTimeModel CurDischargeTimeModel { get; set; } + /// /// 税务大楼光伏逆流触发模型 /// @@ -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 /// private CancellationTokenSource NoSolarTokenSource { get; set; } + /// + /// 关闭系统:停止线程循环并断开所有通信连接 + /// + public void CloseSystem() + { + // 先关闭扫描线程使能,令 while(ThreadEnable) 循环尽快退出 + try { ThreadEnable = false; } catch { } + try { MasterTokenSource?.Cancel(); } catch { } + try { SlaveTokenSource?.Cancel(); } catch { } + try { Night_MasterTokenSource?.Cancel(); } catch { } + try { Night_SlaveTokenSource?.Cancel(); } catch { } + try { WaitTokenSource?.Cancel(); } catch { } + try { NoSolarTokenSource?.Cancel(); } catch { } + + // 停止输出,置零功率 + try { if (SlaveClient != null) { SlaveClient.ServerCmd.CmdPw = 0; } } catch { } + try { if (MasterClient != null) { MasterClient.ServerCmd.CmdPw = 0; } } catch { } + + // 断开设备通信 + try { SolarMdDrive?.Close(); } catch { } + try { MeterMdDrive?.Close(); } catch { } + try { ModbusTcpNetDrive?.ConnectClose(); } catch { } + try { BmsDataService.CloseDrive(); } catch { } + try { FFService.CloseDrive(); } catch { } + try { InPowerPCSDataService.CloseModbusRtu(); } catch { } + + } /// /// 预先状态 /// 就是确定目标状态,先进入准备状态时这个预先的状态信息,等条件具备后再进行操作 @@ -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 /// 配置服务信息 /// public ConfigDataService ConfigDataService { get; } + public BmsDataService BmsDataService { get; } + public FFService FFService { get; } + public InPowerPCSDataService InPowerPCSDataService { get; } /// diff --git a/OrpaonEMS.App/ViewModels/ControlConfigViewModel.cs b/OrpaonEMS.App/ViewModels/ControlConfigViewModel.cs new file mode 100644 index 0000000..cc1e250 --- /dev/null +++ b/OrpaonEMS.App/ViewModels/ControlConfigViewModel.cs @@ -0,0 +1,347 @@ +using OrpaonEMS.App.Models; +using OrpaonEMS.App.Services; +using OrpaonEMS.Core; +using Prism.Commands; +using System; +using System.Collections.ObjectModel; +using System.Linq; + +namespace OrpaonEMS.App.ViewModels +{ + public class ControlConfigViewModel : NavigationViewModel + { + public ControlConfigViewModel(YuePuRunModelService yuePuRunModelService, ConfigDataService configDataService) + { + YuePuRunModelService = yuePuRunModelService; + ConfigDataService = configDataService; + + // 从配置文件加载参数 + if (ConfigDataService.ControlConfigValue != null) + { + SolarToEsAsFullSoc = ConfigDataService.ControlConfigValue.SolarToEsAsFullSoc; + Master_ToSlaveByMasterSoc = ConfigDataService.ControlConfigValue.Master_ToSlaveByMasterSoc; + Master_ToSlaveBySlaveSoc = ConfigDataService.ControlConfigValue.Master_ToSlaveBySlaveSoc; + Master_SolarToSlaveEsFullByMasterSoc = ConfigDataService.ControlConfigValue.Master_SolarToSlaveEsFullByMasterSoc; + Slave_ToMasterBySlaveSoc = ConfigDataService.ControlConfigValue.Slave_ToMasterBySlaveSoc; + Slave_ToMasterByMasterSoc = ConfigDataService.ControlConfigValue.Slave_ToMasterByMasterSoc; + Slave_SolarToMasterEsFullBySlaverSoc = ConfigDataService.ControlConfigValue.Slave_SolarToMasterEsFullBySlaverSoc; + NightMaster_ToMasterFullSoc = ConfigDataService.ControlConfigValue.NightMaster_ToMasterFullSoc; + NightSlave_ToSlaveFullSoc = ConfigDataService.ControlConfigValue.NightSlave_ToSlaveFullSoc; + + // 加载放电模式列表与已选项 + var list = ConfigDataService.GetDisChargeModel(); + DisChargeModels = new ObservableCollection(list); + var sel = ConfigDataService.ControlConfigValue.SelectedDisChargeModel; + if (DisChargeModels != null && DisChargeModels.Count > 0) + { + // 若配置中无效则回落到第一个项 + if (DisChargeModels.Any(m => m.Model == sel)) + SelectedDisChargeModelId = sel; + else + SelectedDisChargeModelId = DisChargeModels[0].Model; + } + else + { + SelectedDisChargeModelId = 1; + } + // 与旧逻辑保持同步(可选) + if (System.Enum.IsDefined(typeof(DisChargeType), SelectedDisChargeModelId)) + SelectedDisChargeType = (DisChargeType)SelectedDisChargeModelId; + } + + // 同步到运行模式服务 + YuePuRunModelService.SolarToEsAsFullSoc = SolarToEsAsFullSoc; + YuePuRunModelService.Master_ToSlaveByMasterSoc = Master_ToSlaveByMasterSoc; + YuePuRunModelService.Master_ToSlaveBySlaveSoc = Master_ToSlaveBySlaveSoc; + YuePuRunModelService.Master_SolarToSlaveEsFullByMasterSoc = Master_SolarToSlaveEsFullByMasterSoc; + YuePuRunModelService.Slave_ToMasterBySlaveSoc = Slave_ToMasterBySlaveSoc; + YuePuRunModelService.Slave_ToMasterByMasterSoc = Slave_ToMasterByMasterSoc; + YuePuRunModelService.Slave_SolarToMasterEsFullBySlaverSoc = Slave_SolarToMasterEsFullBySlaverSoc; + YuePuRunModelService.NightMaster_ToMasterFullSoc = NightMaster_ToMasterFullSoc; + YuePuRunModelService.NightSlave_ToSlaveFullSoc = NightSlave_ToSlaveFullSoc; + + + //SelectedDisChargeType= + } + + + private DelegateCommand _BtnConfigSave; + /// + /// 运行模式操作 + /// + public DelegateCommand BtnConfigSave + { + set + { + _BtnConfigSave = value; + } + get + { + if (_BtnConfigSave == null) + { + _BtnConfigSave = new DelegateCommand((par) => BtnConfigSaveCall(par)); + } + return _BtnConfigSave; + } + } + + + private DelegateCommand _BtnCloseSys; + /// + /// 关闭系统 + /// + public DelegateCommand BtnCloseSys + { + set + { + _BtnCloseSys = value; + } + get + { + if (_BtnCloseSys == null) + { + _BtnCloseSys = new DelegateCommand(() => BtnCloseSysCall()); + } + return _BtnCloseSys; + } + } + /// + /// 关闭系统的执行方法 + /// + private void BtnCloseSysCall() + { + try + { + // 停止后台监听线程(例如削峰填谷时间监听) + if (ConfigDataService != null) + { + ConfigDataService.ThreadEnable = false; + } + + // 关闭运行服务中的状态循环与通信连接 + YuePuRunModelService?.CloseSystem(); + } + catch (System.Exception ex) + { + System.Windows.MessageBox.Show($"关闭系统时发生异常: {ex.Message}", "错误", System.Windows.MessageBoxButton.OK, System.Windows.MessageBoxImage.Error); + } + finally + { + try + { + // 退出应用程序 + System.Windows.Application.Current.Shutdown(); + } + catch + { + System.Environment.Exit(0); + } + } + } + + private void BtnConfigSaveCall(string par) + { + try + { + // 同步到运行模式服务 + YuePuRunModelService.SolarToEsAsFullSoc = SolarToEsAsFullSoc; + YuePuRunModelService.Master_ToSlaveBySlaveSoc = Master_ToSlaveBySlaveSoc; + YuePuRunModelService.Master_SolarToSlaveEsFullByMasterSoc = Master_SolarToSlaveEsFullByMasterSoc; + YuePuRunModelService.Slave_ToMasterBySlaveSoc = Slave_ToMasterBySlaveSoc; + YuePuRunModelService.Slave_ToMasterByMasterSoc = Slave_ToMasterByMasterSoc; + YuePuRunModelService.Slave_SolarToMasterEsFullBySlaverSoc = Slave_SolarToMasterEsFullBySlaverSoc; + YuePuRunModelService.NightMaster_ToMasterFullSoc = NightMaster_ToMasterFullSoc; + YuePuRunModelService.NightSlave_ToSlaveFullSoc = NightSlave_ToSlaveFullSoc; + + // 保存到配置文件 + if (ConfigDataService.ControlConfigValue != null) + { + ConfigDataService.ControlConfigValue.SolarToEsAsFullSoc = SolarToEsAsFullSoc; + ConfigDataService.ControlConfigValue.Master_ToSlaveByMasterSoc = Master_ToSlaveByMasterSoc; + ConfigDataService.ControlConfigValue.Master_ToSlaveBySlaveSoc = Master_ToSlaveBySlaveSoc; + ConfigDataService.ControlConfigValue.Master_SolarToSlaveEsFullByMasterSoc = Master_SolarToSlaveEsFullByMasterSoc; + ConfigDataService.ControlConfigValue.Slave_ToMasterBySlaveSoc = Slave_ToMasterBySlaveSoc; + ConfigDataService.ControlConfigValue.Slave_ToMasterByMasterSoc = Slave_ToMasterByMasterSoc; + ConfigDataService.ControlConfigValue.Slave_SolarToMasterEsFullBySlaverSoc = Slave_SolarToMasterEsFullBySlaverSoc; + ConfigDataService.ControlConfigValue.NightMaster_ToMasterFullSoc = NightMaster_ToMasterFullSoc; + ConfigDataService.ControlConfigValue.NightSlave_ToSlaveFullSoc = NightSlave_ToSlaveFullSoc; + + // 保存选择的放电模式(以 Model Id 为准) + ConfigDataService.ControlConfigValue.SelectedDisChargeModel = SelectedDisChargeModelId; + // 同步旧字段 + if (System.Enum.IsDefined(typeof(DisChargeType), SelectedDisChargeModelId)) + SelectedDisChargeType = (DisChargeType)SelectedDisChargeModelId; + + // 调用保存方法 + bool saveResult = ConfigDataService.SaveControlConfigValue(); + + if (saveResult) + { + System.Windows.MessageBox.Show("控制参数配置保存成功!", "提示", System.Windows.MessageBoxButton.OK, System.Windows.MessageBoxImage.Information); + } + else + { + System.Windows.MessageBox.Show("控制参数配置保存失败,请查看日志!", "错误", System.Windows.MessageBoxButton.OK, System.Windows.MessageBoxImage.Error); + } + } + else + { + System.Windows.MessageBox.Show("控制参数配置对象为空!", "错误", System.Windows.MessageBoxButton.OK, System.Windows.MessageBoxImage.Error); + } + } + catch (System.Exception ex) + { + System.Windows.MessageBox.Show($"保存控制参数配置时发生异常: {ex.Message}", "错误", System.Windows.MessageBoxButton.OK, System.Windows.MessageBoxImage.Error); + } + } + + private double _SolarToEsAsFullSoc = 97; + /// + /// 光伏给储能充电 作为满的比值 + /// 90-97 + /// + public double SolarToEsAsFullSoc + { + get { return _SolarToEsAsFullSoc; } + set { _SolarToEsAsFullSoc = value; RaisePropertyChanged(); } + } + + private double _Master_ToSlaveByMasterSoc = 5; + /// + /// Master模式,切换到Slave模式时MasterSOC的阀值 + /// 两个同时考虑 + /// + public double Master_ToSlaveByMasterSoc + { + get { return _Master_ToSlaveByMasterSoc; } + set { _Master_ToSlaveByMasterSoc = value; RaisePropertyChanged(); } + } + + + private double _Master_ToSlaveBySlaveSoc = 15; + /// + /// Master模式,切换到Slave模式时Slave SOC的阀值 + /// 两个同时考虑 + /// + public double Master_ToSlaveBySlaveSoc + { + get { return _Master_ToSlaveBySlaveSoc; } + set { _Master_ToSlaveBySlaveSoc = value; RaisePropertyChanged(); } + } + + private double _Master_SolarToSlaveEsFullByMasterSoc = 95; + /// + /// Master模式,光伏给从储能充满了,是否切换到Slave模式,但是此时需要判断主储能MasterSOC是否满了(主储能也满的话,也无法接收光伏的电),否则不切换 + /// 主要考虑尽可能的不浪费光伏的电,主从储能只要有余量就要接受光伏的电,也要防止频繁的切换 + /// < SolarToEsAsFullSoc + /// + public double Master_SolarToSlaveEsFullByMasterSoc + { + get { return _Master_SolarToSlaveEsFullByMasterSoc; } + set { _Master_SolarToSlaveEsFullByMasterSoc = value; RaisePropertyChanged(); } + } + + + + private double _Slave_ToMasterBySlaveSoc = 5; + /// + /// Slave模式,切换到Master模式时SOC的阀值 + /// 两个同时考虑 + /// + public double Slave_ToMasterBySlaveSoc + { + get { return _Slave_ToMasterBySlaveSoc; } + set { _Slave_ToMasterBySlaveSoc = value; RaisePropertyChanged(); } + } + + + private double _Slave_ToMasterByMasterSoc = 15; + /// + /// Slave模式,切换到Master模式时SOC的阀值 + /// 两个同时考虑 + /// + public double Slave_ToMasterByMasterSoc + { + get { return _Slave_ToMasterByMasterSoc; } + set { _Slave_ToMasterByMasterSoc = value; RaisePropertyChanged(); } + } + + + private double _Slave_SolarToMasterEsFullBySlaverSoc = 95; + /// + /// Slave模式,光伏给主储能充满了,是否切换到Master模式,但是此时需要判断从储能SOC是否满了(从储能也满的话,也无法接收光伏的电),否则不切换 + /// 主要考虑尽可能的不浪费光伏的电,主从储能只要有余量就要接受光伏的电,也要防止频繁的切换 + /// < SolarToEsAsFullSoc + /// + public double Slave_SolarToMasterEsFullBySlaverSoc + { + get { return _Slave_SolarToMasterEsFullBySlaverSoc; } + set { _Slave_SolarToMasterEsFullBySlaverSoc = value; RaisePropertyChanged(); } + } + + private double _NightMaster_ToMasterFullSoc = 98; + /// + /// 晚上,主储能充满的标志,也是切换到从储能的控制标志 + /// + public double NightMaster_ToMasterFullSoc + { + get { return _NightMaster_ToMasterFullSoc; } + set { _NightMaster_ToMasterFullSoc = value; RaisePropertyChanged(); } + } + + + private double _NightSlave_ToSlaveFullSoc = 98; + /// + /// 晚上,从储能充满的标志,也是切换到主储能的控制标志 + /// + public double NightSlave_ToSlaveFullSoc + { + get { return _NightSlave_ToSlaveFullSoc; } + set { _NightSlave_ToSlaveFullSoc = value; RaisePropertyChanged(); } + } + + public YuePuRunModelService YuePuRunModelService { get; } + + /// + /// 配置数据服务 + /// + public ConfigDataService ConfigDataService { get; } + + /// + /// 选中的放电模式 + /// + private DisChargeType _SelectedDisChargeType; + public DisChargeType SelectedDisChargeType + { + get { return _SelectedDisChargeType; } + set { _SelectedDisChargeType = value; RaisePropertyChanged(); } + } + + /// + /// 放电模式列表 + /// + private ObservableCollection _DisChargeModels; + public ObservableCollection DisChargeModels + { + get { return _DisChargeModels; } + set { _DisChargeModels = value; RaisePropertyChanged(); } + } + + /// + /// 选中的放电模式(列表项的 Model Id) + /// + private int _SelectedDisChargeModelId; + public int SelectedDisChargeModelId + { + get { return _SelectedDisChargeModelId; } + set + { + _SelectedDisChargeModelId = value; + // 同步枚举值(若兼容) + if (System.Enum.IsDefined(typeof(DisChargeType), _SelectedDisChargeModelId)) + SelectedDisChargeType = (DisChargeType)_SelectedDisChargeModelId; + RaisePropertyChanged(); + } + } + } +} diff --git a/OrpaonEMS.App/Views/AutoHandView.xaml b/OrpaonEMS.App/Views/AutoHandView.xaml index b4320c0..9be5e3d 100644 --- a/OrpaonEMS.App/Views/AutoHandView.xaml +++ b/OrpaonEMS.App/Views/AutoHandView.xaml @@ -687,6 +687,10 @@ FontSize="16" Text="{Binding YuePuRunModelService.EsSoc2}" /> + + + + diff --git a/OrpaonEMS.App/Views/ControlConfigView.xaml b/OrpaonEMS.App/Views/ControlConfigView.xaml index 3cdd344..6db6262 100644 --- a/OrpaonEMS.App/Views/ControlConfigView.xaml +++ b/OrpaonEMS.App/Views/ControlConfigView.xaml @@ -14,15 +14,18 @@ prism:ViewModelLocator.AutoWireViewModel="True" mc:Ignorable="d"> - + @@ -41,20 +44,44 @@ + + + + + + + + + + + + + + - - - - + +