将 StandardScene 各插件的配置/监控窗体从 WinForms 迁移到 CycleGUI(删除 .Designer.cs/.resx,重写为 PanelBuilder 立即模式 UI,新增 CycleUiHelper 统一对话框)。 同时修复代码审核中的问题: - 后台文件写入加锁 + try/catch(ButtonBoxManager / DoorManager,对齐 LoopViewer.SaveTasks 模式) - CoderFieldsMetadata.cs 启用 #nullable enable,消除 CS8632 警告 - DummyCar 移除已废弃的 rightClickAction()/SetPosition() - CarRemoteHelper.OpenVehicleWebPage 的 Process.Start 加 try/catch - 重命名名不副实的 Mstsc()(现为打开网页) - 统一弃元命名为 _ - TrafficInterlockViewer 改用稳定 Id(GUID)做选择/编辑,替代行索引 - csproj 改用 $(CGUILibDir) 解析 CycleGUI,绝对路径收敛到 Directory.Build.props 构建:dotnet build StandardScene.sln → 0 错误,30 警告(均为历史遗留)。 注:static 单例状态重构(审核第 8 项)暂未处理,留待单独任务。
404 lines
15 KiB
C#
404 lines
15 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using CycleGUI;
|
|
using SimpleCore;
|
|
using SimpleCore.Library;
|
|
using StandardScene.Utils;
|
|
|
|
namespace StandardScene.Charge
|
|
{
|
|
/// <summary>
|
|
/// 充电桩管理辅助类
|
|
/// 提供简化的静态方法用于快速访问充电桩功能
|
|
/// </summary>
|
|
public static class ChargeStationHelper
|
|
{
|
|
/// <summary>打开充电桩管理面板(单实例)。</summary>
|
|
public static void OpenManagementWindow() => ChargeStationManagementForm.Open();
|
|
|
|
/// <summary>打开充电桩管理面板(兼容旧 API)。</summary>
|
|
public static void OpenManagementDialog() => ChargeStationManagementForm.Open();
|
|
|
|
/// <summary>
|
|
/// 获取指定站点的充电桩
|
|
/// </summary>
|
|
/// <param name="siteId">站点ID</param>
|
|
/// <returns>充电桩对象,如果不存在则返回null</returns>
|
|
public static ChargeStation GetStationBySiteId(int siteId)
|
|
{
|
|
var dataService = ChargeStationDataService.Instance;
|
|
return dataService.GetAllStations()
|
|
.FirstOrDefault(s => s.SiteId == siteId);
|
|
}
|
|
|
|
/// <summary>
|
|
/// 获取指定IP的充电桩
|
|
/// </summary>
|
|
/// <param name="ipAddress">IP地址</param>
|
|
/// <returns>充电桩对象,如果不存在则返回null</returns>
|
|
public static ChargeStation GetStationByIp(string ipAddress)
|
|
{
|
|
var dataService = ChargeStationDataService.Instance;
|
|
return dataService.GetAllStations()
|
|
.FirstOrDefault(s => s.IpAddress == ipAddress);
|
|
}
|
|
|
|
/// <summary>
|
|
/// 获取所有充电桩配置
|
|
/// </summary>
|
|
/// <returns>所有充电桩配置列表</returns>
|
|
public static List<ChargeStation> GetAllStationConfigs()
|
|
{
|
|
var dataService = ChargeStationDataService.Instance;
|
|
return dataService.GetAllStations();
|
|
}
|
|
|
|
/// <summary>
|
|
/// 获取当前充电策略配置
|
|
/// </summary>
|
|
/// <returns>充电策略配置对象</returns>
|
|
public static ChargeStrategyConfig GetChargeStrategyConfig()
|
|
{
|
|
var configService = ChargeStrategyConfigService.Instance;
|
|
return configService.LoadConfig();
|
|
}
|
|
|
|
/// <summary>
|
|
/// 保存充电策略配置
|
|
/// </summary>
|
|
/// <param name="config">充电策略配置对象</param>
|
|
public static void SaveChargeStrategyConfig(ChargeStrategyConfig config)
|
|
{
|
|
var configService = ChargeStrategyConfigService.Instance;
|
|
configService.SaveConfig(config);
|
|
}
|
|
|
|
/// <summary>
|
|
/// 检查指定站点是否有可用的充电桩
|
|
/// </summary>
|
|
/// <param name="siteId">站点ID</param>
|
|
/// <returns>true表示有可用充电桩,false表示没有</returns>
|
|
public static bool IsSiteHasAvailableChargeStation(int siteId)
|
|
{
|
|
var station = GetStationBySiteId(siteId);
|
|
return station != null &&
|
|
station.Enabled &&
|
|
station.Status == ChargeStationStatus.Idle;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 标记充电桩开始充电
|
|
/// </summary>
|
|
/// <param name="stationId">充电桩编号</param>
|
|
/// <param name="carId">车辆ID</param>
|
|
/// <returns>成功返回true,失败返回false</returns>
|
|
public static bool StartCharging(string stationId, int carId)
|
|
{
|
|
try
|
|
{
|
|
var dataService = ChargeStationDataService.Instance;
|
|
var station = dataService.GetStationById(stationId);
|
|
|
|
if (station == null)
|
|
{
|
|
Diagnosis.Log($"充电桩 {stationId} 不存在", "ChargeStation", true);
|
|
return false;
|
|
}
|
|
|
|
if (station.Status == ChargeStationStatus.Charging)
|
|
{
|
|
Diagnosis.Log($"充电桩 {station.Name} 已经在充电中", "ChargeStation", true);
|
|
return false;
|
|
}
|
|
|
|
bool success = dataService.UpdateStationStatus(stationId, ChargeStationStatus.Charging);
|
|
|
|
if (success)
|
|
{
|
|
Diagnosis.Log($"车辆 {carId} 开始在充电桩 {station.Name} 充电", "ChargeStation", true);
|
|
}
|
|
|
|
return success;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Diagnosis.Log($"启动充电失败: {ExceptionFormatter.FormatEx(ex)}", "ChargeStation", true);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 标记充电桩停止充电
|
|
/// </summary>
|
|
/// <param name="stationId">充电桩编号</param>
|
|
/// <param name="carId">车辆ID</param>
|
|
/// <returns>成功返回true,失败返回false</returns>
|
|
public static bool StopCharging(string stationId, int carId)
|
|
{
|
|
try
|
|
{
|
|
var dataService = ChargeStationDataService.Instance;
|
|
var station = dataService.GetStationById(stationId);
|
|
|
|
if (station == null)
|
|
{
|
|
Diagnosis.Log($"充电桩 {stationId} 不存在", "ChargeStation", true);
|
|
return false;
|
|
}
|
|
|
|
bool success = dataService.UpdateStationStatus(stationId, ChargeStationStatus.Idle);
|
|
|
|
if (success)
|
|
{
|
|
Diagnosis.Log($"车辆 {carId} 在充电桩 {station.Name} 充电完成", "ChargeStation", true);
|
|
}
|
|
|
|
return success;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Diagnosis.Log($"停止充电失败: {ExceptionFormatter.FormatEx(ex)}", "ChargeStation", true);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 标记充电桩为故障状态
|
|
/// </summary>
|
|
/// <param name="stationId">充电桩编号</param>
|
|
/// <param name="reason">故障原因</param>
|
|
/// <returns>成功返回true,失败返回false</returns>
|
|
public static bool MarkAsFault(string stationId, string reason = "")
|
|
{
|
|
try
|
|
{
|
|
var dataService = ChargeStationDataService.Instance;
|
|
bool success = dataService.UpdateStationStatus(stationId, ChargeStationStatus.Fault);
|
|
|
|
if (success)
|
|
{
|
|
var station = dataService.GetStationById(stationId);
|
|
var message = string.IsNullOrEmpty(reason)
|
|
? $"充电桩 {station.Name} 标记为故障"
|
|
: $"充电桩 {station.Name} 标记为故障: {reason}";
|
|
|
|
Diagnosis.Log(message, "ChargeStation", true);
|
|
}
|
|
|
|
return success;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Diagnosis.Log($"标记故障失败: {ExceptionFormatter.FormatEx(ex)}", "ChargeStation", true);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 获取充电桩状态摘要信息
|
|
/// </summary>
|
|
/// <returns>格式化的状态字符串</returns>
|
|
public static string GetStatusSummary()
|
|
{
|
|
var dataService = ChargeStationDataService.Instance;
|
|
var stations = dataService.GetAllStations();
|
|
|
|
var total = stations.Count;
|
|
var idle = stations.Count(s => s.Status == ChargeStationStatus.Idle && s.Enabled);
|
|
var charging = stations.Count(s => s.Status == ChargeStationStatus.Charging);
|
|
var fault = stations.Count(s => s.Status == ChargeStationStatus.Fault);
|
|
var offline = stations.Count(s => s.Status == ChargeStationStatus.Battery);
|
|
|
|
return $"总数:{total} | 空闲:{idle} | 充电中:{charging} | 故障:{fault} | 离线:{offline}";
|
|
}
|
|
|
|
/// <summary>
|
|
/// 获取最近的空闲充电桩(基于站点ID)
|
|
/// </summary>
|
|
/// <param name="currentSiteId">当前站点ID</param>
|
|
/// <returns>最近的充电桩,如果没有则返回null</returns>
|
|
public static ChargeStation FindNearestIdleStation(int currentSiteId)
|
|
{
|
|
var dataService = ChargeStationDataService.Instance;
|
|
var idleStations = dataService.GetIdleStations();
|
|
|
|
if (idleStations.Count == 0)
|
|
return null;
|
|
|
|
// 优先选择同站点的充电桩
|
|
var sameStation = idleStations.FirstOrDefault(s => s.SiteId == currentSiteId);
|
|
if (sameStation != null)
|
|
return sameStation;
|
|
|
|
// 否则选择第一个可用的
|
|
return idleStations[0];
|
|
}
|
|
|
|
/// <summary>
|
|
/// 快速创建测试充电桩(用于测试)
|
|
/// </summary>
|
|
/// <param name="name">名称</param>
|
|
/// <param name="ip">IP地址</param>
|
|
/// <param name="siteId">站点ID</param>
|
|
/// <returns>创建成功返回true</returns>
|
|
public static bool QuickAddStation(string name, string ip, int? siteId = null)
|
|
{
|
|
var dataService = ChargeStationDataService.Instance;
|
|
|
|
var station = new ChargeStation
|
|
{
|
|
Name = name,
|
|
IpAddress = ip,
|
|
Port = 502,
|
|
SetVoltage = 220.0,
|
|
SetElectricCurrent = 32.0,
|
|
Status = ChargeStationStatus.Idle,
|
|
Enabled = true,
|
|
SiteId = siteId,
|
|
Remarks = $"快速创建于 {DateTime.Now}"
|
|
};
|
|
|
|
bool success = dataService.AddStation(station, out string errorMsg);
|
|
|
|
if (success)
|
|
{
|
|
Diagnosis.Log($"快速创建充电桩: {name}", "ChargeStation", true);
|
|
}
|
|
else
|
|
{
|
|
Diagnosis.Log($"快速创建充电桩失败: {errorMsg}", "ChargeStation", true);
|
|
}
|
|
|
|
return success;
|
|
}
|
|
|
|
/// <summary>显示充电桩选择面板(非阻塞;通过 <paramref name="onSelected"/> 回调返回结果)。</summary>
|
|
public static void ShowStationSelectionDialog(ChargeStationStatus? filterByStatus, System.Action<ChargeStation> onSelected)
|
|
{
|
|
var stations = ChargeStationDataService.Instance.GetAllStations();
|
|
if (filterByStatus.HasValue)
|
|
stations = stations.Where(s => s.Status == filterByStatus.Value).ToList();
|
|
|
|
if (stations.Count == 0)
|
|
{
|
|
CycleUiHelper.Alert("提示", "没有符合条件的充电桩");
|
|
onSelected?.Invoke(null);
|
|
return;
|
|
}
|
|
|
|
var labels = stations.Select(s =>
|
|
$"[{s.StationId}] {s.Name} - {s.IpAddress}:{s.Port} - {GetStatusText(s.Status)}").ToArray();
|
|
int sel = 0;
|
|
var dlg = GUI.DeclarePanel()
|
|
.ShowTitle("选择充电桩")
|
|
.TopMost(true)
|
|
.InitSize(520, 400)
|
|
.InitPos(false, 0, 0, 0.5f, 0.5f, 0.5f, 0.5f);
|
|
dlg.Define(pb =>
|
|
{
|
|
if (pb.Closing()) { dlg.Exit(); onSelected?.Invoke(null); return; }
|
|
sel = pb.ListBox("充电桩", labels, height: 12);
|
|
if (pb.Button("确定", distinct: "cs-pick-ok"))
|
|
{
|
|
dlg.Exit();
|
|
onSelected?.Invoke(sel >= 0 && sel < stations.Count ? stations[sel] : null);
|
|
}
|
|
pb.SameLine(8);
|
|
if (pb.Button("取消", distinct: "cs-pick-cancel"))
|
|
{
|
|
dlg.Exit();
|
|
onSelected?.Invoke(null);
|
|
}
|
|
});
|
|
}
|
|
|
|
/// <summary>
|
|
/// 获取状态文本
|
|
/// </summary>
|
|
private static string GetStatusText(ChargeStationStatus status)
|
|
{
|
|
switch (status)
|
|
{
|
|
case ChargeStationStatus.Idle: return "空闲";
|
|
case ChargeStationStatus.Charging: return "充电中";
|
|
case ChargeStationStatus.Fault: return "故障";
|
|
case ChargeStationStatus.Battery: return "离线";
|
|
default: return "未知";
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 批量更新充电桩在线状态(用于定期监控)
|
|
/// </summary>
|
|
/// <param name="timeout">超时时间(毫秒)</param>
|
|
/// <returns>更新的充电桩数量</returns>
|
|
public static int UpdateOnlineStatus(int timeout = 3000)
|
|
{
|
|
var dataService = ChargeStationDataService.Instance;
|
|
var stations = dataService.GetAllStations().Where(s => s.Enabled).ToList();
|
|
int updatedCount = 0;
|
|
|
|
foreach (var station in stations)
|
|
{
|
|
try
|
|
{
|
|
// 这里应该实际ping充电桩,此处仅演示
|
|
bool isOnline = PingStation(station.IpAddress, station.Port, timeout);
|
|
|
|
var expectedStatus = isOnline
|
|
? (station.Status == ChargeStationStatus.Battery ? ChargeStationStatus.Idle : station.Status)
|
|
: ChargeStationStatus.Battery;
|
|
|
|
if (station.Status != expectedStatus &&
|
|
(station.Status == ChargeStationStatus.Battery || expectedStatus == ChargeStationStatus.Battery))
|
|
{
|
|
if (dataService.UpdateStationStatus(station.StationId, expectedStatus))
|
|
{
|
|
updatedCount++;
|
|
Diagnosis.Log($"充电桩 {station.Name} 状态更新为: {GetStatusText(expectedStatus)}",
|
|
"ChargeStation", true);
|
|
}
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Diagnosis.Log($"检查充电桩 {station.Name} 在线状态失败: {ex.Message}",
|
|
"ChargeStation");
|
|
}
|
|
}
|
|
|
|
return updatedCount;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Ping 充电桩(检查连通性)
|
|
/// </summary>
|
|
private static bool PingStation(string ip, int port, int timeout)
|
|
{
|
|
try
|
|
{
|
|
using (var client = new System.Net.Sockets.TcpClient())
|
|
{
|
|
var result = client.BeginConnect(ip, port, null, null);
|
|
var success = result.AsyncWaitHandle.WaitOne(TimeSpan.FromMilliseconds(timeout));
|
|
|
|
if (success)
|
|
{
|
|
client.EndConnect(result);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
}
|
|
catch
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|