786 lines
32 KiB
C#
786 lines
32 KiB
C#
using System;
|
||
using System.Collections.Generic;
|
||
using System.Linq;
|
||
using System.Net;
|
||
using System.Text;
|
||
using System.Threading;
|
||
using System.Threading.Tasks;
|
||
using System.Windows.Forms;
|
||
using CommonUsage;
|
||
using LessokajiWeaverUtilities.Utilities;
|
||
using Newtonsoft.Json;
|
||
using SimpleLite;
|
||
using SimpleLite.RCS;
|
||
using SimpleLite.RCS.CarTypes;
|
||
using SimpleCore;
|
||
using SimpleCore.Library;
|
||
using SimpleCore.PropType;
|
||
using StandardScene.Chained;
|
||
using StandardScene.ChargeStationType;
|
||
using StandardScene.Model;
|
||
|
||
namespace StandardScene.Charge
|
||
{
|
||
/// <summary>
|
||
/// 标准充电进程状态
|
||
/// </summary>
|
||
public class StandardChargeMissionStatus : AbstractChargeMissionStatus
|
||
{
|
||
/// <summary>
|
||
/// 是否屏蔽充电桩交互(true=屏蔽,false=允许)
|
||
/// </summary>
|
||
public bool ShieldInterLock = false;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 标准充电进程
|
||
/// 负责充电桩的初始化、通讯管理和充电业务逻辑处理
|
||
/// </summary>
|
||
[MissionType(Name = "充电进程", editor = typeof(StandardChargeMission))]
|
||
[I18N.DocumentTranslation(Name = "Charge Mission", locale = "en")]
|
||
public class StandardChargeMission : AbstractChargeLogiceMission
|
||
{
|
||
#region 字段和属性
|
||
|
||
/// <summary>
|
||
/// 充电站字典 Key=站点ID, Value=充电站对象
|
||
/// </summary>
|
||
[JsonIgnore]
|
||
public Dictionary<int, AbstractChargeStation> ChargeStations;
|
||
|
||
/// <summary>
|
||
/// 进程状态
|
||
/// </summary>
|
||
public override MissionStatus status { get; set; } = new StandardChargeMissionStatus();
|
||
|
||
/// <summary>
|
||
/// 进程是否已启动
|
||
/// </summary>
|
||
[JsonIgnore]
|
||
public bool myStarted = false;
|
||
|
||
/// <summary>
|
||
/// 时间同步服务是否已启动
|
||
/// </summary>
|
||
[JsonIgnore]
|
||
public bool tsStarted = false;
|
||
|
||
/// <summary>
|
||
/// 充电处理线程
|
||
/// </summary>
|
||
[JsonIgnore]
|
||
private Thread ChargeThread;
|
||
|
||
/// <summary>
|
||
/// 当前正在充电的车辆 Key=车辆ID, Value=充电次数
|
||
/// </summary>
|
||
[JsonIgnore]
|
||
public Dictionary<string, int> inChargeCar = new();
|
||
|
||
/// <summary>
|
||
/// 上一次充电的车辆记录
|
||
/// </summary>
|
||
[JsonIgnore]
|
||
public Dictionary<string, int> lastinChargeCar = new();
|
||
|
||
/// <summary>
|
||
/// 充电开始时间 Key=车辆ID, Value=开始时间
|
||
/// </summary>
|
||
[JsonIgnore]
|
||
public Dictionary<string, DateTime> BeginTime = new();
|
||
|
||
/// <summary>
|
||
/// 充电条件状态
|
||
/// </summary>
|
||
[JsonIgnore]
|
||
public Dictionary<string, bool> Condition = new();
|
||
|
||
/// <summary>
|
||
/// 上一次充电条件状态
|
||
/// </summary>
|
||
[JsonIgnore]
|
||
public Dictionary<string, bool> lastCondition = new();
|
||
|
||
/// <summary>
|
||
/// 充电超时时间(小时)
|
||
/// </summary>
|
||
[JsonIgnore]
|
||
public double outtimeOfCharge = 0.5;
|
||
|
||
/// <summary>
|
||
/// UDP通讯服务
|
||
/// </summary>
|
||
[JsonIgnore]
|
||
public ChargeUdpService UdpService;
|
||
|
||
#endregion
|
||
|
||
#region 辅助方法
|
||
|
||
/// <summary>
|
||
/// 获取最低电量车辆的SOC值
|
||
/// 用于充电策略判断,找到系统中电量最低的空闲车辆
|
||
/// </summary>
|
||
/// <param name="car">当前车辆</param>
|
||
/// <param name="allChargeSite">所有充电站点列表</param>
|
||
/// <returns>最低电量值</returns>
|
||
public override float LowerCarSoc(AbstractCar car, List<Site> allChargeSite)
|
||
{
|
||
try
|
||
{
|
||
// 查找符合条件的最低电量车辆:
|
||
// 1. 车辆在有效站点上(GetLastSite != -1)
|
||
// 2. 车辆有坐标信息(haveCoordination)
|
||
// 3. 车辆未被占用(!occupied)
|
||
// 4. 车辆未在充电(!charging)
|
||
// 5. 车辆不在充电站点上
|
||
// 6. 车辆在线
|
||
var lowCar = SimpleLib.GetAllCars()
|
||
.OfType<Car>()
|
||
.ToList()
|
||
.FindAll(p =>
|
||
p.GetLastSite() != -1 &&
|
||
p.haveCoordination &&
|
||
!p.tags.Contains("occupied") &&
|
||
!p.tags.Contains("charging") &&
|
||
!allChargeSite.Contains(SimpleLib.GetSite(p.GetLastSite())) &&
|
||
IsOnlineCar((Car)p))
|
||
.OrderBy(p => Commons.CarValue(p, "Soc"))
|
||
.FirstOrDefault();
|
||
|
||
// 如果没有找到符合条件的车辆,返回当前车辆的电量
|
||
if (lowCar == null)
|
||
return (float)Commons.CarValue((Car)car, "Soc");
|
||
|
||
return (float)Commons.CarValue(lowCar, "Soc");
|
||
}
|
||
catch (Exception e)
|
||
{
|
||
Diagnosis.Post($"获取最低电量车辆失败: {e.Message}", "error");
|
||
return (float)0;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取车辆的充电类型
|
||
/// 根据车辆字段判断是FRLD还是MuXing类型
|
||
/// </summary>
|
||
/// <param name="car">车辆对象</param>
|
||
/// <returns>充电类型字符串</returns>
|
||
public override string GetChargeType(AbstractCar car)
|
||
{
|
||
if (car is null)
|
||
{
|
||
return "";
|
||
}
|
||
|
||
// 优先判断FRLD类型
|
||
if (car.fields.ContainsKey("FRLD"))
|
||
return "FRLD";
|
||
|
||
// 其次判断MuXing类型
|
||
if (car.fields.ContainsKey("MuXing"))
|
||
return "MuXing";
|
||
|
||
// 默认返回MuXing
|
||
return "MuXing";
|
||
}
|
||
|
||
/// <summary>
|
||
/// 判断车辆是否在线
|
||
/// 通过车辆是否在有效站点上来判断
|
||
/// </summary>
|
||
/// <param name="car">车辆对象</param>
|
||
/// <returns>true=在线, false=离线</returns>
|
||
public override bool IsOnlineCar(Car car)
|
||
{
|
||
return car.GetLastSite() != -1;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 车辆到达站点时的处理
|
||
/// 记录车辆到达充电站点的日志
|
||
/// </summary>
|
||
/// <param name="car">车辆对象</param>
|
||
/// <param name="site">站点对象</param>
|
||
public override void ArriveAction(Car car, Site site)
|
||
{
|
||
// 判断是否为充电站点
|
||
if (site.fields.TryGetValue("Charge", out var strStationId))
|
||
{
|
||
// 记录到达日志(模拟车辆除外)
|
||
if (!car.name.Contains("模拟"))
|
||
{
|
||
Diagnosis.Post($"{car.name}({car.id})到达充电站点{site.id}");
|
||
}
|
||
}
|
||
else
|
||
{
|
||
Diagnosis.Post($"arrived, site{site.id} is not charge site");
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 车辆离开站点时的处理
|
||
/// 记录车辆离开充电站点的日志
|
||
/// </summary>
|
||
/// <param name="car">车辆对象</param>
|
||
/// <param name="site">站点对象</param>
|
||
public override void LeaveAction(Car car, Site site)
|
||
{
|
||
// 判断是否为充电站点
|
||
if (site.fields.TryGetValue("Charge", out var strStationId))
|
||
{
|
||
// 记录离开日志(模拟车辆除外)
|
||
if (!car.name.Contains("模拟"))
|
||
{
|
||
Diagnosis.Post($"{car.name}({car.id})离开充电站点{site.id}");
|
||
}
|
||
}
|
||
else
|
||
{
|
||
Diagnosis.Post($"left, site{site.id} is not charge site");
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 站点筛选器
|
||
/// 判断站点是否为充电站点
|
||
/// </summary>
|
||
/// <param name="siteId">站点ID</param>
|
||
/// <returns>true=充电站点, false=非充电站点</returns>
|
||
public override bool SiteFilter(int siteId)
|
||
{
|
||
// 如果未屏蔽充电桩交互,返回false
|
||
if (((StandardChargeMissionStatus)status).ShieldInterLock)
|
||
{
|
||
return false;
|
||
}
|
||
|
||
// 检查站点字段中是否包含"Charge"关键字
|
||
var site = SimpleLib.GetSite(siteId);
|
||
return site.fields.Keys.ToList().Any(p => p.Contains("Charge"));
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region 核心业务方法
|
||
|
||
/// <summary>
|
||
/// 启动充电进程
|
||
/// 初始化充电站、创建通讯连接、启动充电业务循环
|
||
/// </summary>
|
||
[MethodMember(Name = "启动进程", Description = "开始处理充电维护进程")]
|
||
public override void Execute()
|
||
{
|
||
|
||
|
||
// 防止重复启动
|
||
if (myStarted)
|
||
{
|
||
MessageBox.Show("充电进程已启动,不可重复启动");
|
||
return;
|
||
}
|
||
status.status = "已启动";
|
||
|
||
|
||
myStarted = true;
|
||
int iteration = 0;
|
||
ChargeStations = new Dictionary<int, AbstractChargeStation>();
|
||
|
||
// 创建充电处理线程
|
||
ChargeThread = new Thread(() =>
|
||
{
|
||
ChargeStations = new Dictionary<int, AbstractChargeStation>();
|
||
|
||
while (true)
|
||
{
|
||
try
|
||
{
|
||
if (status.status.Contains("已停止"))
|
||
{
|
||
break;
|
||
}
|
||
var shieldInterLock = ((StandardChargeMissionStatus)status).ShieldInterLock;
|
||
|
||
// ==================== 步骤1: 初始化充电站 ====================
|
||
// 从充电桩管理配置中获取所有充电桩配置
|
||
var allStationConfigs = ChargeStationHelper.GetAllStationConfigs();
|
||
|
||
// 遍历所有充电桩配置,创建充电站实例
|
||
foreach (var stationConfig in allStationConfigs)
|
||
{
|
||
// 1.1 检查充电桩是否启用
|
||
if (!stationConfig.Enabled)
|
||
{
|
||
Diagnosis.Log($"ChargeStation[{stationConfig.StationId}] is disabled, skip");
|
||
continue;
|
||
}
|
||
|
||
// 1.2 验证站点ID
|
||
if (!stationConfig.SiteId.HasValue || stationConfig.SiteId.Value <= 0)
|
||
{
|
||
Diagnosis.Log($"ChargeStation[{stationConfig.StationId}] has invalid SiteId, skip");
|
||
continue;
|
||
}
|
||
|
||
int siteId = stationConfig.SiteId.Value;
|
||
|
||
// 1.4 验证IP地址格式
|
||
if (string.IsNullOrWhiteSpace(stationConfig.IpAddress) ||
|
||
!IPAddress.TryParse(stationConfig.IpAddress, out var ipAddress))
|
||
{
|
||
Diagnosis.Log($"ERR:ChargeStation[{stationConfig.StationId}] IP[{stationConfig.IpAddress}] is invalid");
|
||
continue;
|
||
}
|
||
|
||
// 1.5 验证端口范围
|
||
int port = stationConfig.Port;
|
||
if (port <= 0 || port > 65535)
|
||
{
|
||
Diagnosis.Log($"ERR:ChargeStation[{stationConfig.StationId}] Port[{port}] is invalid");
|
||
continue;
|
||
}
|
||
|
||
// 1.3 检查站点是否已存在,以及IP/端口是否变更
|
||
if (ChargeStations.ContainsKey(siteId))
|
||
{
|
||
var existingStation = ChargeStations[siteId];
|
||
|
||
// 检查IP或端口是否变更
|
||
if (existingStation.Ip != ipAddress.ToString() ||
|
||
existingStation.Port != port)
|
||
{
|
||
Diagnosis.Log($"ChargeStation[{stationConfig.StationId}] IP/Port changed from [{existingStation.Ip}:{existingStation.Port}] to [{ipAddress}:{port}], recreating connection...");
|
||
|
||
// 先关闭旧连接
|
||
try
|
||
{
|
||
existingStation.CloseCommunication();
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Diagnosis.Log($"WARN:ChargeStation[{stationConfig.StationId}] failed to close old connection: {ex.Message}");
|
||
}
|
||
|
||
// 更新IP和端口
|
||
existingStation.Ip = ipAddress.ToString();
|
||
existingStation.Port = port;
|
||
|
||
// 重新创建通讯连接
|
||
try
|
||
{
|
||
existingStation.CreateCommunication(ipAddress, port);
|
||
Diagnosis.Log($"ChargeStation[{stationConfig.StationId}] connection recreated successfully");
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Diagnosis.Log($"ERR:ChargeStation[{stationConfig.StationId}] failed to recreate connection: {ex.Message}");
|
||
}
|
||
}
|
||
|
||
// 站点已存在且IP/端口未变更,跳过
|
||
continue;
|
||
}
|
||
|
||
// 1.6 根据充电桩类型创建对应的充电站对象
|
||
string chargeTypeString = GetChargeTypeString(stationConfig.Type);
|
||
// 跨程序集解析:充电桩具体类型可能位于卫星插件 dll(StandardScene.Devices.Charge),
|
||
// 不能再用 Type.GetType(简单名,仅当前程序集)。改用内核同款全域类型发现。
|
||
string chargeTypeFullName = "StandardScene.ChargeStationType." + chargeTypeString;
|
||
Type type = SimpleLite.Utils.UiTypeDiscovery.AllTypes()
|
||
.FirstOrDefault(t => t.FullName == chargeTypeFullName);
|
||
|
||
if (type == null)
|
||
{
|
||
Diagnosis.Log($"ERR:ChargeStation[{stationConfig.StationId}] Type[{chargeTypeString}] not found");
|
||
continue;
|
||
}
|
||
|
||
// 1.7 创建充电站实例并配置基本信息
|
||
object chargeStation = Activator.CreateInstance(type);
|
||
((AbstractChargeStation)chargeStation).SiteId = siteId;
|
||
((AbstractChargeStation)chargeStation).Ip = ipAddress.ToString();
|
||
((AbstractChargeStation)chargeStation).Port = port;
|
||
|
||
// 1.8 设置通讯类型(从配置读取,默认TCP)
|
||
if (stationConfig.Type == ChargeStationType.FRLDShort)
|
||
{
|
||
((AbstractChargeStation)chargeStation).CommunicationType = "UDP";
|
||
}
|
||
else
|
||
{
|
||
((AbstractChargeStation)chargeStation).CommunicationType = stationConfig.CommunicationType;
|
||
}
|
||
//((AbstractChargeStation)chargeStation).CommunicationType =
|
||
// string.IsNullOrWhiteSpace(stationConfig.CommunicationType)
|
||
// ? "UDP"
|
||
// : stationConfig.CommunicationType.ToUpper();
|
||
|
||
// 1.9 创建通讯连接
|
||
((AbstractChargeStation)chargeStation).CreateCommunication(ipAddress, port);
|
||
|
||
// 1.10 添加到充电站字典
|
||
ChargeStations.Add(siteId, (AbstractChargeStation)chargeStation);
|
||
|
||
Diagnosis.Log($"ChargeStation ADD: StationId[{stationConfig.StationId}] SiteId[{siteId}] IP[{ipAddress}:{port}] Type[{chargeTypeString}] Comm[{((AbstractChargeStation)chargeStation).CommunicationType}]");
|
||
}
|
||
|
||
// ==================== 步骤2: 初始化UDP服务 ====================
|
||
// 如果有任意充电桩使用UDP通讯,则创建UDP服务
|
||
if (ChargeStations.Any(c => c.Value.CommunicationType == "UDP"))
|
||
{
|
||
UdpService ??= new ChargeUdpService();
|
||
}
|
||
|
||
status.status = $"已启动-循环{iteration++}";
|
||
|
||
// ==================== 步骤3: 充电业务处理循环 ====================
|
||
foreach (var item in ChargeStations.Keys.ToArray()) //移除配置
|
||
{
|
||
var chargeStationSetting = ChargeStationHelper.GetStationBySiteId(item);
|
||
if (chargeStationSetting == null)
|
||
{
|
||
try
|
||
{
|
||
ChargeStations[item].CloseCommunication();
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Diagnosis.Log($"WARN:ChargeStationHelper [{item}] failed to close old connection: {ex.Message}");
|
||
}
|
||
ChargeStations.Remove(item);
|
||
}
|
||
|
||
}
|
||
|
||
//S站点存在配置里没有的,需要移除配置
|
||
var sites = SimpleLib.GetAllSites().Where(s => s.fields.ContainsKey("Charge"));
|
||
foreach (var item in sites)
|
||
{
|
||
if (!ChargeStations.Keys.Contains(item.id))
|
||
{
|
||
//移除站点的配置
|
||
item.fields.Remove("Charge");
|
||
item.fields.Remove("setVoltage");
|
||
item.fields.Remove("setElectricCurrent");
|
||
item.fields.Remove("group");
|
||
Diagnosis.Post($"Charge {item.name}-{item.id} 未在充电管理配置移除参数");
|
||
item.name = "NoName";
|
||
//并关闭对应的连接
|
||
|
||
}
|
||
|
||
|
||
}
|
||
|
||
// 遍历所有已添加的充电站,处理充电业务逻辑
|
||
|
||
|
||
foreach (var chargeStationEntry in ChargeStations)
|
||
{
|
||
var openCharge = 0;
|
||
int siteId = chargeStationEntry.Key;
|
||
var chargeStation = chargeStationEntry.Value;
|
||
var site = SimpleLib.GetSite(siteId);
|
||
if (site == null)
|
||
{
|
||
Console.WriteLine($"从地图中未获取到站点的信息 siteId {siteId}");
|
||
continue;
|
||
}
|
||
var chargeStationSetting = ChargeStationHelper.GetStationBySiteId(siteId);
|
||
if (!chargeStationSetting.Enabled)
|
||
{
|
||
continue;
|
||
}
|
||
//绑定group 添加charge
|
||
if (site != null)
|
||
{
|
||
site.name = chargeStationSetting.Name;
|
||
site.fields["Charge"] = "True";
|
||
site.fields["setVoltage"] = chargeStationSetting.SetVoltage.ToString("0.0");
|
||
site.fields["setElectricCurrent"] = chargeStationSetting.SetElectricCurrent.ToString("0.0");
|
||
if (chargeStationSetting.Enabled)
|
||
{
|
||
site.fields["group"] = chargeStationSetting.GroupCarType.ToString();
|
||
}
|
||
else
|
||
{
|
||
site.fields["group"] = "禁停";
|
||
}
|
||
|
||
|
||
}
|
||
|
||
// 3.1 设置站点访问权限(默认允许进入和离开)
|
||
|
||
if (chargeStationSetting.ChargeMethod == ChargeMethodType.Side)
|
||
{
|
||
SetAllowEnter(siteId, chargeStationSetting.ShieldSiteMechanismStatus || chargeStationSetting.MechanismStatus == MechanismStatus.Retracted);
|
||
|
||
SetAllowExit(siteId, chargeStationSetting.ShieldSiteMechanismStatus || chargeStationSetting.MechanismStatus == MechanismStatus.Retracted);
|
||
SetAcknowledgeLeave(siteId, true);
|
||
|
||
}
|
||
else
|
||
{
|
||
SetAllowEnter(siteId, true);
|
||
SetAllowExit(siteId, true);
|
||
SetAcknowledgeLeave(siteId, true);
|
||
|
||
}
|
||
|
||
|
||
|
||
// 3.2 查找当前在充电站点的车辆
|
||
// 条件:车辆在站点上 或 正在获取站点锁 或 持有站点锁
|
||
var car = SimpleLib
|
||
.GetAllCars()
|
||
.FirstOrDefault(c =>
|
||
c.GetLastSite() == siteId ||
|
||
c.status.aquiringLock == siteId ||
|
||
c.status.holdingLocks.Contains(siteId)
|
||
);
|
||
|
||
// 3.3 如果找到车辆,处理充电逻辑
|
||
if (car != null)
|
||
{
|
||
// 3.3.1 检查车辆状态是否正常
|
||
if (Commons.GetVehicleStatus((Car)car) != VehicleStatus.Normal&&car.fields.ContainsKey("SkipStatus"))
|
||
{
|
||
continue;
|
||
}
|
||
|
||
// 3.3.2 判断车辆是否正在充电
|
||
var charging = car.tags.Contains("charging");
|
||
|
||
// 3.3.3 检查充电条件
|
||
// 条件:未被占用 && 未获取其他锁 && 正在充电标记
|
||
if (!car.tags.Contains("occupied")
|
||
&& car.status.holdingLocks.Length == 1 && car.status.pendingLocks.Length == 0 &&
|
||
charging)
|
||
{
|
||
openCharge = 1; // 允许充电
|
||
|
||
//// 3.3.4 安全检查:验证叉齿是否升起
|
||
//var actualLiftPillar = 1; // 默认已升起
|
||
//if (car.status.enums.TryGetValue("actualLiftPillar", out var liftPillar))
|
||
//{
|
||
// actualLiftPillar = Convert.ToInt32(liftPillar);
|
||
//}
|
||
|
||
//// 如果叉齿未升起,禁止充电
|
||
//if (actualLiftPillar != 1)
|
||
//{
|
||
// openCharge = 0;
|
||
// Console.WriteLine($"{car.id} 小车不满足充电的安全条件 叉齿未抬升");
|
||
// // TODO: 触发报警,通知人员处理
|
||
//}
|
||
}
|
||
|
||
}
|
||
|
||
// 3.3.5 发送充电指令(如果未屏蔽充电桩交互)
|
||
if (!shieldInterLock)
|
||
{
|
||
Diagnosis.Log($"向充电站[{siteId}]发送充电指令, 车辆[{car?.id}], 指令[{openCharge}]","Charge",true);
|
||
chargeStation.SendToChargeStation(openCharge, (Car)car, site);
|
||
}
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Diagnosis.Post(
|
||
$"StandardChargeMission Error: {ExceptionFormatter.FormatEx(ex)}",
|
||
"error"
|
||
);
|
||
}
|
||
|
||
// 等待500ms后进行下一次循环
|
||
Thread.Sleep(500);
|
||
}
|
||
})
|
||
{
|
||
Name = "StandardChargeMission",
|
||
IsBackground = true
|
||
};
|
||
|
||
// 启动充电处理线程
|
||
ChargeThread.Start();
|
||
|
||
// ==================== 步骤4: 启动站点禁用状态上传任务 ====================
|
||
// 定期将禁用站点信息上传到迷毂系统
|
||
Task.Factory.StartNew(() =>
|
||
{
|
||
HttpPostData httpPostData = new HttpPostData();
|
||
|
||
while (true)
|
||
{
|
||
try
|
||
{
|
||
if (status.status.Contains("已停止"))
|
||
{
|
||
break;
|
||
}
|
||
// 4.1 获取所有标记为"unavailable"的站点
|
||
var sites = SimpleLib
|
||
.GetAllSites()
|
||
.Where(site => site.tags.Contains("unavailable"))
|
||
.ToList();
|
||
|
||
// 4.2 收集所有需要禁用的站点ID
|
||
HashSet<int> disabledSites = new HashSet<int>();
|
||
|
||
foreach (var site in sites)
|
||
{
|
||
// 添加当前站点
|
||
disabledSites.Add(site.id);
|
||
|
||
// 4.3 检查并添加关联的必须释放站点(mustFree)
|
||
if (site.fields.ContainsKey("mustFree") &&
|
||
site.mustFree != null &&
|
||
site.mustFree.Length > 0)
|
||
{
|
||
for (int i = 0; i < site.mustFree.Length; i++)
|
||
{
|
||
disabledSites.Add(site.mustFree[i]);
|
||
}
|
||
}
|
||
}
|
||
|
||
// 4.4 上传禁用站点列表到迷毂系统
|
||
Diagnosis.Log($"向迷毂提供禁用站点,共 {disabledSites.Count} 个", "siteIsEnable", true);
|
||
httpPostData.UploadListNode(disabledSites);
|
||
|
||
// 等待500ms后进行下一次上传
|
||
Thread.Sleep(500);
|
||
}
|
||
catch (Exception e)
|
||
{
|
||
Diagnosis.Post($"上传禁用站点失败: {ExceptionFormatter.FormatEx(e)}", "禁用站点");
|
||
}
|
||
}
|
||
}, TaskCreationOptions.LongRunning);
|
||
|
||
// 调用基类Execute方法
|
||
base.Execute();
|
||
}
|
||
|
||
/// <summary>
|
||
/// 关闭充电进程
|
||
/// 停止所有相关线程
|
||
/// </summary>
|
||
[MethodMember(Name = "关闭进程", Description = "关闭充电进程")]
|
||
public void Stop()
|
||
{
|
||
try
|
||
{
|
||
started = false;
|
||
myStarted = false;
|
||
// 先置停止状态,循环体检测到“已停止”后会自行 break
|
||
status.status = "已停止";
|
||
|
||
// 协作式停止:等待工作线程在下一次循环检测标志后退出(不再使用 .NET8 已不支持的 Thread.Abort)
|
||
myThread?.Join(2000);
|
||
ChargeThread?.Join(2000);
|
||
|
||
Diagnosis.Log("充电进程已停止");
|
||
foreach (var item in ChargeStations.Values)
|
||
{
|
||
item.CloseCommunication();
|
||
Diagnosis.Post($"充电进程已停止,关闭充电通讯连接{item.Ip}-{item.Port}");
|
||
}
|
||
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
|
||
Diagnosis.Post($"充电进程已停止 {ex.ToString()}");
|
||
}
|
||
|
||
}
|
||
|
||
/// <summary>
|
||
/// 切换充电桩交互屏蔽状态
|
||
/// true=屏蔽交互,false=允许交互
|
||
/// </summary>
|
||
[MethodMember(Name = "切换充电桩交互状态", Description = "屏蔽/允许充电桩交互")]
|
||
public void ShieldInterLock()
|
||
{
|
||
var currentStatus = ((StandardChargeMissionStatus)status).ShieldInterLock;
|
||
((StandardChargeMissionStatus)status).ShieldInterLock = !currentStatus;
|
||
|
||
string statusText = ((StandardChargeMissionStatus)status).ShieldInterLock ? "已屏蔽" : "已允许";
|
||
Console.WriteLine($"充电桩交互状态: {statusText}");
|
||
Diagnosis.Log($"充电桩交互状态切换为: {statusText}");
|
||
}
|
||
|
||
/// <summary>
|
||
/// 打开充电桩管理界面
|
||
/// 用于配置和监控充电桩
|
||
/// </summary>
|
||
[MethodMember(Name = "打开充电桩管理界面", Description = "打开充电桩管理界面")]
|
||
public void OpenManagementWindow()
|
||
{
|
||
ChargeStationHelper.OpenManagementWindow();
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region 辅助工具方法
|
||
|
||
/// <summary>
|
||
/// 将充电桩类型枚举转换为类型字符串
|
||
/// 用于通过反射创建对应的充电站对象
|
||
/// </summary>
|
||
/// <param name="type">充电桩类型枚举</param>
|
||
/// <returns>充电站类名</returns>
|
||
private string GetChargeTypeString(ChargeStationType type)
|
||
{
|
||
switch (type)
|
||
{
|
||
case ChargeStationType.FRLDTall:
|
||
return "FLChargeStation";
|
||
|
||
case ChargeStationType.FRLDShort:
|
||
return "PCBChargeStation";
|
||
|
||
case ChargeStationType.MuXing:
|
||
return "MuXingChargeStation";
|
||
|
||
default:
|
||
// 默认使用FRLD矮款充电桩
|
||
return "PCBChargeStation";
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 给车辆下发充电任务
|
||
/// 为测试或调试用途,手动给车辆添加shouldCharge标签
|
||
/// </summary>
|
||
[MethodMember(Name = "小车下发充电任务", Description = "给小车下发充电任务")]
|
||
public void addtagshuldcharge()
|
||
{
|
||
// 查找第一个在有效站点上的车辆
|
||
var car = (Car)SimpleLib.GetAllCars()
|
||
.ToList()
|
||
.Find(c => c.GetLastSite() != -1);
|
||
|
||
if (car != null)
|
||
{
|
||
// 添加shouldCharge标签
|
||
Commons.AddOrUpdateTag(car.tags, "shouldCharge", "true");
|
||
Diagnosis.Log($"已为车辆 {car.id} 添加充电任务标签");
|
||
}
|
||
else
|
||
{
|
||
Diagnosis.Log("未找到在有效站点上的车辆");
|
||
}
|
||
}
|
||
|
||
#endregion
|
||
|
||
|
||
}
|
||
}
|