diff --git a/.gitignore b/.gitignore index ca1c7a3..5892a93 100644 --- a/.gitignore +++ b/.gitignore @@ -1,8 +1,7 @@ -# ---> VisualStudio ## Ignore Visual Studio temporary files, build results, and ## files generated by popular Visual Studio add-ons. ## -## Get latest from https://github.com/github/gitignore/blob/main/VisualStudio.gitignore +## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore # User-specific files *.rsuser @@ -91,7 +90,6 @@ StyleCopReport.xml *.tmp_proj *_wpftmp.csproj *.log -*.tlog *.vspscc *.vssscc .builds @@ -295,17 +293,6 @@ node_modules/ # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) *.vbw -# Visual Studio 6 auto-generated project file (contains which files were open etc.) -*.vbp - -# Visual Studio 6 workspace and project file (working project files containing files to include in project) -*.dsw -*.dsp - -# Visual Studio 6 technical files -*.ncb -*.aps - # Visual Studio LightSwitch build output **/*.HTMLClient/GeneratedArtifacts **/*.DesktopClient/GeneratedArtifacts @@ -362,9 +349,6 @@ ASALocalRun/ # Local History for Visual Studio .localhistory/ -# Visual Studio History (VSHistory) files -.vshistory/ - # BeatPulse healthcheck temp database healthchecksdb @@ -377,24 +361,6 @@ MigrationBackup/ # Fody - auto-generated XML schema FodyWeavers.xsd -# VS Code files for those working on multiple tools -.vscode/* -!.vscode/settings.json -!.vscode/tasks.json -!.vscode/launch.json -!.vscode/extensions.json -*.code-workspace - -# Local History for Visual Studio Code -.history/ - -# Windows Installer files from build outputs -*.cab -*.msi -*.msix -*.msm -*.msp - -# JetBrains Rider -*.sln.iml - +build/ +/build/SimpleComposer.exe +/build/plugins/StandardScene.dll diff --git a/DEFENSE_QUESTIONS.md b/DEFENSE_QUESTIONS.md new file mode 100644 index 0000000..6af11f1 --- /dev/null +++ b/DEFENSE_QUESTIONS.md @@ -0,0 +1,797 @@ +# StandardScene 项目答辩综合题 + +**难度等级**:★★★★☆(中高难度) +**预期答题时间**:60-90 分钟 +**评分标准**:架构理解 30% + 代码实现 40% + 问题分析 20% + 创新性 10% + +--- + +## 背景场景 + +你是一个物流仓库的技术负责人,现有以下业务需求: + +### 现状描述 + +仓库中有多台 VDA5050 标准车,目前系统存在以下问题: + +1. **效率问题**:有些任务被标记为"堵塞",车辆无法被正确分配 +2. **公平性问题**:优先级低的任务可能永远无法执行 +3. **可靠性问题**:当 MQTT 连接断开时,系统无法自动恢复 +4. **成本问题**:车辆频繁往返避让点,增加运营成本 + +### 业务指标 + +| 指标 | 目标 | 当前 | +|-----|------|-----| +| 任务通过率 | 100% | 92% | +| 平均等待时间 | <30s | 45s | +| 车辆利用率 | 85% | 72% | +| 系统可用性 | 99% | 94% | + +--- + +## 综合题目(三选一,必答主题题) + +### 【主题题】:智能避障与任务重规划系统设计 + +**题目描述**: + +当前系统在以下场景中表现不佳: + +**场景 1**:取货点被另一台车占用 +``` +时间线: +T0: 任务A(从工位1取货→工位2放货)分配给车C1 +T1: 车C1 前往工位1 取货 +T2: 同时,车C2 的任务指向工位1(C2也要取货) +T3: C2 已抵达工位1,正在取货 +T4: C1 抵达工位1,发现被阻挡,任务堵塞 +``` + +**场景 2**:放货点被占用且无避让点 +``` +T0: 任务B(工位5→工位8)分配给车C3,已取货 +T1: 工位8 被 C4 占用,C3 被迫等待 +T2: C4 的放货逻辑出现故障,一直占用工位8 +T3: C3 无法完成任务,系统卡死 +``` + +**场景 3**:避让点本身被占用 +``` +T0: 避让点9 本来是给C1 用的 +T1: 但 C5 的任务终点恰好是工位9 +T2: C1 无法到达避让点,任务更新失败 +``` + +### 要求 + +你需要设计一个**智能避障与任务重规划系统**,包括以下内容: + +#### A. 系统架构设计(15 分) + +**请完成以下工作**: + +1. **绘制系统架构图**,包含以下组件及其交互关系: + - 避障决策引擎(Obstacle Avoidance Engine) + - 任务重规划模块(Task Rescheduling Module) + - 冲突检测器(Conflict Detector) + - 路权管理器(Right-of-Way Manager) + +2. **定义数据结构**,用于: + - 表示车辆状态转移(State Transitions) + - 存储冲突信息(Conflict Information) + - 记录避障历史(Avoidance History) + +3. **说明各模块的职责**,特别是: + - 如何检测冲突? + - 如何选择避障策略? + - 如何决定任务重规划? + +#### B. 代码实现(40 分) + +**请实现以下代码**(在现有代码框架基础上): + +**B.1 增强的冲突检测器** (15分) + +```csharp +/// +/// 增强的冲突检测类 +/// 需要检测以下场景: +/// 1. 多个车同时到达同一工位(资源冲突) +/// 2. 路径交叉(交通冲突) +/// 3. 避让点不足(空间冲突) +/// 4. 任务优先级冲突 +/// +public class EnhancedConflictDetector +{ + // 待实现:检测资源冲突的方法 + public bool DetectResourceConflict(AbstractDelivery d, AbstractCar car, out AbstractCar[] blockingCars) + { + // TODO: 实现资源冲突检测 + // 返回是否存在冲突,以及冲突的车辆列表 + blockingCars = null; + return false; + } + + // 待实现:检测避让点不足的方法 + public bool IsGiveWayAvailable(AbstractCar car, int targetSiteId, out int availableGiveWaySiteId) + { + // TODO: 实现检查是否有可用避让点 + // 返回是否有可用的避让点,以及避让点的ID + availableGiveWaySiteId = -1; + return false; + } + + // 待实现:获取冲突等级 + public ConflictLevel GetConflictLevel(ConflictInfo conflict) + { + // TODO: 根据冲突信息判断严重程度 + // 返回冲突等级:Low(可以继续等待)、Medium(需要避让)、Critical(需要重规划) + return ConflictLevel.Low; + } + + // 数据结构定义 + public class ConflictInfo + { + public int DeliveryId { get; set; } + public int AssignedCarId { get; set; } + public int[] BlockingCarIds { get; set; } + public int ConflictSiteId { get; set; } // 冲突发生的工位 + public DateTime DetectTime { get; set; } + public string Description { get; set; } + } + + public enum ConflictLevel + { + None = 0, + Low = 1, + Medium = 2, + Critical = 3, + Deadlock = 4 + } +} +``` + +**B.2 任务重规划模块** (15分) + +```csharp +/// +/// 任务重规划模块 +/// 实现多种避障和恢复策略 +/// +public class TaskReschedulingModule +{ + private AbstractChainedDeliveryMission _mission; + + public TaskReschedulingModule(AbstractChainedDeliveryMission mission) + { + _mission = mission; + } + + /// + /// 根据冲突情况选择合适的避障策略 + /// + /// 策略优先级: + /// 1. 推挤任务(被阻挡车执行其他待处理任务) + /// 2. 避让点绕行(被阻挡车前往避让点) + /// 3. 任务切换(当前任务改派给其他车,被阻挡车接其他任务) + /// 4. 任务延迟(等待冲突解除) + /// 5. 强制中止(仅在死锁时使用) + /// + public async Task ResolveConflict( + EnhancedConflictDetector.ConflictInfo conflict, + EnhancedConflictDetector.ConflictLevel level) + { + // TODO: 实现冲突解决逻辑 + // 返回是否成功解决 + + switch (level) + { + case EnhancedConflictDetector.ConflictLevel.Low: + // 低级冲突:等待 + return await WaitForConflictResolution(conflict); + + case EnhancedConflictDetector.ConflictLevel.Medium: + // 中等冲突:尝试推挤或避让 + if (await TryPushAwayTask(conflict)) + return true; + return await GoToGiveWay(conflict); + + case EnhancedConflictDetector.ConflictLevel.Critical: + // 严重冲突:任务重规划 + if (await TrySwitchCar(conflict)) + return true; + if (await TryRescheduleDelivery(conflict)) +return true; + return await GoToGiveWay(conflict); + + case EnhancedConflictDetector.ConflictLevel.Deadlock: + // 死锁:强制中止某个任务 + return ForceBreakDeadlock(conflict); + + default: + return false; + } + } + + /// + /// 尝试推挤任务:让被阻挡车执行其他待处理任务 + /// + /// 条件: + /// - 被阻挡车附近有其他待处理任务 + /// - 这个任务的优先级不能太低 + /// - 不能形成任务链死锁 + /// + /// 返回:是否成功推挤 + /// + private async Task TryPushAwayTask(EnhancedConflictDetector.ConflictInfo conflict) + { + // TODO: 实现推挤逻辑 + return false; + } + + /// + /// 让被阻挡车前往避让点 + /// + /// 条件: + /// - 避让点可达 + /// - 避让点未被占用 + /// + /// 返回:是否成功 + /// + private async Task GoToGiveWay(EnhancedConflictDetector.ConflictInfo conflict) + { + // TODO: 实现避让点逻辑 + return false; + } + + /// + /// 尝试切换车辆:将冲突的任务改派给其他车 + /// + /// 条件: + /// - 任务未取货 + /// - 有其他可用车辆 + /// + /// 返回:是否成功 + /// + private async Task TrySwitchCar(EnhancedConflictDetector.ConflictInfo conflict) + { + // TODO: 实现车辆切换逻辑 + return false; + } + +/// + /// 尝试重新规划任务 + /// + /// 思路: + /// - 将任务分解为多个子任务 + /// - 调整执行顺序 + /// - 更新优先级 + /// + /// 返回:是否成功 + /// + private async Task TryRescheduleDelivery(EnhancedConflictDetector.ConflictInfo conflict) + { + // TODO: 实现任务重规划逻辑 + return false; + } + + /// + /// 等待冲突自动解除 + /// + /// 条件: + /// - 被阻挡车等待时间 < 阈值 + /// - 阻挡车正在移动 + /// + /// 返回:是否成功 + /// + private async Task WaitForConflictResolution(EnhancedConflictDetector.ConflictInfo conflict) + { + // TODO: 实现等待逻辑 + return false; +} + + /// + /// 强制中止某个任务以破坏死锁 + /// + /// 策略: + /// - 找到优先级最低的任务 + /// - 取消该任务 + /// - 释放其持有的资源 + /// + /// 返回:是否成功 + /// + private bool ForceBreakDeadlock(EnhancedConflictDetector.ConflictInfo conflict) + { + // TODO: 实现死锁破坏逻辑 + return false; + } +} +``` + +**B.3 连接恢复机制** (10分) + +```csharp +/// +/// MQTT 连接恢复机制 +/// 在当前 MasterMQTTCommunication 的基础上增强 +/// +public class ResilientMQTTCommunication : MasterMQTTCommunication +{ + private DateTime _lastSuccessfulConnection = DateTime.Now; + private int _connectionFailureCount = 0; + private const int MAX_RETRY_COUNT = 5; +private const int RETRY_INTERVAL_SECONDS = 10; + + /// + /// 启用自动重连机制 + /// 监控连接状态,当断线时自动重连 + /// + public async Task EnableAutoReconnect() + { + // TODO: 实现自动重连逻辑 + // 1. 监听连接状态变化 + // 2. 当断线时,指数退避重试 + // 3. 重连成功后,恢复订阅 + // 4. 同步未发送的消息 + } + + /// + /// 持久化消息队列 +/// 连接断开时,将未发送的消息保存到本地 + /// 恢复连接后,自动重发 + /// + public void EnableMessagePersistence(string persistDir) + { + // TODO: 实现消息持久化 + // 1. 创建消息队列文件 + // 2. 连接断开时,保存消息 + // 3. 恢复连接时,读取并重发 + // 4. 成功发送后,删除文件 + } + + /// + /// 健康检查 + /// 定期向 MQTT broker 发送心跳,检查连接是否正常 + /// + public async Task StartHealthCheck(int intervalSeconds = 30) + { + // TODO: 实现健康检查 + // 1. 创建定时器,每 intervalSeconds 发送一次心跳 + // 2. 如果未收到响应,标记连接异常 + // 3. 触发重连机制 + } +} +``` + +#### C. 问题分析 (20 分) + +**C.1 场景分析** (10 分) + +请分析以下三个真实场景,并说明系统应该如何处理: + +**场景 A:优先级倒挂** +``` +时间 事件 任务状态 +T0 任务A(优先级=5)加入队列 Waiting +T1 任务B(优先级=10)加入队列 Waiting +T2 任务A 被分配给车C1 Fetching +T3 任务B 到达避让点等待 Waiting +T4 车C1 堵塞在取货点(4分钟) Blocked +T5 任务B 等待超过 300 秒,必须执行 必须安排 + +问题:此时应该做什么? +A) 继续等待C1完成? +B) 让C1中止当前任务,让B先执行? +C) 给B分配其他车? +D) 其他方案? + +请说明你的理由,并考虑以下因素: +- 公平性(任务不能无限期等待) +- 效率(减少车辆空闲时间) +- 一致性(系统状态不能出现不一致) +``` + +**场景 B:级联避让** +``` + [工位1] + ↑ + [车C1]←──(被C2阻挡) + / \ + [避1] [工位2] + ↑ + [车C2]←──(被C3阻挡) + / \ + [避2] [工位3] + +问题:C1、C2、C3 都被阻挡,谁应该先让开? + +如何避免以下问题: +- 所有车都跑到避让点,导致避让点满? +- 车辆在避让点之间来回奔波(浪费能源)? +- 形成避让死锁(无法找到有效的避让点链)? +``` + +**场景 C:突发故障恢复** +``` +时间线: +T0 主控向AGV发送订单(包含10个节点) +T1-T4 前3个节点执行成功 +T5 MQTT 连接断开 +T6 主控检测到断线,进行重连 +T7 重连成功,但AGV已执行第5个节点 +T8 主控应该重新同步状态 + +问题: +1. 主控应该重发整个订单还是部分订单? +2. 如何处理已执行但未被主控确认的节点? +3. 如果重发导致节点重复执行怎么办? +4. 如何确保数据一致性? +``` + +**请对每个场景做以下分析**: +1. 识别系统中涉及的关键决策点 +2. 列举可能的处理方案(至少3个) +3. 分析每个方案的优缺点 +4. 给出最终推荐方案及理由 + +--- + +**C.2 性能与可靠性分析** (10 分) + +1. **性能分析**: + - 当系统中有 100 个待处理任务时,调度循环的时间复杂度是多少? + - 如何优化避障搜索以减少平均响应时间? + - 推荐的检查间隔是多少? + +2. **可靠性分析**: + - 系统最多能容忍多少个并发冲突而不死锁? + - 如何检测死锁? + - 死锁恢复的代价是什么? + +3. **可扩展性分析**: + - 当车辆数增加到 100+ 时,系统如何扩展? + - MQTT broker 是否成为瓶颈? + - 建议如何分布式部署? + +#### D. 创新性改进 (10 分) + +**请提出至少 2 项创新改进方案**: + +1. **改进方案 A**: + - 描述问题 + - 提出解决思路 + - 说明实现步骤 + - 预期效果 + +2. **改进方案 B**: + - 描述问题 + - 提出解决思路 + - 说明实现步骤 + - 预期效果 + +**参考方向**: +- 机器学习优化任务分配 +- 图论优化路权分配 +- 预测性避障(提前预防冲突) +- 动态路线更新 +- 能耗优化 +- 多目标优化(吞吐量 vs 能耗 vs 公平性) + +--- + +### 【选择题 1】:MQTT 状态同步机制优化 + +**题目描述**: + +当前 VDA5050Car 的状态同步方式是: +1. VDA5050Car 通过 `keepAlive()` 定期调用 `ProcessCacheAndSendOrderMessage()` +2. 车端通过 MQTT 发送 `stateMessage` +3. `UpdateState()` 接收并更新本地缓存 + +**问题**:这种方式存在以下缺陷: +- 状态更新延迟可能达到 100ms+(keepAlive 间隔) +- 快速变化的状态可能被覆盖 +- 消息顺序性无法保证 + +**请设计一个改进的状态同步机制**,包括: + +1. **架构设计**(10 分): + - 使用事件驱动模式代替轮询 + - 引入状态版本号防止过期状态覆盖 + - 实现状态变更日志 + +2. **实现代码**(20 分): +```csharp +/// +/// 事件驱动的状态管理器 +/// +public class EventDrivenStateManager +{ + private Queue _stateChangeHistory = new(); + private uint _stateVersion = 0; + + public event EventHandler OnStateChanged; + + public struct StateChangeEvent + { + public uint Version { get; set; } + public DateTime Timestamp { get; set; } +public string StateType { get; set; } + public object OldValue { get; set; } + public object NewValue { get; set; } + } + + // TODO: 实现状态版本控制 + // TODO: 实现状态变更通知 + // TODO: 实现状态历史查询 +} +``` + +3. **问题分析**(10 分): + - 新机制如何处理乱序消息? + - 如何避免状态爆炸? + - 实现成本是多少? + +--- + +### 【选择题 2】:车辆故障诊断与自愈系统 + +**题目描述**: + +你需要为 VDA5050Car 设计一个**自动故障诊断与自愈系统**。 + +**常见故障场景**: +- MQTT 连接断开 +- 脚本执行超时 +- 车辆不响应 +- 路径规划失败 +- 死锁状态 + +**请设计一个诊断系统**,包括: + +1. **故障检测** (10 分): + - 定义故障指标(KPI) + - 实现故障识别算法 + - 设计告警规则 + +2. **自愈机制** (20 分): +```csharp +/// +/// 车辆自愈系统 +/// +public class VehicleSelfHealingSystem +{ + public enum FaultType + { + MQTTDisconnected, + ScriptTimeout, + VehicleUnresponsive, + PathPlanningFailed, + DeadLock + } + + public class FaultDiagnosis + { + public FaultType Type { get; set; } + public DateTime DetectTime { get; set; } + public string Description { get; set; } + public int Severity { get; set; } // 0-100 + } + + // TODO: 实现故障诊断 + public FaultDiagnosis Diagnose(Car car) + { + // 分析车辆状态,识别故障 + return null; + } + + // TODO: 实现自愈策略 + public async Task SelfHeal(FaultDiagnosis fault) + { + // 根据故障类型,选择合适的恢复策略 + return false; + } + + // TODO: 实现故障恢复验证 + public bool VerifyHealing(FaultDiagnosis fault) + { + // 验证自愈是否成功 + return false; + } +} +``` + +3. **案例分析** (10 分): + - MQTT 断线如何诊断和恢复? + - 脚本超时如何处理? + - 如何区分临时故障和永久故障? + +--- + +### 【选择题 3】:多目标优化调度算法 + +**题目描述**: + +当前系统的调度目标单一(最近距离优先)。实际上,运营方关心多个目标: + +| 目标 | 说明 | 权重 | +|-----|------|------| +| 吞吐量 | 完成任务数/时间 | 40% | +| 公平性 | 任务等待时间方差 | 30% | +| 能耗 | 车辆行驶距离 | 20% | +| 可靠性 | 避免冲突和死锁 | 10% | + +**请设计一个多目标优化调度算法**: + +1. **算法设计** (15 分): + - 定义目标函数 + - 说明优化方法(贪心/遗传/蚁群等) + - 证明算法收敛性 + +2. **实现代码** (15 分): +```csharp +/// +/// 多目标优化调度器 +/// +public class MultiObjectiveScheduler +{ + public struct ScheduleObjectives + { + public double Throughput { get; set; } // 吞吐量 + public double Fairness { get; set; } // 公平性(1-方差) + public double EnergyEfficiency { get; set; } // 能耗效率 + public double Reliability { get; set; } // 可靠性 + } + + // TODO: 实现多目标评分函数 + public double CalculateScore(ScheduleObjectives obj, Dictionary weights) + { + // 根据权重计算综合评分 + return 0; + } + + // TODO: 实现帕累托前沿搜索 + public List FindParetoOptimalSchedule( + List candidates) + { + // 找到帕累托前沿上的最优调度方案 + return null; + } +} +``` + +3. **权衡分析** (10 分): + - 为什么不能同时最大化所有目标? + - 如何在这些目标之间找到平衡? + - 运营决策者应该如何选择权重? + +--- + +## 评分标准 + +### A. 系统架构(15 分) + +| 评分 | 标准 | +|-----|-----| +| 15 | 架构清晰完整,组件划分合理,交互明确,支持扩展 | +| 12 | 架构基本清晰,大部分组件正确,少数地方欠考虑 | +| 9 | 架构思路正确,但细节不完善,缺少某些重要组件 | +| 6 | 架构基本可行,但设计粗糙,重要细节缺失 | +| 0 | 没有架构或完全错误 | + +### B. 代码实现(40 分) + +| 评分 | 标准 | +|-----|-----| +| 40 | 代码完整可运行,逻辑清晰,异常处理完善,性能良好 | +| 32 | 代码基本完整,核心逻辑正确,少数边界情况未处理 | +| 24 | 代码框架正确,核心逻辑基本实现,缺少优化 | +| 16 | 代码结构合理,但实现不完整,存在明显缺陷 | +| 8 | 代码框架可见,但实现很不完善 | +| 0 | 没有代码或完全无法运行 | + +### C. 问题分析(20 分) + +| 评分 | 标准 | +|-----|-----| +| 20 | 分析全面深入,考虑周全,方案可行性强,论证充分 | +| 16 | 分析基本全面,考虑大部分因素,方案合理 | +| 12 | 分析有深度,但不够全面,某些方案欠妥 | +| 8 | 分析浮表,缺少深入思考,方案可行性一般 | +| 4 | 分析肤浅,考虑不足,方案有问题 | +| 0 | 没有分析或完全错误 | + +### D. 创新性(10 分) + +| 评分 | 标准 | +|-----|-----| +| 10 | 提出的改进方案新颖,技术难度高,应用价值大 | +| 8 | 方案创新,有一定难度,实用性较好 | +| 6 | 方案合理但不够创新,实用性一般 | +| 4 | 方案基本可行,但缺乏创新 | +| 2 | 方案平凡,基本没有创新 | +| 0 | 没有方案或完全无创意 | + +--- + +## 答题建议 + +### 时间分配 + +``` +总时间:90 分钟 + +1. 审题与理解 (5 分钟) +2. A. 架构设计 (15 分钟) +3. B. 代码实现 (40 分钟) + - B.1 冲突检测 (12 分钟) + - B.2 任务重规划 (15 分钟) + - B.3 连接恢复 (13 分钟) +4. C. 问题分析 (20 分钟) +5. D. 创新性改进 (10 分钟) +``` + +### 答题策略 + +1. **优先完成主题题**:主题题分值最高(85 分) +2. **先做框架,后做细节**:先完成整体设计,再补充实现 +3. **代码示例重于完整实现**:关键方法的伪代码比完整但有 bug 的代码更好 +4. **多用图表和表格**:架构图、状态机、时间线等有助于表达 +5. **充分论证方案**:说明"为什么"往往比"如何做"更重要 + +### 常见错误 + +? **错误做法**: +- 只写代码不做设计 +- 过于关注细节,忽视整体架构 +- 没有考虑系统中的并发和同步问题 +- 忽视边界情况和故障处理 +- 完全照搬现有代码,没有创新 + +? **正确做法**: +- 先思考后编码 +- 重视系统设计和权衡 +- 充分考虑并发、故障、扩展性 +- 列举各种情况并给出处理方案 +- 在理解基础上进行创新改进 + +--- + +## 参考资源 + +### 理论基础 + +- 《分布式系统》—— Kleppmann +- 《设计数据密集型应用》 —— Kleppmann +- 多目标优化理论 +- 状态机设计模式 +- 事件驱动架构 + +### 相关技术 + +- MQTT 协议详解 +- C# 异步编程 +- 线程安全与同步 +- 图论算法(最短路径、避障) +- 调度算法(EDF、LLF 等) + +### 项目代码 + +- `VDA5050Car.cs` —— 车型实现参考 +- `AbstractChainedDeliveryMission.cs` —— 任务调度参考 +- `Commons.cs` —— 工具方法参考 +- `MasterMQTTCommunication.cs` —— 通信参考 + +--- + +**祝你答题顺利!** ?? + +如有疑问,请参考 `DEVELOPMENT_GUIDE.md` 开发指导文档。 + +--- + +**题目版本**:1.0 +**难度等级**:★★★★☆ +**预期评审时间**:90 分钟 + diff --git a/DEFENSE_REFERENCE.md b/DEFENSE_REFERENCE.md new file mode 100644 index 0000000..2303f20 --- /dev/null +++ b/DEFENSE_REFERENCE.md @@ -0,0 +1,1159 @@ +# StandardScene 答辩综合题 - 参考答题框架 + +**本文档提供的是答题思路指引,不是标准答案。鼓励基于此框架进行深度思考和创新。** + +--- + +## 主题题答题参考 + +### A. 系统架构设计参考 + +#### A.1 核心组件与交互 + +**建议的架构图框架**: + +``` +┌─────────────────────────────────────────────────────────────┐ +│ 冲突管理层(Conflict Management) │ +│ │ +│ ┌──────────────────┐ ┌─────────────────┐ │ +│ │ 冲突检测器 │───→│ 冲突级别评估 │ │ +│ │ (Detector) │ │ (Classifier) │ │ +│ └────────┬─────────┘ └────────┬────────┘ │ +│ │ │ │ +│ └───────┬───────────────┘ │ +│ ▼ │ +│ ┌─────────────────────────────────────┐ │ +│ │ 策略选择引擎 │ │ +│ │ (Strategy Selector) │ │ +│ └────────────────┬────────────────────┘ │ +│ │ │ +├───────────────────┼──────────────────────────────────────┤ +│ 策略执行层(Strategy Execution) │ +│ │ │ +│ ┌───────┴─────────────────────┬──────────────┐ │ +│ ▼ ▼ ▼ ▼ ▼ │ +│ 推挤 避让 切换 延迟 中止 │ +│ 任务 任务 车辆 处理 任务 │ +│(Push) (GiveWay) (Switch) (Wait) (Abort) │ +│ │ +├──────────────────────────────────────────────────────────┤ +│ 任务调度层(Task Scheduling) │ +│ │ +│ ┌─────────────────┐ ┌──────────────────┐ │ +│ │ 任务队列 │───→│ 优先级管理 │ │ +│ │ (TaskQueue) │ │ (PriorityMgr) │ │ +│ └─────────────────┘ └──────────────────┘ │ +│ │ +├──────────────────────────────────────────────────────────┤ +│ 车辆管理层 / 通信层│ +│ (VDA5050Car / MasterMQTTCommunication) │ +│ │ +└─────────────────────────────────────────────────────────────┘ +``` + +#### A.2 数据结构设计参考 + +**关键数据结构**: + +```csharp +// 1. 冲突信息 +public class ConflictInfo +{ + public int ConflictId { get; set; } + public int DeliveryId { get; set; } + public int AssignedCarId { get; set; } + public int[] BlockingCarIds { get; set; } + public int ConflictSiteId { get; set; } + public ConflictType Type { get; set; }// 资源冲突/路径冲突/空间冲突 + public DateTime DetectTime { get; set; } + public int WaitTime { get; set; } + public string Description { get; set; } +} + +// 2. 避障历史 +public class AvoidanceHistory +{ + public int HistoryId { get; set; } + public int CarId { get; set; } + public int DeliveryId { get; set; } + public AvoidanceStrategy Strategy { get; set; } + public DateTime ExecuteTime { get; set; } + public bool IsSuccessful { get; set; } + public int CostDistance { get; set; } // 额外行驶距离 + public int CostTime { get; set; } // 额外耗时(秒) +} + +// 3. 车辆状态转移 +public class VehicleStateTransition +{ + public int CarId { get; set; } + public VehicleState FromState { get; set; } + public VehicleState ToState { get; set; } + public string Reason { get; set; } + public DateTime Timestamp { get; set; } +} + +public enum VehicleState +{ + Idle, // 空闲 + Fetching, // 取货中 + Transporting, // 运输中 + Putting, // 放货中 + AvoidingObstacle, // 避障中 + GoingToGiveWay, // 前往避让点 + StandbyAtGiveWay, // 在避让点待命 + WaitingForTask, // 等待任务 + Charging, // 充电中 + Faulty // 故障 +} + +// 4. 路权管理 +public class RightOfWayAllocation +{ + public int AllocationId { get; set; } + public int PrimaryCarId { get; set; } // 有路权的车 + public int[] SecondaryCarIds { get; set; } // 让路的车 + public int ResourceSiteId { get; set; } // 争夺的资源 + public DateTime AllocateTime { get; set; } + public int DurationSeconds { get; set; } +} +``` + +--- + +### B. 代码实现参考思路 + +#### B.1 冲突检测器参考实现 + +**核心思路**: + +```csharp +public bool DetectResourceConflict(AbstractDelivery d, AbstractCar car, out AbstractCar[] blockingCars) +{ + blockingCars = null; + var conflictSite = d.src; // 假设冲突发生在取货点 + + // 1. 遍历所有其他车辆 + var otherCars = SimpleLib.GetAllCars().OfType() + .Where(c => c.id != car.id) + .ToArray(); + + // 2. 检查是否有车要到达或已在冲突工位 + var blocking = new List(); + foreach (var otherCar in otherCars) + { + // 检查目标工位是否相同 + if (otherCar.tags.TryGetValue("dest", out var destStr) && + int.TryParse(destStr, out var destId) && + destId == conflictSite) + { + blocking.Add(otherCar); + } + + // 检查当前工位是否是冲突工位 + if (otherCar.siteID == conflictSite) + { + blocking.Add(otherCar); + } + + // 检查是否在前往冲突工位的路径中 + var plan = new SegmentPlan { usingCar = otherCar }; + try + { + // 如果规划的路径经过冲突工位,也算冲突 + if (plan.segments?.Any(seg => seg.id == conflictSite) == true) + { + blocking.Add(otherCar); + } + } + catch { } + } + + if (blocking.Count > 0) + { + blockingCars = blocking.ToArray(); + return true; + } + + return false; +} + +public bool IsGiveWayAvailable(AbstractCar car, int targetSiteId, out int availableGiveWaySiteId) +{ + availableGiveWaySiteId = -1; + + // 1. 找到所有避让点 + var giveWaySites = SimpleLib.GetAllSites() + .Where(s => s.fields.ContainsKey("giveWay") && + s.fields["giveWay"] == "true") + .ToArray(); + + if (giveWaySites.Length == 0) + return false; + + // 2. 检查哪些避让点是可达且未被占用的 + foreach (var giveWaySite in giveWaySites) + { + // 检查避让点是否被占用 + var isOccupied = SimpleLib.GetAllCars().OfType() + .Any(c => c.siteID == giveWaySite.id); + + if (isOccupied) + continue; + + // 检查是否可达 +var plan = new SegmentPlan { usingCar = car }; + try + { + var currentSite = car.GetLastSite(); + if (currentSite == -1) + continue; + + plan.FindRoute( + SimpleLib.GetSite(currentSite), + giveWaySite); + + availableGiveWaySiteId = giveWaySite.id; + return true; + } + catch + { + // 这个避让点不可达,尝试下一个 + continue; + } + } + + return false; +} + +public ConflictLevel GetConflictLevel(ConflictInfo conflict) +{ + // 1. 根据等待时间 + var waitSeconds = (DateTime.Now - conflict.DetectTime).TotalSeconds; + if (waitSeconds > 300) // 等待超过5分钟 +return ConflictLevel.Critical; + + // 2. 根据冲突类型和严重程度 + switch (conflict.Type) + { + case ConflictType.ResourceConflict: + if (conflict.BlockingCarIds.Length > 2) + return ConflictLevel.Critical; +else + return ConflictLevel.Medium; + + case ConflictType.PathConflict: + return ConflictLevel.Medium; + + case ConflictType.SpaceConflict: + // 没有可用避让点 + return ConflictLevel.Critical; + + default: + return ConflictLevel.Low; + } +} +``` + +#### B.2 任务重规划参考思路 + +**策略选择逻辑**: + +```csharp +public async Task ResolveConflict( + EnhancedConflictDetector.ConflictInfo conflict, + EnhancedConflictDetector.ConflictLevel level) +{ + switch (level) + { + case ConflictLevel.Low: +// 低级冲突:只需等待,通常会自动解除 + return await WaitForConflictResolution(conflict); + + case ConflictLevel.Medium: + // 中等冲突:尝试推挤或避让 + if (await TryPushAwayTask(conflict)) + { + Diagnosis.Post($"冲突{conflict.ConflictId}:通过推挤任务解决", "conflict", true); + return true; + } + + if (await GoToGiveWay(conflict)) + { + Diagnosis.Post($"冲突{conflict.ConflictId}:通过避让点解决", "conflict", true); + return true; + } + + // 避让也失败,升级为严重冲突 + return false; + + case ConflictLevel.Critical: + // 严重冲突:尝试车辆切换或任务重规划 + if (await TrySwitchCar(conflict)) + { + Diagnosis.Post($"冲突{conflict.ConflictId}:通过切换车辆解决", "conflict", true); + return true; + } + + if (await TryRescheduleDelivery(conflict)) + { + Diagnosis.Post($"冲突{conflict.ConflictId}:通过重规划任务解决", "conflict", true); + return true; + } + + if (await GoToGiveWay(conflict)) + { +Diagnosis.Post($"冲突{conflict.ConflictId}:通过避让点解决", "conflict", true); + return true; + } + + return false; + + case ConflictLevel.Deadlock: + // 死锁:必须强制中止某个任务 + if (ForceBreakDeadlock(conflict)) + { + Diagnosis.Post($"检测到死锁:已强制中止任务", "deadlock", true); + return true; + } + + return false; + } + + return false; +} + +private async Task TryPushAwayTask(ConflictInfo conflict) +{ + var blockingCar = SimpleLib.GetCar(conflict.BlockingCarIds[0]) as Car; + var delivery = _mission.GetDeliveries().FirstOrDefault(d => d.id == conflict.DeliveryId); + + if (blockingCar == null || delivery == null) + return false; + + // 1. 寻找其他可做的任务 + var alternativeTasks = _mission.GetDeliveries() + .Where(d => d.usingCar == null && // 未分配 + d.GetStatus() == DeliveryStatus.Waiting && // 等待中 + d.priority >= delivery.priority * 0.8) // 优先级相近 + .ToArray(); + + if (alternativeTasks.Length == 0) + return false; + + // 2. 计算距离和可达性 + var bestTask = alternativeTasks[0]; + float bestDistance = float.MaxValue; + + foreach (var task in alternativeTasks) + { + try + { + var plan = new SegmentPlan { usingCar = blockingCar }; + var currentSite = blockingCar.GetLastSite(); + if (currentSite == -1) + continue; + + var distance = plan.FindRoute( + SimpleLib.GetSite(currentSite), + SimpleLib.GetSite(task.src)); + + if (distance < bestDistance) + { + bestDistance = distance; + bestTask = task; + } + } + catch + { + continue; + } + } + + // 3. 如果距离在阈值内,分配该任务 + if (bestDistance <= 10000) // 10000 是推挤距离阈值 + { + bestTask.usingCar = blockingCar; + return true; + } + + return false; +} + +private async Task GoToGiveWay(ConflictInfo conflict) +{ + var car = SimpleLib.GetCar(conflict.AssignedCarId) as Car; + if (car == null) + return false; + + // 1. 检查是否有可用避让点 + if (!_detector.IsGiveWayAvailable(car, conflict.ConflictSiteId, out var giveWaySiteId)) + return false; + + // 2. 规划路径到避让点 + try + { + var plan = new SegmentPlan { usingCar = car }; + var currentSite = car.GetLastSite(); + if (currentSite == -1) + return false; + + plan.FindRoute( + SimpleLib.GetSite(currentSite), + SimpleLib.GetSite(giveWaySiteId)); + + // 3. 更新车辆标签和任务状态 + car.tags.Add("redirect", conflict.DeliveryId.ToString()); + car.tags.Add("dest", giveWaySiteId.ToString()); + + // 4. 编译并执行 + var program = plan.Compile($"GiveWay_{giveWaySiteId}"); + await program.Queue(); + + car.siteID = giveWaySiteId; + return true; + } + catch (Exception ex) + { + Diagnosis.Log($"前往避让点失败: {ex.Message}", "error", true); + return false; + } +} + +private async Task TrySwitchCar(ConflictInfo conflict) +{ + var delivery = _mission.GetDeliveries().FirstOrDefault(d => d.id == conflict.DeliveryId); + if (delivery == null || delivery.skipFetch) // 已取货,不能切换 + return false; + + // 1. 找到其他可用车辆 + var availableCars = SimpleLib.GetAllCars().OfType() + .Where(c => Commons.SelectCar(c) >= 0) // 车辆可用 + .ToArray(); + + if (availableCars.Length == 0) + return false; + + // 2. 选择最近的车 + float bestDistance = float.MaxValue; + Car bestCar = null; + + foreach (var car in availableCars) + { + try + { + var plan = new SegmentPlan { usingCar = car }; +var distance = plan.FindRoute( + SimpleLib.GetSite(car.GetLastSite()), + SimpleLib.GetSite(delivery.src)); + + if (distance < bestDistance) + { + bestDistance = distance; + bestCar = car; + } + } + catch { } + } + + if (bestCar != null) + { + delivery.usingCar = bestCar; + + var oldCar = SimpleLib.GetCar(conflict.AssignedCarId) as Car; + if (oldCar != null) + { + oldCar.tags.Remove("occupied"); + oldCar.tags.Remove("deliver"); + } + + return true; + } + + return false; +} + +private bool ForceBreakDeadlock(ConflictInfo conflict) +{ + // 1. 找到优先级最低的任务 + var deliveries = _mission.GetDeliveries().Where(d => d.IsActive()).ToArray(); + if (deliveries.Length == 0) + return false; + + var lowestPriorityDelivery = deliveries.OrderBy(d => d.priority).First(); + + // 2. 中止该任务 + if (lowestPriorityDelivery.Cancel()) + { + Diagnosis.Log($"已中止任务{lowestPriorityDelivery.id}以破坏死锁", "deadlock", true); + + // 3. 释放该任务占用的资源 + if (lowestPriorityDelivery.usingCar != null) + { + lowestPriorityDelivery.usingCar.tags.Clear(); + lowestPriorityDelivery.usingCar.tags.Add("idle", DateTime.Now.ToString()); + } + + return true; + } + + return false; +} +``` + +#### B.3 连接恢复参考思路 + +```csharp +public class ResilientMQTTCommunication : MasterMQTTCommunication +{ + private DateTime _lastSuccessfulConnection = DateTime.Now; + private int _connectionFailureCount = 0; + private const int MAX_RETRY_COUNT = 5; + private const int RETRY_INTERVAL_SECONDS = 10; + private Queue<(string Topic, string Payload, DateTime Timestamp)> _pendingMessages = new(); + private CancellationTokenSource _healthCheckCts; + + public async Task EnableAutoReconnect() + { + try + { + // 1. 监听连接状态变化 +_client.ConnectedAsync += async e => + { + _connectionFailureCount = 0; + _lastSuccessfulConnection = DateTime.Now; + Diagnosis.Post("MQTT 连接已建立", "mqtt", true); + + // 2. 重连成功后,恢复订阅 + SubsribeToConnectionTopic(); + SubscribeToState(); + SubscribeToVisualization(); + + // 3. 发送待发消息 + await FlushPendingMessages(); + + return; + }; + + _client.DisconnectedAsync += async e => + { + Diagnosis.Post($"MQTT 连接断开", "mqtt", true); + _connectionFailureCount += 1; + + // 4. 指数退避重试 + if (_connectionFailureCount <= MAX_RETRY_COUNT) + { + var backoffSeconds = (int)Math.Min( + RETRY_INTERVAL_SECONDS * Math.Pow(2, _connectionFailureCount - 1), + 300// 最多等待5分钟 + ); + + Diagnosis.Post( + $"将在 {backoffSeconds} 秒后重连 (第 {_connectionFailureCount} 次)...", + "mqtt", + true); + + await Task.Delay(backoffSeconds * 1000); + await RestartClient(); + } + else + { + Diagnosis.Post("连接失败次数过多,请检查网络和 MQTT broker", "mqtt", true); + } + + return; + }; + } + catch (Exception ex) + { + Diagnosis.Log($"启用自动重连失败: {ex.Message}", "error", true); + } + } + + private async Task FlushPendingMessages() + { + while (_pendingMessages.Count > 0) + { +var (topic, payload, timestamp) = _pendingMessages.Dequeue(); + + // 检查消息是否过期(超过5分钟) + if ((DateTime.Now - timestamp).TotalSeconds > 300) + { + Diagnosis.Log($"消息已过期,已删除: {topic}", "mqtt", true); + continue; + } + + try + { + await PublishTo(payload); + Diagnosis.Log($"已补发消息到 {topic}", "mqtt", true); + } + catch (Exception ex) + { + Diagnosis.Log($"补发消息失败: {ex.Message}", "error", true); + // 重新加入队列 + _pendingMessages.Enqueue((topic, payload, timestamp)); + break; + } + } + } + + public void EnableMessagePersistence(string persistDir) + { + if (!Directory.Exists(persistDir)) + Directory.CreateDirectory(persistDir); + + // 1. 连接断开时,保存消息 + var originalPublishTo = PublishTo; + PublishTo = async (payload) => + { + try + { + await originalPublishTo(payload); + } + catch + { + // 保存到文件 + var fileName = Path.Combine( + persistDir, + $"message_{DateTime.Now:yyyyMMdd_HHmmss_fff}.json" + ); + File.WriteAllText(fileName, payload); + + _pendingMessages.Enqueue(("vda5050/order", payload, DateTime.Now)); + } + }; + + // 2. 启动时,读取待发消息 + var files = Directory.GetFiles(persistDir, "message_*.json"); + foreach (var file in files) + { + try + { + var payload = File.ReadAllText(file); + _pendingMessages.Enqueue(("vda5050/order", payload, File.GetCreationTime(file))); + } + catch { } + } + } + + public async Task StartHealthCheck(int intervalSeconds = 30) + { + _healthCheckCts = new CancellationTokenSource(); + + var healthCheckTask = new Task(async () => + { + while (!_healthCheckCts.Token.IsCancellationRequested) + { + try + { + if (!_client.IsConnected) + { + Diagnosis.Post("健康检查:连接已断开", "mqtt", true); + await RestartClient(); + } + else + { + // 5. 检查最后一条消息的时间戳 + var timeSinceLastMessage = DateTime.Now - _lastSuccessfulConnection; +if (timeSinceLastMessage.TotalSeconds > intervalSeconds * 3) + { + Diagnosis.Post( + $"长时间未收到消息 ({timeSinceLastMessage.TotalSeconds:0.0}s)", + "mqtt", + true); + + // 可能是单向断连,尝试重连 + await RestartClient(); + } + } + + await Task.Delay(intervalSeconds * 1000, _healthCheckCts.Token); + } + catch (OperationCanceledException) + { + break; + } + catch (Exception ex) + { + Diagnosis.Log($"健康检查异常: {ex.Message}", "error", true); + } + } + }); + + healthCheckTask.Start(); + } +} +``` + +--- + +### C. 问题分析参考答题框架 + +#### C.1 场景 A:优先级倒挂 - 参考分析 + +| 方案 | 说明 | 优点 | 缺点 | 推荐度 | +|-----|------|-----|-----|--------| +| **A)继续等待** | C1完成当前任务 | 简单,不改变系统状态 | 违反公平性原则,B永远无法执行 | ? 不推荐 | +| **B)中止C1任务** | 取消C1任务,释放资源 | 满足必须执行规则 | 丢弃已投入的工作,浪费资源,C1返空 | ?? 谨慎使用 | +| **C)分配其他车** | 给B分配其他可用车 | 保证任务继续进行 | 需要有可用车,否则无效 | ? 首选 | +| **D)推荐方案** | 动态优先级 + 等待补偿 | 公平且高效 | 实现复杂 | ? 最优 | + +**推荐答案框架**: + +``` +场景分析: +- 任务A已在执行(已投入资源) +- 任务B等待超过阈值(优先级升级) +- 系统状态:C1被阻挡,无其他车可用 + +最优方案:动态优先级 + 等待补偿机制 + +实现步骤: +1. 监控任务等待时间 +2. 当等待超过 300 秒时,自动升级优先级 +3. 评估当前任务的预期完成时间 +4. 如果 A 的剩余时间 > B 的升级等待时间,则中止 A +5. 否则继续执行 A,同时尝试分配其他车给 B + +伪代码: +if (B.waitTime > threshold && B.priority_upgraded) +{ + if (A.remaining_time > B.accumulated_wait_time) + { + // 中止 A,执行 B + A.Cancel(); + // 分配 B 给其他车 + } + else + { + // 继续 A,为 B 找其他车 + if (FindAvailableCar(B)) + AssignCar(B); + } +} + +优点: +? 保证公平性(任务不会无限期等待) +? 最大化系统效率(优先完成高优先级任务) +? 最小化浪费(只在必要时中止) + +缺点: +? 中止任务会导致已取货的物料需要放回 +? 可能形成任务链复杂度 + +结论: +这是公平性和效率的良好平衡。建议采用。 +``` + +#### C.2 场景 B:级联避让 - 参考分析 + +**问题树分析**: + +``` + 级联避让问题 + │ + ┌─────────────┼─────────────┐ + │ │ │ + 避让点满资源浪费 避让死锁 + (Space) (Time/Energy) (Deadlock) + + ↓ ↓ ↓ + 怎么办? 怎么办? 怎么办? +``` + +**参考答案框架**: + +``` +问题1:避让点满 +━━━━━━━━━━━━━━━━ +原因:所有车都涌向少数几个避让点 + +解决方案: +1. 增加避让点数量(规划优化) +2. 分布式避让(让车避让到不同方向) +3. 避让点预约制(提前锁定避让点资源) +4. 多级避让(临时避让点 → 标准避让点) + +建议实现: +- 将避让点分为"A区避让"和"B区避让" +- 根据冲突位置动态分配避让点 +- 如果避让点即将满,触发推挤策略 + +代码框架: +class DistributedGiveWayStrategy +{ + public Site SelectGiveWay(Car car, int conflictSite) + { + var candidateSites = GetGiveWaySitesByRegion(conflictSite); + + // 选择最近且未被占用的避让点 + return candidateSites + .Where(s => !IsOccupied(s)) + .OrderBy(s => Distance(car, s)) + .FirstOrDefault(); + } +} + +问题2:资源浪费 +━━━━━━━━━━━━━━━━ +原因:车辆在避让点之间来回奔波 + +解决方案: +1. 锁定避让路径(一旦选择避让点,不再改变) +2. 并行避让(多个车同时从不同方向避让) +3. 避让点等待(在避让点等待而不是往返) +4. 能耗预测(预测避让成本,选择最低成本方案) + +建议实现: +- 为每个避让操作计算成本(距离+时间+能耗) +- 选择成本最低的方案 +- 避免重复避让同一车 + +代码框架: +class EfficientAvoidanceCalculator +{ + public double CalculateAvoidanceCost(Car car, Site target) + { + var distance = GetDistance(car, target); + var time = distance / car.speed; + var energy = distance * car.powerConsumption; + + return distance * 0.5 + time * 0.3 + energy * 0.2; // 加权评分 + } +} + +问题3:避让死锁 +━━━━━━━━━━━━━━━━ +原因:C1→避让1→被C2阻挡→避让2→被C3阻挡→避让1→死锁 + +解决方案: +1. 避让链路唯一性:避免重复访问同一避让点 +2. 避让超时:如果避让操作超时,强制中止并选择其他策略 +3. 避让路径验证:规划前检查避让路径是否会形成循环 +4. 资源预约:预先锁定整条避让路径 + +建议实现: +class DeadlockFreeAvoidance +{ + private Set visitedSites = new(); // 已访问的避让点 + + public Site SelectGiveWay(Car car, int conflictSite) + { + var candidates = GetGiveWaySites() + .Where(s => !visitedSites.Contains(s.id)) // 不重复访问 + .ToArray(); + + if (candidates.Length == 0) + { + // 所有避让点都被访问过,说明形成了死锁 + return BreakDeadlock(car, conflictSite); + } + + var selected = SelectBestSite(candidates); + visitedSites.Add(selected.id); + return selected; + } +} + +综合解决方案: +━━━━━━━━━━━━━━━━ +1. 分布式避让点 + 2. 成本优化 + 3. 死锁检测 + 4. 自动中止 + +流程: +┌─ 检测冲突 +│ +├─ 选择避让点 +│ ├─ 按地区分配(分散压力) +│ ├─ 按成本排序(优化资源) +│ └─ 检查死锁风险(预防问题) +│ +├─ 发送避让指令 +│ └─ 设置超时(防止无限期等待) +│ +└─ 监控避让过程 + ├─ 成功 → 继续 + ├─ 超时 → 尝试推挤 + └─ 死锁 → 强制中止 +``` + +--- + +### D. 创新性改进参考方向 + +#### 改进方向 1:机器学习优化任务分配 + +``` +问题:现有算法基于简单的距离优化,不考虑: +- 历史执行成功率 +- 车辆性能差异 +- 工位的繁忙程度 +- 时间序列的规律性 + +解决思路: +1. 使用强化学习(Q-Learning) +- 状态:车辆位置、任务队列、车辆状态 + - 动作:选择某个车+某个任务的配对 + - 奖励:任务完成速度、避免冲突、能耗效率 + +2. 数据收集:记录每个任务的执行历史 + - 指派车型 + - 执行时间 + - 是否发生冲突 + - 最终成功/失败 + +3. 模型训练:定期重训练模型以适应环境变化 + - 每小时重训一次 + - 积累一周数据进行离线评估 + +预期效果: +? 任务完成率提升 5-10% +? 平均等待时间减少 15% +? 冲突发生率降低 20% + +实现复杂度:★★★☆☆ +``` + +#### 改进方向 2:预测性避障 + +``` +问题:现有系统是反应式(问题发生后才处理),不够主动 + +解决思路: +1. 路径冲突预测 + - 在规划路径时,预测未来30秒内的所有车辆位置 + - 如果发现潜在冲突,提前调整路线 + +2. 优先级预测 + - 根据工位繁忙程度,预测哪些任务可能堵塞 + - 提前调整这些任务的优先级 + +3. 避让点需求预测 + - 预测接下来30秒有多少车可能需要避让 + - 提前释放避让点资源 + +代码框架: +class PredictiveAvoidanceSystem +{ + public bool PredictConflict(SegmentPlan plan, int lookAheadSeconds = 30) + { + // 1. 获取规划的路径 + var path = plan.segments; + + // 2. 预测车辆的未来位置 + var futurePositions = new Dictionary(); + foreach (var car in GetAllCars()) + { + var predictedSite = PredictCarPosition(car, lookAheadSeconds); + futurePositions[car.id] = predictedSite; + } + + // 3. 检查规划路径是否会与预测位置冲突 + foreach (var segment in path) + { + if (futurePositions.Values.Any(s => s.id == segment.id)) + { + return true; // 发现潜在冲突 + } + } + + return false; + } + + private Site PredictCarPosition(Car car, int seconds) + { + // 基于车的速度和目标,预测位置 + var currentSpeed = car.speed; + var distanceTraveled = currentSpeed * seconds; + + // 简单预测:沿着当前路线继续前进 + var pendingLocks = car.status.pendingLocks; + if (pendingLocks.Length == 0) + return SimpleLib.GetSite(car.siteID); + + // 计算会在哪个工位 + // ... + return pendingLocks.Last(); + } +} + +预期效果: +? 冲突检测提前30秒 +? 避障成功率从 95% 提升到 99% +? 系统响应更主动 + +实现复杂度:★★★★☆ +``` + +#### 改进方向 3:多目标优化框架 + +``` +问题:现有系统只优化距离,忽视吞吐量、能耗、公平性等 + +解决思路:使用帕累托最优(Pareto Optimality) + +class MultiObjectiveOptimizer +{ + public List FindParetoFrontier( + List tasks, + Dictionary weights) + { + var paretoFront = new List(); + + // 1. 枚举所有可能的分配方案 + var allAssignments = GenerateAllAssignments(tasks); + + // 2. 评估每个方案的多个目标 + foreach (var assignment in allAssignments) + { + var evaluation = Evaluate(assignment); + + // 3. 检查是否被现有方案支配 + var isDominated = paretoFront.Any(existing => + existing.throughput >= evaluation.throughput && + existing.fairness >= evaluation.fairness && + existing.energyEfficiency >= evaluation.energyEfficiency && + existing.reliability >= evaluation.reliability + ); + + if (!isDominated) +{ + paretoFront.Add(assignment); + } + } + + // 4. 根据权重选择最终方案 + return paretoFront + .OrderByDescending(a => CalculateWeightedScore(a, weights)) + .ToList(); + } +} + +预期效果: +? 支持多种运营目标 +? 提高决策的透明性和可控性 +? 系统更加灵活 + +实现复杂度:★★★★☆ +``` + +--- + +## 常见答题错误与改进 + +### ? 常见错误 1:忽视线程安全 + +**错误示例**: +```csharp +// 错误:竞态条件 +if (ConflictExists(d.src)) // 检查 +{ + AssignDelivery(d); // 在此期间其他线程可能改变了状态 +} +``` + +**正确做法**: +```csharp +lock (syncLock) +{ + if (ConflictExists(d.src)) + { + AssignDelivery(d); + } +} +``` + +### ? 常见错误 2:没有考虑回滚 + +**错误示例**: +```csharp +// 错误:分配任务后规划失败,但不回滚 +delivery.usingCar = car; +try +{ + plan.FindRoute(...); +} +catch { } // 异常吞没,车辆未释放 +``` + +**正确做法**: +```csharp +try +{ + plan.FindRoute(...); + delivery.usingCar = car; // 成功后才分配 +} +catch +{ + // 规划失败,不分配车辆 + delivery.usingCar = null; +} +``` + +### ? 常见错误 3:避障死循环 + +**错误示例**: +```csharp +// 错误:可能形成死循环 +while (ConflictExists(d.src)) +{ + TryAvoid(d); // 避障可能失败,一直循环 +} +``` + +**正确做法**: +```csharp +// 设置重试次数上限和超时 +int retries = 0; +var timeout = DateTime.Now.AddSeconds(300); + +while (ConflictExists(d.src) && retries < MAX_RETRIES && DateTime.Now < timeout) +{ + if (!TryAvoid(d)) + break; // 避障失败,退出 + retries++; +} + +if (ConflictExists(d.src)) +{ + // 所有尝试都失败,中止任务或升级策略 + BreakDeadlock(d); +} +``` + +--- + +## 答题检查清单 + +在提交答案前,请检查: + +- [ ] **架构设计** + - [ ] 包含所有关键组件 + - [ ] 组件间交互清晰 + - [ ] 支持扩展性 + - [ ] 有清晰的数据流向 + +- [ ] **代码实现** + - [ ] 核心方法有实现(非仅伪代码) + - [ ] 考虑了异常处理 + - [ ] 考虑了并发安全 + - [ ] 有适当的日志输出 + - [ ] 变量命名清晰 + +- [ ] **问题分析** + - [ ] 列举了多个解决方案 + - [ ] 分析了方案的优缺点 + - [ ] 给出了明确的推荐 + - [ ] 考虑了现实约束 + +- [ ] **创新性** + - [ ] 提出了至少2项改进 + - [ ] 改进方案具体可行 + - [ ] 说明了预期效果 + - [ ] 评估了实现成本 + +--- + +**祝你答题成功!** ?? + diff --git a/DEVELOPMENT_GUIDE.md b/DEVELOPMENT_GUIDE.md new file mode 100644 index 0000000..33b7177 --- /dev/null +++ b/DEVELOPMENT_GUIDE.md @@ -0,0 +1,273 @@ +锘# StandardScene 寮鍙戞寚鍗 + +## 1. 椤圭洰瀹氫綅 + +`StandardScene` 鏄竴涓敱 `SimpleComposer.exe` 瀹夸富鍔犺浇鐨勫満鏅彃浠跺簱锛岃緭鍑轰负 `StandardScene.dll`锛屼笉鏄嫭绔 EXE銆備粨搴撲富瑕侀潰鍚 AGV/AMR 鍦哄唴璋冨害涓庤仈鍔ㄦ帶鍒讹紝瑕嗙洊锛 + +- 鎼繍浠诲姟涓庣幆绾夸换鍔 +- 鍖哄煙娴佹帶涓庝氦閫氫簰閿 +- 鍏呯數绛栫暐涓庡厖鐢垫々绠$悊 +- 闂ㄧ鑱斿姩涓庡畨鍏ㄤ俊鍙 +- HTTP / MQTT / Modbus 绛夊鍥存帴鍙 + +## 2. 鎶鏈笌杩愯鏂瑰紡 + +| 椤圭洰椤 | 璇存槑 | +| --- | --- | +| 璇█ | `C#` | +| 妗嗘灦 | `.NET Framework 4.8` | +| 宸ョ▼绫诲瀷 | `Library` | +| 瀹夸富 | `SimpleComposer.exe` | +| 鍏抽敭鍏ュ彛 | `MissionType`銆乣CarType`銆乣WebApi.cs` | + +### 鏈満渚濊禆 + +宸ョ▼鏂囦欢閲屽彲瑙佷互涓嬪浐瀹氫緷璧栬矾寰勶細 + +- `D:\MDCS\Dependencies\Commons\CommonUsage.dll` +- `D:\MDCS\Dependencies\deps\LessokajiWeaverUtilities.dll` +- `D:\MDCS\Dependencies\Commons\MDCSToolBox.dll` +- `D:\MDCS\Dependencies\Simple\RefSimpleCore.dll` +- `D:\MDCS\Executables\Simple\SimpleComposer.exe` + +### 鏋勫缓涓庤繍琛 + +1. 鎵撳紑 `StandardScene.sln` +2. 缂栬瘧 `Debug|Any CPU` 鎴 `Release|Any CPU` +3. 缂栬瘧鍚庯紝鏋勫缓浜嬩欢浼氭妸 `StandardScene.dll` 澶嶅埗鍒 `build\plugins` +4. 杩愯 `build\SimpleComposer.exe` + +## 3. 鐩綍缁撴瀯 + +| 璺緞 | 浣滅敤 | +| --- | --- | +| `CarTypes\` | 鍚勮溅鍨嬩笌鍗忚閫傞厤 | +| `Chained\` | 鎼繍浠诲姟銆侀摼寮忚皟搴︺佺幆绾夸换鍔 | +| `Charge\` | 鍏呯數绛栫暐銆佸厖鐢垫々绠$悊 | +| `ChargeStationType\` | 鍏蜂綋鍏呯數妗╃被鍨 | +| `InterLock\` | 鍖哄煙浜掗攣銆佷氦閫氭帶鍒 | +| `Scheduler\` | 蹇冭烦銆佸畨鍏ㄣ佸尯鍩熸祦鎺х瓑鍚庡彴 Mission | +| `ExtendDevice\Door\` | 闂ㄧ璁惧鎺ュ叆涓庤仈鍔 | +| `Model\` | 浠诲姟銆侀厤缃佸湴鍥剧瓑鏁版嵁妯″瀷 | +| `TCP\` / `Utils\` | TCP銆丣SON銆乄eb API銆丮odbus 宸ュ叿 | +| `Commons.cs` | 閫氱敤瀛楁銆佹爣绛俱侀夎溅銆佽矾寰勮緟鍔 | +| `WebApi.cs` | 瀵瑰 HTTP 鎺ュ彛 | + +## 4. 杩愯鏃舵灦鏋 + +杩愯閾捐矾閫氬父鏄細 + +1. 瀹夸富鍚姩骞舵壂鎻 `build\plugins` +2. 鍔犺浇 `StandardScene.dll` +3. 閫氳繃鐗规у弽灏勮瘑鍒 `MissionType` 涓 `CarType` +4. 鍚姩鍏蜂綋 Mission +5. Mission 璋冪敤 `SimpleLib`銆乣TrafficControl`銆乣SegmentPlan` 绛夋牳蹇冭兘鍔 +6. 杞﹁締鐘舵併佷氦閫氱姸鎬併佸厖鐢电姸鎬佸湪杩愯鏃舵寔缁仈鍔 + +## 5. 鏍稿績妯″潡鐞嗚В + +### `Scheduler` + +杩欐槸鏈閫傚悎鍏ラ棬鐨勭洰褰曘 + +- `HeartBeatMission.cs`锛氭渶灏忕嚎绋嬪紡 Mission +- `RegionalTrafficControlMission.cs`锛氫簨浠惰闃呭瀷 Mission +- `SecuritySignalMission.cs`锛氬畨鍏ㄤ俊鍙风被浠诲姟 + +### `Chained` + +涓讳笟鍔¤皟搴︾殑鏍稿績鍖哄煙銆 + +- `AbstractChainedDeliveryMission.cs`锛氭惉杩愪换鍔℃绘帶 +- `TransportMission.cs`锛氬父瑙勮繍杈 Mission +- `AbstractLoopMission.cs`锛氱幆绾夸换鍔¢鏋 +- `LoopMission.cs`锛氱幆绾夸笟鍔″疄渚 + +### `Charge` + +璐熻矗鑳介噺涓庡厖鐢靛崗鍚屻 + +- `AbstractChargeLogicMission.cs` +- `StandardChargeMission.cs` +- `ChargeStationDataService.cs` +- `ChargeStationManagementExample.cs` + +### `CarTypes` + +璐熻矗杞﹀瀷涓庡崗璁傞厤銆 + +- `VDA5050Car.cs` +- `Forklift.cs` +- `Kiva.cs` +- `MultiVehicleCar.cs` +- `MasterMQTTCommunication.cs` + +### `WebApi.cs` + +瀵瑰鏆撮湶 HTTP 鑳藉姏锛屽父瑙佽矾鐢卞寘鎷細 + +- `/car/createTask` +- `/car/getAllCars` +- `/car/goSite` +- `/map/getMap` +- `/task/getTask` +- `/mission_reflection/get_mission_list` + +## 6. 閰嶇疆鏂囦欢 + +| 鏂囦欢 | 浣滅敤 | +| --- | --- | +| `Config\traffic.json` | 浜ら氫簰閿 / 鍖哄煙閰嶇疆 | +| `Config\ChargeStations.json` | 鍏呯數妗╁畾涔 | +| `Config\ChargeStrategyConfig.json` | 鍏呯數绛栫暐 | +| `Config\AlarmConfigs.json` | 鎶ヨ閰嶇疆 | +| `DoorConfig.json` | 闂ㄧ閰嶇疆 | +| `tasklist.json` | 鐜嚎浠诲姟閰嶇疆 | +| `simple.json` | 瀹夸富鍩虹閰嶇疆 | + +闄や簡 JSON 鏂囦欢锛屾湰浠撳簱杩樺ぇ閲忎娇鐢 `fields` 涓 `tags` 浣滀负杞婚噺閰嶇疆鍏ュ彛锛屽挨鍏舵槸绔欑偣涓庤溅杈嗚涓烘帶鍒躲 + +## 7. 蹇熷紑濮嬫渚 + +### 妗堜緥 A锛氭柊澧炰竴涓渶灏 Mission + +鏈鎺ㄨ崘鐨勬柊鎵嬪叆闂ㄦ渚嬶紝鐩存帴鍙傝 `Scheduler\HeartBeatMission.cs`銆 + +```csharp +using System.Threading; +using Newtonsoft.Json; +using SimpleComposer.RCS; +using SimpleCore; + +namespace StandardScene.Scheduler +{ + [MissionType(Name = "Hello Mission", editor = typeof(HelloMission))] + [I18N.DocumentTranslation(Name = "Hello Mission", locale = "en")] + public class HelloMission : Mission + { + [JsonIgnore] private bool _started; + [JsonIgnore] private Thread _thread; + + public static Mission Create() + { + return new HelloMission(); + } + + public override void Execute() + { + if (_started) return; + _started = true; + status.status = "宸插惎鍔"; + + _thread = new Thread(() => + { + int count = 0; + while (_started) + { + Thread.Sleep(1000); + count++; + status.status = $"tick:{count}"; + } + }); + _thread.Start(); + } + + public void Stop() + { + _started = false; + status.status = "宸插仠姝"; + } + } +} +``` + +#### 鍏抽敭鎻愰啋 + +褰撳墠宸ョ▼鏄棫寮 `.csproj`锛屾柊澧 `.cs` 鏂囦欢鍚庡繀椤荤‘璁ゆ枃浠跺凡鍔犲叆宸ョ▼锛涘惁鍒欐枃浠跺瓨鍦ㄤ絾涓嶄細鍙備笌缂栬瘧銆傚繀瑕佹椂鎵嬪伐琛ワ細 + +```xml + +``` + +#### 楠岃瘉鏂瑰紡 + +1. 缂栬瘧瑙e喅鏂规 +2. 鎵撳紑 `build\SimpleComposer.exe` +3. 鍚姩 `Hello Mission` +4. 瑙傚療 `status.status` 鏄惁鍙樻垚 `tick:1`銆乣tick:2` + +### 妗堜緥 B锛氶厤缃尯鍩熸祦鎺 + +璇ユ渚嬪搴 `Scheduler\RegionalTrafficControlMission.cs`銆 + +缁欏尯鍩熷唴绔欑偣娣诲姞瀛楁锛 + +```text +Region1 = 1 +``` + +鍚箟鏄細 + +- 绔欑偣灞炰簬 `Region1` +- `Region1` 鏈澶氬厑璁 1 鍙拌溅杩涘叆 + +#### 瀹為獙姝ラ + +1. 缁欏悓涓鍖哄煙鍐呭涓珯鐐瑰姞涓 `Region1=1` +2. 鍚姩鈥滃尯鍩熸祦閲忕洃鎺р Mission +3. 璁╀袱鍙拌溅鍏堝悗杩涘叆璇ュ尯鍩 +4. 瑙傚療绗簩鍙拌溅鏄惁琚樆姝 +5. 鏌ョ湅 Mission 鐘舵佷腑鐨勫尯鍩熺粺璁′笌鎷︽埅娆℃暟 + +## 8. 寮鍙戝伐浣滄祦寤鸿 + +1. 鍏堝垽鏂姛鑳藉睘浜 `Scheduler`銆乣Chained`銆乣Charge`銆乣CarTypes` 杩樻槸 `WebApi.cs` +2. 鎵炬渶鎺ヨ繎鐨勭幇鏈夌被浣滀负妯℃澘 +3. 鏄庣‘閰嶇疆鍏ュ彛鏄 JSON銆乣fields` 杩樻槸 `tags` +4. 琛ラ綈鏃ュ織銆佺姸鎬佷笌鍋滄閫昏緫 +5. 纭鏂囦欢宸插姞鍏ュ伐绋 +6. 缂栬瘧鍚庡湪瀹夸富閲岄獙璇佹槸鍚﹁兘琚瘑鍒 + +## 9. 璋冭瘯寤鸿 + +浼樺厛瑙傚療杩欎簺鐐癸細 + +- `status.status` +- `Diagnosis.Post` / `Diagnosis.Log` +- `car.status.pendingLocks` +- `car.status.holdingLocks` +- 绔欑偣 / 杞﹁締鐨 `fields` 涓 `tags` +- `WebApi.cs` 涓殑瀹為檯璺敱 + +鎺ㄨ崘璋冭瘯鏂瑰紡锛 + +- 浠 `build\SimpleComposer.exe` 浣滀负澶栭儴绋嬪簭鍚姩璋冭瘯 +- 鎴栧厛杩愯瀹夸富锛屽啀闄勫姞杩涚▼ + +## 10. 甯歌鍧 + +### 鏂板 Mission 鐪嬩笉鍒 + +浼樺厛妫鏌ワ細 + +- 鏄惁鍔犱簡 `MissionType` +- 鏄惁鏈夐潤鎬 `Create()` +- 鏄惁澶嶅埗鍒颁簡 `build\plugins` +- 鏄惁宸茬粡鍔犲叆 `.csproj` + +### 鍖哄煙娴佹帶涓嶇敓鏁 + +浼樺厛妫鏌ワ細 + +- 瀛楁鍚嶆槸鍚︿互 `Region` 寮澶 +- 瀛楁鍊兼槸鍚﹁兘瑙f瀽涓烘暣鏁 +- Mission 鏄惁宸插惎鍔 + +### 浠诲姟涓嶆墽琛 + +浼樺厛妫鏌ワ細 + +- 杞﹁締鏄惁鍦ㄧ嚎 +- 璺緞鏄惁鍙揪 +- 鏄惁琚簰閿併佹祦鎺ф垨闂ㄦ帶鎷︽埅 +- 鏄惁宸叉湁鏍囩灏嗚溅杈嗘爣璁颁负蹇欑鎴栧厖鐢典腑 \ No newline at end of file diff --git a/DocumentHub.html b/DocumentHub.html new file mode 100644 index 0000000..137ffce --- /dev/null +++ b/DocumentHub.html @@ -0,0 +1,879 @@ +锘 + + + + + + StandardScene 鏂囨。涓績 + + + +
+
+
+
+
SS
+
+

StandardScene Documentation Hub

+

鏈湴鍙洿鎺ユ墦寮 路 UTF-8 路 涓枃 / English / emoji 鉁

+
+
+ +
+
+ +
+
宸ヤ笟绱枃妗i棬鎴 路 Offline Ready 路 No Deploy Needed
+

StandardScene 寮鍙戞寚鍗椾笌蹇熷紑濮

+

+ 鏈〉闈㈠凡缁忔妸寮鍙戞寚鍗椼佹ā鍧楀湴鍥俱佹瀯寤烘柟寮忋佸揩閫熷紑濮嬫渚嬪拰甯歌鎺掗敊鏁村悎鍒颁竴涓 HTML 鏂囦欢涓 + 浣犲彲浠ョ洿鎺ュ弻鍑 `DocumentHub.html` 鎵撳紑锛屾棤闇閮ㄧ讲銆佹棤闇鑱旂綉銆佹棤闇棰濆璧勬簮銆 +

+ +
+
+
.NET 4.8
+
宸ョ▼鐩爣妗嗘灦
+
+
+
Plugin DLL
+
杈撳嚭涓 `StandardScene.dll`
+
+
+
Mission + CarType
+
涓ょ被鏍稿績鎵╁睍鍏ュ彛
+
+
+
涓枃 / EN / 馃槑
+
瀛椾綋涓庣紪鐮佸凡鍏煎
+
+
+ + +
+ +
+ + +
+
+

01. 椤圭洰鎬昏

+

+ `StandardScene` 鏄 AGV/AMR 鍦烘櫙鎻掍欢搴擄紝涓嶆槸鐙珛 EXE銆傚畠渚濊禆瀹夸富 `SimpleComposer.exe` 杩愯锛岃兘鍔涜鐩栨惉杩愯皟搴︺佺幆绾夸换鍔° + 浜ら氫簰閿併佸尯鍩熸祦閲忔帶鍒躲佸厖鐢靛崗鍚屻侀棬鎺ц仈鍔紝浠ュ強 HTTP / MQTT / Modbus 绛夊鍥存帴鍙c +

+
+
+ 椤圭洰瀹氫綅 + 浣滀负瀹夸富鎻掍欢琚姞杞斤紝鏍稿績鑱岃矗鏄滃満鏅昏緫鈥濊屼笉鏄滅嫭绔嬪簲鐢ㄢ濄 +
+
+ 鏈灏忕悊瑙e崟鍏 + `Mission` 璐熻矗鍦烘櫙娴佺▼锛宍CarType` 璐熻矗杞﹀瀷涓庡崗璁 +
+
+ 鏈鍏堥槄璇绘枃浠 + `Scheduler/HeartBeatMission.cs`銆乣Scheduler/RegionalTrafficControlMission.cs` +
+
+
+ 杈撳嚭绫诲瀷锛歀ibrary + 瀹夸富锛歋impleComposer.exe + 澶栭儴鎺ュ彛锛歂ancy / HTTP + 鍏稿瀷鍗忚锛歁QTT / Modbus +
+
+ +
+

02. 杩愯鏋舵瀯

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
灞傜骇瑙掕壊鍏稿瀷鏂囦欢
瀹夸富灞鍚姩绋嬪簭銆佽杞芥彃浠躲佸睍绀洪厤缃笌 Missionbuild/SimpleComposer.exe
鍦烘櫙閫昏緫灞浠诲姟璋冨害銆佸尯鍩熸祦鎺с佸厖鐢点侀棬绂佺瓑Scheduler/Chained/Charge/
杞﹁締鍗忚灞鍚勮溅鍨嬫帴鍏ヤ笌鐘舵佸悓姝CarTypes/
鍩虹鑳藉姏灞璺緞瑙勫垝銆侀攣鐐广佸叏灞瀵硅薄璁块棶SimpleCoreSimpleLib
+ +

瀹夸富瑁呰浇娴佺▼

+
    +
  1. 缂栬瘧寰楀埌 StandardScene.dll
  2. +
  3. 鏋勫缓浜嬩欢灏 DLL 澶嶅埗鍒 build/plugins
  4. +
  5. 瀹夸富鍚姩鍚庢壂鎻忔彃浠剁洰褰
  6. +
  7. 閫氳繃鐗规у弽灏勮瘑鍒 MissionTypeCarType
  8. +
  9. 鐢ㄦ埛鎴 API 鍚姩瀵瑰簲鍦烘櫙閫昏緫
  10. +
+
+ +
+

03. 妯″潡鍦板浘

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
鐩綍鑱岃矗寤鸿璧锋鏂囦欢
Scheduler/蹇冭烦銆佸畨鍏ㄤ俊鍙枫佸尯鍩熸祦鎺х瓑杞婚噺 MissionHeartBeatMission.cs
Chained/鎼繍涓庣幆绾夸换鍔′富娴佺▼TransportMission.cs
Charge/鍏呯數绔欑鐞嗐佺瓥鐣ユ帶鍒躲佺姸鎬佺淮鎶StandardChargeMission.cs
InterLock/鍖哄煙浜掗攣銆佷氦閫氭帶鍒TrafficInterlockMission.cs
CarTypes/杞﹀瀷涓庡崗璁疄鐜VDA5050Car.cs
ExtendDevice/Door/闂ㄧ鑱斿姩DoorMission.cs
WebApi.cs澶栭儴绯荤粺鎺ュ叆鍏ュ彛/car/*/map/*/mission_reflection/*
+
+ +
+

04. 鏋勫缓涓庤繍琛

+

鏈満渚濊禆璺緞

+
D:\MDCS\Dependencies\Commons\CommonUsage.dll
+D:\MDCS\Dependencies\deps\LessokajiWeaverUtilities.dll
+D:\MDCS\Dependencies\Commons\MDCSToolBox.dll
+D:\MDCS\Dependencies\Simple\RefSimpleCore.dll
+D:\MDCS\Executables\Simple\SimpleComposer.exe
+ +

鏋勫缓姝ラ

+
    +
  1. 鎵撳紑 StandardScene.sln
  2. +
  3. 缂栬瘧 Debug|Any CPURelease|Any CPU
  4. +
  5. 纭鏋勫缓浜嬩欢宸插皢鎻掍欢澶嶅埗鍒 build/plugins
  6. +
  7. 杩愯 build/SimpleComposer.exe
  8. +
+ +
+ 褰撳墠椤圭洰涓嶆槸 SDK 椋庢牸宸ョ▼銆傛柊澧 `.cs` 鏂囦欢鍚庯紝蹇呴』纭瀹冨凡缁忚鍔犲叆 `.csproj`锛涘惁鍒欐枃浠跺瓨鍦ㄤ絾涓嶄細鍙備笌缂栬瘧銆 +
+
+ +
+

05. 蹇熷紑濮嬫渚

+ +

妗堜緥 A锛氭柊澧 HelloMission

+

+ 杩欐槸鎺ㄨ崘鐨勬柊鎵嬬涓缁冦傚畠鐩存帴娌跨敤 `Scheduler/HeartBeatMission.cs` 鐨勭粨鏋勶紝鍙繚鐣欐渶灏忕敓鍛藉懆鏈燂細`Create()`銆乣Execute()`銆乣Stop()`銆 +

+
using System.Threading;
+using Newtonsoft.Json;
+using SimpleComposer.RCS;
+using SimpleCore;
+
+namespace StandardScene.Scheduler
+{
+    [MissionType(Name = "Hello Mission", editor = typeof(HelloMission))]
+    [I18N.DocumentTranslation(Name = "Hello Mission", locale = "en")]
+    public class HelloMission : Mission
+    {
+        [JsonIgnore] private bool _started;
+        [JsonIgnore] private Thread _thread;
+
+        public static Mission Create()
+        {
+            return new HelloMission();
+        }
+
+        public override void Execute()
+        {
+            if (_started) return;
+            _started = true;
+            status.status = "宸插惎鍔";
+
+            _thread = new Thread(() =>
+            {
+                int count = 0;
+                while (_started)
+                {
+                    Thread.Sleep(1000);
+                    count++;
+                    status.status = $"tick:{count}";
+                }
+            });
+            _thread.Start();
+        }
+
+        public void Stop()
+        {
+            _started = false;
+            status.status = "宸插仠姝";
+        }
+    }
+}
+
    +
  1. 鏂板缓 Scheduler/HelloMission.cs
  2. +
  3. 纭瀹冨凡琚姞鍏ュ伐绋
  4. +
  5. 缂栬瘧骞舵墦寮 build/SimpleComposer.exe
  6. +
  7. 鍚姩鍚庤瀵 status.status 鏄惁鎸夌閫掑
  8. +
+ +

妗堜緥 B锛氬尯鍩熸祦鎺у疄楠

+

+ 璇ユ渚嬪搴 Scheduler/RegionalTrafficControlMission.cs銆傜粰鍚屼竴鍖哄煙鐨勭珯鐐瑰姞瀛楁 Region1=1锛屽嵆鍙檺鍒惰鍖哄煙鏈澶氬悓鏃跺彧鏈 1 鍙拌溅銆 +

+
Region1 = 1
+
    +
  1. 缁欑洰鏍囧尯鍩熷唴澶氫釜绔欑偣閮藉姞涓婄浉鍚岀殑 Region1 瀛楁
  2. +
  3. 鍚姩鈥滃尯鍩熸祦閲忕洃鎺р Mission
  4. +
  5. 璁╀袱鍙拌溅渚濇鐢宠杩涘叆璇ュ尯鍩
  6. +
  7. 瑙傚療绗簩鍙拌溅鏄惁琚樆姝紝鏃ュ織鍜岀姸鎬侀噷浼氳褰曟嫤鎴鏁
  8. +
+
+ +
+

06. 閰嶇疆鏂囦欢

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
鏂囦欢鐢ㄩ
Config/traffic.json浜ら氫簰閿佷笌鍖哄煙鎺у埗
Config/ChargeStations.json鍏呯數妗╁畾涔
Config/ChargeStrategyConfig.json鍏呯數绛栫暐
Config/AlarmConfigs.json鍏呯數鎶ヨ閰嶇疆
DoorConfig.json闂ㄧ璁惧閰嶇疆
tasklist.json鐜嚎浠诲姟鍒楄〃
simple.json瀹夸富鍩虹閰嶇疆
+

+ 闄や簡 JSON 鏂囦欢锛屽緢澶氶昏緫杩樺ぇ閲忎緷璧栫珯鐐广佽溅杈嗗拰杞ㄩ亾鐨 fieldstags銆備緥濡傚尯鍩熸祦鎺т緷璧栦互 + Region 寮澶寸殑瀛楁锛岃澶氳皟搴﹂昏緫渚濊禆 groupgiveWaystandby 绛夊瓧娈点 +

+
+ +
+

07. API 鍏ュ彛

+

WebApi.cs 鏄閮ㄧ郴缁熸渶閲嶈鐨勮繘鍏ョ偣锛屼粨搴撲腑宸插彲鐪嬪埌杩欎簺鍏稿瀷璺敱锛

+
+
+ /car/createTask + 鍒涘缓浠诲姟 +
+
+ /car/getAllCars + 鏌ヨ杞﹁締鍒楄〃 +
+
+ /car/goSite + 鎸囨淳杞﹁締鍘荤珯鐐 +
+
+ /map/getMap + 鑾峰彇鍦板浘 +
+
+ /task/getTask + 鏌ヨ浠诲姟 +
+
+ /mission_reflection/* + 鍙嶅皠璋冪敤 Mission 鏂规硶 +
+
+
+ +
+

08. 寮鍙戝伐浣滄祦

+
    +
  1. 鍏堝畾浣嶅姛鑳藉睘浜庡摢涓洰褰
  2. +
  3. 鎵炬渶鎺ヨ繎鐨勭幇鏈夌被浣滀负妯℃澘
  4. +
  5. 鏄庣‘瀹冧緷璧 JSON 閰嶇疆杩樻槸 `fields` / `tags`
  6. +
  7. 琛ラ綈鏃ュ織銆佺姸鎬佸拰鍋滄閫昏緫
  8. +
  9. 纭鏂版枃浠跺凡鍔犲叆宸ョ▼
  10. +
  11. 缂栬瘧鍚庡湪瀹夸富涓獙璇佹槸鍚﹁璇嗗埆
  12. +
+
+ 鏂板 Mission锛氬厛鐪 HeartBeatMission + 鏂板鍖哄煙閫昏緫锛氬厛鐪 RegionalTrafficControlMission + 鏂板鎺ュ彛锛氬厛鐪 WebApi.cs + 鏂板閰嶇疆锛氬厛鍐欓粯璁ゅ间笌鐢熸晥鏃舵満 +
+
+ +
+

09. 甯歌鎺掗敊

+ + + + + + + + + + + + + + + + + + + + + + + + + +
鐜拌薄浼樺厛妫鏌
鏂板 Mission 鍦ㄥ涓婚噷鐪嬩笉鍒鐗规ф槸鍚︽纭乣Create()` 鏄惁瀛樺湪銆佹枃浠舵槸鍚﹀凡鍔犲叆 `.csproj`銆佹彃浠舵槸鍚﹀鍒跺埌 `build/plugins`
鍖哄煙娴佹帶涓嶇敓鏁绔欑偣瀛楁鍚嶆槸鍚︿互 `Region` 寮澶淬佸瓧娈靛兼槸鍚︿负鏁存暟銆丮ission 鏄惁宸插惎鍔
杞︿笉鍔ㄦ垨浠诲姟涓嶈蛋杞﹁締鍦ㄧ嚎鐘舵併佽矾寰勬槸鍚﹀彲杈俱佹槸鍚﹁閿佺偣 / 浜掗攣 / 闂ㄦ帶鎷︽埅
API 璋冧笉閫璺敱鏄惁姝g‘銆佸涓荤鍙f槸鍚︽墦寮銆佽姹傛槸鍚︾湡鐨勫懡涓 `WebApi.cs`
+ +
+ 缂栫爜璇存槑锛氭湰椤典娇鐢 `UTF-8` 鍜屽吋瀹逛腑鏂 / English / emoji 鐨勫瓧浣撴爤锛屾湰鍦板弻鍑绘墦寮涓嶄緷璧栦换浣曞閮ㄨ祫婧愶紝鍥犳涓嶄細鍥犱负閮ㄧ讲鎴 CDN 缂哄け瀵艰嚧涔辩爜銆 +
+
+
+
+ +
+ StandardScene 鏂囨。闂ㄦ埛 路 宸ヤ笟绱鏍 路 鍙绾跨洿鎺ユ墦寮 路 UTF-8 Safe +
+
+ + \ No newline at end of file diff --git a/INDEX.md b/INDEX.md new file mode 100644 index 0000000..ab1b464 --- /dev/null +++ b/INDEX.md @@ -0,0 +1,58 @@ +锘# StandardScene 鏂囨。瀵艰埅 + +## 1. 鏂囨。鍏ュ彛 + +| 鏂囨。 | 閫傚悎璋 | 鐢ㄩ | +| --- | --- | --- | +| `DocumentHub.html` | 鎵鏈変汉 | 鍗曟枃浠舵祻瑙堝叆鍙o紝鍙屽嚮鐩存帴鎵撳紑 | +| `README.md` | 绗竴娆℃帴瑙︿粨搴撶殑浜 | 蹇熶簡瑙i」鐩畾浣嶄笌杩愯鏂瑰紡 | +| `DEVELOPMENT_GUIDE.md` | 瑕佸紑濮嬪紑鍙戠殑浜 | 绯荤粺鍖栫悊瑙f灦鏋勩佹ā鍧椼侀厤缃佹渚 | +| `QUICK_REFERENCE.md` | 姝e湪鍐欎唬鐮佺殑浜 | 蹇熸煡鍏ュ彛銆佽矾寰勩侀厤缃佹帓閿欑偣 | + +## 2. 鎺ㄨ崘闃呰璺嚎 + +### 璺嚎 A锛氱涓娆℃帴瑙︿粨搴 + +1. 鎵撳紑 `DocumentHub.html` +2. 闃呰 `README.md` +3. 闃呰 `DEVELOPMENT_GUIDE.md` +4. 鎵撳紑 `Scheduler\HeartBeatMission.cs` +5. 鍔ㄦ墜鍋 HelloMission 妗堜緥 + +### 璺嚎 B锛氬凡缁忎細璺戝伐绋嬶紝鍑嗗寮鍙 + +1. 闃呰 `QUICK_REFERENCE.md` +2. 鏍规嵁鐩爣瀹氫綅鐩綍 +3. 鎵炬渶鎺ヨ繎鐨勫弬鑰冪被 +4. 鍐欎唬鐮 +5. 鍥炲埌 `DEVELOPMENT_GUIDE.md` 鏌ラ厤缃笌璋冭瘯寤鸿 + +### 璺嚎 C锛氬噯澶囨敼鐪熷疄涓氬姟閫昏緫 + +1. 鍏堢‘璁ゅ彉鏇村睘浜庡摢涓ā鍧 +2. 濡傛灉鏄皟搴︼紝鐪 `Chained\` +3. 濡傛灉鏄仈鍔ㄦ帶鍒讹紝鐪 `Scheduler\` 鎴 `InterLock\` +4. 濡傛灉鏄澶囨垨鍗忚锛岀湅 `CarTypes\`銆乣Charge\`銆乣ExtendDevice\Door\` +5. 濡傛灉鏄閮ㄧ郴缁熸帴鍏ワ紝鐪 `WebApi.cs` + +## 3. 鎸夌洰鏍囨煡鏂囦欢 + +| 鐩爣 | 鐩存帴鎵撳紑杩欎簺鏂囦欢 | +| --- | --- | +| 浜嗚В鎻掍欢鎬庝箞琚涓昏瘑鍒 | `Scheduler\HeartBeatMission.cs` | +| 鍋氬尯鍩熸祦閲忛檺鍒 | `Scheduler\RegionalTrafficControlMission.cs` | +| 鍋氭惉杩愯皟搴 | `Chained\AbstractChainedDeliveryMission.cs`銆乣Chained\TransportMission.cs` | +| 鍋氬厖鐢电瓥鐣 | `Charge\AbstractChargeLogicMission.cs`銆乣Charge\StandardChargeMission.cs` | +| 鏌 API | `WebApi.cs` | +| 鏌ラ氱敤宸ュ叿 | `Commons.cs` | + +## 4. 鏈湴鎵撳紑 HTML 鏂囨。 + +鐩存帴鍙屽嚮 `DocumentHub.html` 鍗冲彲锛屾棤闇浠讳綍閮ㄧ讲銆傞〉闈㈠凡鍐呭祵鏍峰紡涓庤剼鏈紝骞舵樉寮忓0鏄 `UTF-8` 缂栫爜銆 + +## 5. 寤鸿鐨勭涓涓粌涔 + +1. 鎸 `DEVELOPMENT_GUIDE.md` 鐨勬渚 A 鏂板缓 `HelloMission` +2. 缂栬瘧鍚庢墦寮 `build\SimpleComposer.exe` +3. 鍚姩 Mission锛岀‘璁ょ姸鎬佹瘡绉掗掑 +4. 鍐嶆寜妗堜緥 B 缁欑珯鐐规坊鍔 `Region1=1`锛屼綋楠屽尯鍩熸祦鎺 \ No newline at end of file diff --git a/LooMission.md b/LooMission.md new file mode 100644 index 0000000..143181e --- /dev/null +++ b/LooMission.md @@ -0,0 +1,412 @@ +锘# AbstractLoopMission 鎶借薄鐜嚎浠诲姟鍩虹被 + +## 姒傝堪 + +`AbstractLoopMission` 鏄幆绾夸换鍔$殑鎶借薄鍩虹被锛屾彁渚涗簡瀹屾暣鐨勫惊鐜换鍔¤皟搴︽鏋躲傚畠鏀寔澶氱鍚姩绫诲瀷銆佷换鍔$被鍒佹祦閲忔帶鍒跺拰浼樺厛绾ц皟搴︼紝鏄墍鏈夊叿浣撶幆绾夸换鍔″疄鐜扮殑鍩虹銆 + +## 鏍稿績鐗规 + +| 鐗规 | 璇存槑 | + +|------|------| +| 澶氬惎鍔ㄧ被鍨 | AutoLoop锛堣嚜鍔ㄥ惊鐜級銆丄pi銆丳lc銆丅uttonBox銆丆harge | +| 澶氫换鍔$被鍒 | Loop锛堟櫘閫氬惊鐜級銆丅ranchPoint锛堝垎娴佺偣锛夈丣oinPoint锛堟眹鍚堢偣锛 | +| 娴侀噺鎺у埗 | 闄愬埗鐩爣绔欑偣鐨勬渶澶ц溅杈嗘暟 | +| 浼樺厛绾ц皟搴 | 楂樹紭鍏堢骇浠诲姟浼樺厛澶勭悊 | +| 閰嶇疆鐑洿鏂 | tasklist.json 鏂囦欢鍙樻洿鑷姩鍒锋柊 | +| 璺緞缂撳瓨 | 閬垮厤閲嶅璁$畻璺緞锛屾彁鍗囨ц兘 | +| 鏉′欢瑙﹀彂 | 瀛愮被鍙噸鍐欎簨浠跺洖璋冿紝鑷畾涔夎Е鍙戞潯浠 | + +## 绫诲浘 + +![alt text](image.png) + +## 蹇熷紑濮 + +### 1. 閰嶇疆浠诲姟鍒楄〃 + +鍦 浠诲姟杩涚▼鏂规硶鏄剧ず鐣岄潰涓厤缃换鍔℃垨鑰呭湪`tasklist.json` 涓厤缃换鍔 +[ { "Id": 1, "Name": "涓荤嚎寰幆", "CurrentStationId": 100, "TargetStationId": 200, "StartType": "AutoLoop", +"Kind": "Loop", "Priority": 10, "TrafficControl": 2, "IsViaPoint": true }, +{ "Id": 2, "Name": "鍒嗘祦鐐笰", "CurrentStationId": 150, "TargetStationId": 201, "StartType": "AutoLoop", "Kind": "BranchPoint", "Priority": 8, "TrafficControl": 1 }, +{ "Id": 3, "Name": "姹囧悎鐐笲", "CurrentStationId": 180, "TargetStationId": 300, "StartType": "Plc", "Kind": "JoinPoint", "Priority": 5, "TrafficControl": 1 } ] + +### 2. 鍒涘缓瀛愮被 + +public class MyLoopMission : AbstractLoopMission { protected override ExternalTriggerResult OnApiTrigger(int currentSiteId, LoopTask task, Car car) { // 涓氬姟閫昏緫鍒ゆ柇 if (ShouldProcessTask(task, car)) { // 鏂瑰紡1锛氫娇鐢ㄥ瓙绫绘寚瀹氱殑鐩爣绔欑偣 return ExternalTriggerResult.UseTarget(200); + // 鏂瑰紡2锛氫娇鐢ㄩ厤缃枃浠朵腑鐨勭洰鏍囩珯鐐 + // return ExternalTriggerResult.UseConfigTarget(); + } + // 涓氬姟澶辫触锛屼笉鍒嗛厤浠诲姟 + return ExternalTriggerResult.Fail(); +} + +protected override ExternalTriggerResult OnPlcTrigger(int currentSiteId, LoopTask task, Car car) +{ + // PLC 淇″彿瑙﹀彂閫昏緫 + if (CheckPlcSignal(currentSiteId)) + { + return ExternalTriggerResult.UseConfigTarget(); + } + return ExternalTriggerResult.Fail(); +} +} + +## 浠诲姟绫诲埆璇存槑 + +### Loop锛堟櫘閫氬惊鐜級 + +杞﹁締浠庡綋鍓嶇珯鐐圭Щ鍔ㄥ埌鐩爣绔欑偣鐨勭畝鍗曚换鍔° +绔欑偣A 鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈻 绔欑偣B + +### BranchPoint锛堝垎娴佺偣锛 + +涓涓珯鐐瑰彲浠ュ垎娴佸埌澶氫釜鐩爣绔欑偣锛屾寜浼樺厛绾у拰娴侀噺鎺у埗閫夋嫨鐩爣銆 + 鈹屸攢鈹鈻 鐩爣绔欑偣1 (浼樺厛绾ч珮) +鍒嗘祦鐐 鈹鈹鈹鈹鈹尖攢鈹鈻 鐩爣绔欑偣2 鈹斺攢鈹鈻 鐩爣绔欑偣3 (浼樺厛绾т綆) + +### JoinPoint锛堟眹鍚堢偣锛 + +澶氫釜绔欑偣姹囧悎鍒板悓涓涓洰鏍囩珯鐐癸紝鎸変紭鍏堢骇鍐冲畾鏀捐椤哄簭銆 +鏉ユ簮绔欑偣1 鈹鈹鈹 鏉ユ簮绔欑偣2 鈹鈹鈹尖攢鈹鈻 姹囧悎鐐 鏉ユ簮绔欑偣3 鈹鈹鈹 + +## 娴侀噺鎺у埗 + +閫氳繃 `TrafficControl` 灞炴ч檺鍒剁洰鏍囩珯鐐圭殑鏈澶ц溅杈嗘暟锛 +// 妫鏌ユ祦閲忔帶鍒 if (!CheckTrafficControl(targetSiteId, task.TrafficControl)) { // 鐩爣绔欑偣娴侀噺宸叉弧锛岀瓑寰 continue; } + +- `TrafficControl = 0`锛氫笉闄愬埗 +- `TrafficControl = 1`锛氱洰鏍囩珯鐐规渶澶 1 杈嗚溅 +- `TrafficControl = N`锛氱洰鏍囩珯鐐规渶澶 N 杈嗚溅 + +缁熻鑼冨洿鍖呮嫭锛 + +1. 宸插湪鐩爣绔欑偣鐨勮溅杈嗭紙`holdingLocks` 鍖呭惈璇ョ珯鐐癸級 +2. 姝e湪鍓嶅線鐩爣绔欑偣鐨勮溅杈嗭紙`pendingLocks` 鏈鍚庝竴涓负璇ョ珯鐐癸級 + +### 3. 鍚姩浠诲姟 + +var mission = new MyLoopMission(); +// 鍚姩鎵鏈夌嚎绋嬶紙绛栫暐鍚屾 + 涓氬姟閫昏緫锛 mission.StartAll(); +// 鎴栬呭垎鍒惎鍔 // mission.StartLoop(); // 鍚姩绛栫暐鍚屾绾跨▼ // mission.StartLogicLoop(); // 鍚姩涓氬姟閫昏緫绾跨▼ +// 鍋滄浠诲姟 // mission.StopAll(); +// 閲婃斁璧勬簮 // mission.Dispose(); + +## 鍚姩绫诲瀷璇存槑 + +| 绫诲瀷 | 鏋氫妇鍊 | 璇存槑 | 瀛愮被鎺ュ彛 | + +|------|--------|------|----------| +| AutoLoop | `TaskStartType.AutoLoop` | 鑷姩寰幆锛岃溅杈嗗埌绔欒嚜鍔ㄨЕ鍙 | 鏃犻渶閲嶅啓 | +| Api | `TaskStartType.Api` | API 澶栭儴璋冪敤瑙﹀彂 | `OnApiTrigger()` | +| Plc | `TaskStartType.Plc` | PLC 淇″彿瑙﹀彂 | `OnPlcTrigger()` | +| ButtonBox | `TaskStartType.ButtonBox` | 鎸夐挳鐩掕Е鍙 | `OnButtonTrigger()` | +| Charge | `TaskStartType.Charge` | 鍏呯數鏉′欢瑙﹀彂 | `OnChargeTrigger()` | + +## 瀛愮被鍙噸鍐欎簨浠跺洖璋 + +### 姒傝堪 + +`AbstractLoopMission` 鎻愪緵浜嗗洓涓彲閲嶅啓鐨勪簨浠跺洖璋冩柟娉曪紝瀛愮被鍙互閫氳繃閲嶅啓杩欎簺鏂规硶瀹炵幇鑷畾涔夌殑瑙﹀彂鏉′欢閫昏緫銆傚綋杞﹁締鍒拌揪閰嶇疆鐨勫綋鍓嶇珯鐐规椂锛岀郴缁熶細鏍规嵁浠诲姟鐨 `StartType` 璋冪敤瀵瑰簲鐨勫洖璋冩柟娉曘 + +### 鍥炶皟鏂规硶绛惧悕 + +| 鏂规硶 | 瑙﹀彂鏉′欢 | 榛樿琛屼负 | +|------|----------|----------| +| `OnApiTrigger(int currentSiteId, LoopTask task, Car car)` | StartType = Api | 杩斿洖 `Fail()` | +| `OnPlcTrigger(int currentSiteId, LoopTask task, Car car)` | StartType = Plc | 杩斿洖 `Fail()` | +| `OnButtonTrigger(int currentSiteId, LoopTask task, Car car)` | StartType = ButtonBox | 杩斿洖 `Fail()` | +| `OnChargeTrigger(int currentSiteId, LoopTask task, Car car)` | StartType = Charge | 杩斿洖 `Fail()` | + +### 鍥炶皟鍙傛暟璇存槑 + +| 鍙傛暟 | 绫诲瀷 | 璇存槑 | +|------|------|------| +| `currentSiteId` | `int` | 杞﹁締褰撳墠鎵鍦ㄧ珯鐐笽D | +| `task` | `LoopTask` | 鍖归厤鍒扮殑浠诲姟閰嶇疆 | +| `car` | `Car` | 鍒拌揪绔欑偣鐨勮溅杈嗗璞 | + +### 杩斿洖鍊艰鏄 + +鍥炶皟鏂规硶蹇呴』杩斿洖 `ExternalTriggerResult` 瀵硅薄锛 + +| 杩斿洖鏂瑰紡 | 璇存槑 | 浣跨敤鍦烘櫙 | +|----------|------|----------| +| `ExternalTriggerResult.UseTarget(siteId)` | 鎴愬姛锛屼娇鐢ㄥ瓙绫绘寚瀹氱殑鐩爣绔欑偣 | 闇瑕佸姩鎬佽绠楃洰鏍囩珯鐐规椂 | +| `ExternalTriggerResult.UseConfigTarget()` | 鎴愬姛锛屼娇鐢ㄩ厤缃枃浠朵腑鐨勭洰鏍囩珯鐐 | 鏉′欢婊¤冻锛屼娇鐢ㄩ璁剧洰鏍囨椂 | +| `ExternalTriggerResult.Fail()` | 澶辫触锛屼笉鍒嗛厤浠诲姟 | 鏉′欢涓嶆弧瓒筹紝绛夊緟涓嬫妫鏌ユ椂 | + +### 浣跨敤绀轰緥 + +#### 绀轰緥1锛欰PI 瑙﹀彂 - 妫鏌ュ閮ㄧ郴缁熺姸鎬 + +public class ApiLoopMission : AbstractLoopMission { protected override ExternalTriggerResult OnApiTrigger(int currentSiteId, LoopTask task, Car car) { // 妫鏌ュ閮 API 鏄惁鍏佽鍙戣溅 var apiResult = ExternalApiService.CheckCanDispatch(currentSiteId, car.id); + if (apiResult.Success) + { + // API 杩斿洖鎸囧畾鐩爣 + if (apiResult.TargetSiteId.HasValue) + { + return ExternalTriggerResult.UseTarget(apiResult.TargetSiteId.Value); + } + // 浣跨敤閰嶇疆鐩爣 + return ExternalTriggerResult.UseConfigTarget(); + } + // 鏉′欢涓嶆弧瓒筹紝绛夊緟 + return ExternalTriggerResult.Fail(); +} +} + +#### 绀轰緥2锛歅LC 瑙﹀彂 - 妫鏌 PLC 淇″彿鐘舵 + +public class PlcLoopMission : AbstractLoopMission { protected override ExternalTriggerResult OnPlcTrigger(int currentSiteId, LoopTask task, Car car) { // 璇诲彇 PLC 淇″彿 bool plcSignal = PlcManager.ReadBool($"Station{currentSiteId}.AllowDispatch"); + if (plcSignal) + { + // 鏍规嵁 PLC 鏁版嵁鍐冲畾鐩爣 + int plcTarget = PlcManager.ReadInt($"Station{currentSiteId}.TargetStation"); + if (plcTarget > 0) + { + return ExternalTriggerResult.UseTarget(plcTarget); + } + return ExternalTriggerResult.UseConfigTarget(); + } + return ExternalTriggerResult.Fail(); +} +} + +#### 绀轰緥3锛氭寜閽洅瑙﹀彂 - 妫鏌ユ寜閽姸鎬 + +public class ButtonBoxLoopMission : AbstractLoopMission { protected override ExternalTriggerResult OnButtonTrigger(int currentSiteId, LoopTask task, Car car) { // 妫鏌ユ寜閽洅鏄惁鎸変笅 var buttonBox = ButtonBoxManager.GetByStation(currentSiteId); + if (buttonBox != null && buttonBox.IsPressed) + { + // 閲嶇疆鎸夐挳鐘舵 + buttonBox.Reset(); + // 鏍规嵁鎸夐挳绫诲瀷閫夋嫨鐩爣 + switch (buttonBox.PressedButton) + { + case ButtonType.Green: + return ExternalTriggerResult.UseTarget(task.TargetStationId); + case ButtonType.Yellow: + return ExternalTriggerResult.UseTarget(GetAlternativeTarget(currentSiteId)); + default: + return ExternalTriggerResult.UseConfigTarget(); + } + } + + return ExternalTriggerResult.Fail(); +} +} + +#### 绀轰緥4锛氬厖鐢佃Е鍙 - 妫鏌ョ數閲忔潯浠 + +public class ChargeLoopMission : AbstractLoopMission { private const int LOW_BATTERY_THRESHOLD = 20; +protected override ExternalTriggerResult OnChargeTrigger(int currentSiteId, LoopTask task, Car car) +{ + // 鑾峰彇杞﹁締鐢甸噺 + int batteryLevel = car.GetBatteryLevel(); + // 妫鏌ユ槸鍚﹂渶瑕佸厖鐢 + if (batteryLevel <= LOW_BATTERY_THRESHOLD) + { + // 鏌ユ壘鏈杩戠殑绌洪棽鍏呯數绔 + int chargeStation = FindNearestAvailableChargeStation(currentSiteId); + if (chargeStation > 0) + { + Diagnosis.Post($"杞﹁締 {car.name} 鐢甸噺 {batteryLevel}%锛屽墠寰鍏呯數绔 {chargeStation}", "Charge", true); + return ExternalTriggerResult.UseTarget(chargeStation); + } + } + // 鐢甸噺鍏呰冻鎴栨棤鍙敤鍏呯數绔欙紝涓嶈Е鍙戝厖鐢典换鍔 + return ExternalTriggerResult.Fail(); +} + +private int FindNearestAvailableChargeStation(int currentSiteId) +{ + // 瀹炵幇鏌ユ壘鏈杩戝厖鐢电珯閫昏緫 + return ChargeStationManager.FindNearest(currentSiteId); +} +} + +#### 绀轰緥5锛氱粍鍚堟潯浠惰Е鍙 + +public class ComplexLoopMission : AbstractLoopMission { protected override ExternalTriggerResult OnApiTrigger(int currentSiteId, LoopTask task, Car car) { // 缁勫悎澶氫釜鏉′欢鍒ゆ柇 + // 鏉′欢1锛氭鏌ヨ溅杈嗘槸鍚︽惡甯﹁揣鐗 + bool hasLoad = car.tags.ContainsKey("hasLoad") && car.tags["hasLoad"] == "true"; + // 鏉′欢2锛氭鏌ョ洰鏍囩珯鐐规槸鍚﹀彲鐢 + var targetSite = SimpleLib.GetSite(task.TargetStationId); + bool targetAvailable = targetSite != null && !targetSite.IsDisabled(); + // 鏉′欢3锛氭鏌ユ椂闂寸獥鍙 + bool inTimeWindow = DateTime.Now.Hour >= 8 && DateTime.Now.Hour <= 20; + // 鏉′欢4锛氭鏌ヤ紭鍏堢骇杞﹁締 + bool isPriorityCar = car.tags.ContainsKey("priority"); + + // 缁勫悎鍒ゆ柇 + if (hasLoad && targetAvailable && (inTimeWindow || isPriorityCar)) + { + return ExternalTriggerResult.UseConfigTarget(); + } + + // 璁板綍涓嶆弧瓒虫潯浠剁殑鍘熷洜 + if (!hasLoad) Diagnosis.Post($"杞﹁締 {car.name} 鏈惡甯﹁揣鐗", "ApiTrigger", true); + if (!targetAvailable) Diagnosis.Post($"鐩爣绔欑偣 {task.TargetStationId} 涓嶅彲鐢", "ApiTrigger", true); + if (!inTimeWindow && !isPriorityCar) Diagnosis.Post($"褰撳墠涓嶅湪宸ヤ綔鏃堕棿绐楀彛", "ApiTrigger", true); + + return ExternalTriggerResult.Fail(); +} +} + +### 鍥炶皟鎵ц娴佺▼ + +杞﹁締鍒拌揪绔欑偣 鈹 鈻 鍖归厤浠诲姟閰嶇疆 (CurrentStationId) 鈹 +鈻 妫鏌ヨ溅杈嗗彲鐢ㄦ (Commons.SelectCar) 鈹 +鈻 鏍规嵁 StartType 璋冪敤瀵瑰簲鍥炶皟 鈹 鈹 +鈹 AutoLoop 鈹鈹鈻 鑷姩澶勭悊锛屾棤闇鍥炶皟 +鈹溾攢 Api 鈹鈹鈹鈹鈹鈹鈹鈹鈻 OnApiTrigger() +鈹溾攢 Plc 鈹鈹鈹鈹鈹鈹鈹鈹鈻 OnPlcTrigger() +鈹溾攢 ButtonBox 鈹鈹鈻 OnButtonTrigger() 鈹斺攢 Charge 鈹鈹鈹鈹鈹鈻 OnChargeTrigger() +鈹 鈻 妫鏌ヨ繑鍥炵粨鏋 鈹 鈹溾攢 Fail() 鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈻 璺宠繃锛岀瓑寰呬笅娆℃鏌 +鈹 鈹斺攢 Success 鈹鈹鈹鈹鈹鈹鈹鈹鈹鈻 纭畾鐩爣绔欑偣 鈹 +鈹溾攢 CustomTargetSiteId 鏈夊 鈹鈹鈻 浣跨敤瀛愮被鎸囧畾鐩爣 鈹斺攢 CustomTargetSiteId 涓虹┖ 鈹鈹鈻 浣跨敤閰嶇疆鏂囦欢鐩爣 +鈹 鈻 娴侀噺鎺у埗妫鏌 鈹 鈹溾攢 閫氳繃 鈹鈹鈻 AssignCarToTarget() 鈹斺攢 涓嶉氳繃 鈹鈹鈻 绛夊緟 + +### 姹囧悎鐐瑰洖璋冩墽琛屾祦绋 + +鎸夌洰鏍囩珯鐐瑰垎缁 + 鈹 + 鈻 +閬嶅巻姣忎釜姹囧悎鐐圭粍 + 鈹 + 鈻 +缁勫唴浠诲姟鎸変紭鍏堢骇闄嶅簭鎺掑垪 + 鈹 + 鈻 +閬嶅巻鎺掑簭鍚庣殑浠诲姟 + 鈹 + 鈹溾攢鈻 娴侀噺鎺у埗妫鏌 鈹鈹鈻 涓嶉氳繃 鈹鈹鈻 璺宠繃 + 鈹 + 鈹溾攢鈻 鏌ユ壘杞﹁締 鈹鈹鈻 鏈壘鍒 鈹鈹鈻 璺宠繃 + 鈹 + 鈹溾攢鈻 妫鏌ヨ溅杈嗗彲鐢 鈹鈹鈻 涓嶅彲鐢 鈹鈹鈻 璺宠繃 + 鈹 + 鈹溾攢鈻 璋冪敤瀛愮被鎺ュ彛 鈹鈹鈻 杩斿洖澶辫触 鈹鈹鈻 璺宠繃 + 鈹 + 鈹溾攢鈻 纭畾鏈缁堢洰鏍囷紙瀛愮被鎸囧畾 > 閰嶇疆锛 + 鈹 + 鈹溾攢鈻 瀛愮被鎸囧畾涓嶅悓鐩爣鏃跺啀娆℃鏌ユ祦閲 + 鈹 + 鈹斺攢鈻 鍒嗛厤鐩爣绔欑偣 + +### 娉ㄦ剰浜嬮」 + +## 澶栭儴瑙﹀彂缁撴灉 + +`ExternalTriggerResult` 鐢ㄤ簬瀛愮被杩斿洖瑙﹀彂澶勭悊缁撴灉锛 + +| 鏂规硶 | 璇存槑 | +|------|------| +| `UseTarget(siteId)` | 浣跨敤瀛愮被鎸囧畾鐨勭洰鏍囩珯鐐 | +| `UseConfigTarget()` | 浣跨敤閰嶇疆鏂囦欢涓殑鐩爣绔欑偣 | +| `Fail()` | 涓氬姟澶辫触锛屼笉鍒嗛厤浠诲姟 | + +protected override ExternalTriggerResult OnApiTrigger(int currentSiteId, LoopTask task, Car car) { +// 鍦烘櫙1锛氬姩鎬佽绠楃洰鏍 int dynamicTarget = CalculateTarget(car); return ExternalTriggerResult.UseTarget(dynamicTarget); +// 鍦烘櫙2锛氫娇鐢ㄩ厤缃洰鏍 +return ExternalTriggerResult.UseConfigTarget(); + +// 鍦烘櫙3锛氭潯浠朵笉婊¤冻 +return ExternalTriggerResult.Fail(); +} + +1. **榛樿杩斿洖 Fail**锛氭墍鏈夊洖璋冩柟娉曢粯璁よ繑鍥 `Fail()`锛屽瓙绫诲繀椤婚噸鍐欐墠鑳藉惎鐢ㄥ搴旂殑瑙﹀彂鍔熻兘銆 + +2. **绾跨▼瀹夊叏**锛氬洖璋冩柟娉曞湪涓氬姟閫昏緫绾跨▼涓墽琛岋紝璁块棶鍏变韩璧勬簮鏃堕渶娉ㄦ剰绾跨▼瀹夊叏銆 + +3. **鎵ц棰戠巼**锛氫笟鍔¢昏緫绾跨▼姣 500ms 鎵ц涓娆★紝鍥炶皟鏂规硶搴旈伩鍏嶉暱鏃堕棿闃诲銆 + +4. **寮傚父澶勭悊**锛氬洖璋冩柟娉曚腑鐨勫紓甯镐細琚崟鑾峰苟璁板綍锛屼笉浼氬奖鍝嶅叾浠栦换鍔$殑澶勭悊銆 + +5. **娴侀噺鎺у埗**锛氬嵆浣垮洖璋冭繑鍥炴垚鍔燂紝浠嶄細杩涜娴侀噺鎺у埗妫鏌ワ紝鍙兘鍥犳祦閲忓凡婊¤岀瓑寰呫 + +## 璺緞鏌ユ壘涓庝换鍔″尮閰 + +### 璺緞鏌ユ壘 + +鍩轰簬杞ㄩ亾杩炴帴鐨勫箍搴︿紭鍏堟悳绱紝鏀寔杞ㄩ亾鏂瑰悜锛 +// 鑾峰彇涓ょ珯鐐逛箣闂寸殑璺緞 var path = GetSitesBetween(100, 200); // 杩斿洖: [100, 150, 180, 200] +// 妫鏌ヨ矾寰勪笂鏄惁鏈夎溅杈 bool hasCar = HasCarOnPath(100, 200, excludeCar); +// 鑾峰彇璺緞闀垮害 int length = GetPathLength(100, 200); + + +### 浠诲姟绛栫暐鍖归厤 + +鏍规嵁杞﹁締褰撳墠浣嶇疆鍖归厤鏈浼樹换鍔★細 +// 鏌ユ壘杞﹁締鐨勬渶浼樹换鍔 var match = FindBestTaskForCar(car); +if (match != null) { Console.WriteLine($"浠诲姟ID: {match.TaskId}"); +Console.WriteLine($"鐩爣绔欑偣: {match.TargetSiteId}"); +Console.WriteLine($"涓嬩竴绔欑偣: {match.NextSiteId}"); +Console.WriteLine($"鍓╀綑璺濈: {match.DistanceToTarget}"); +Console.WriteLine($"杩涘害: {match.ProgressPercent:F1}%"); } + +鍖归厤浼樺厛绾э細 + +1. 璧风偣鍖归厤浼樺厛 +2. 浠诲姟浼樺厛绾ч珮鐨勪紭鍏 +3. 璺濈鐩爣杩戠殑浼樺厛 + +> **娉ㄦ剰**锛氬鏋滅珯鐐规槸璺緞鐨勭粓鐐癸紝鍒欎笉鍖归厤璇ヤ换鍔° + +### TaskListChanged 浜嬩欢 + +浠诲姟鍒楄〃鍙樻洿鏃惰Е鍙戯細 +mission.TaskListChanged += (sender, e) => +{ switch (e.ChangeType) { +case TaskChangeType.Added: Console.WriteLine($"娣诲姞浠诲姟: 绱㈠紩={e.Index}"); break; +case TaskChangeType.Updated: Console.WriteLine($"鏇存柊浠诲姟: 绱㈠紩={e.Index}"); break; +case TaskChangeType.Removed: Console.WriteLine($"绉婚櫎浠诲姟: 绱㈠紩={e.Index}"); break; +case TaskChangeType.Replaced: Console.WriteLine("浠诲姟鍒楄〃宸叉浛鎹"); break; } }; + +## 绾跨▼妯″瀷 + +| 绾跨▼ | 鍚嶇О | 闂撮殧 | 鑱岃矗 | +|------|------|------|------| +| 绛栫暐鍚屾绾跨▼ | `AbstractLoopMission_Strategy` | 1000ms | 鍚屾閰嶇疆鏂囦欢锛屾洿鏂颁换鍔″垪琛 | +| 涓氬姟閫昏緫绾跨▼ | `AbstractLoopMission_Logic` | 500ms | 澶勭悊浠诲姟璋冨害锛屽垎閰嶈溅杈嗙洰鏍 | + +// 鍒嗗埆鎺у埗绾跨▼ mission.StartLoop(); // 鍚姩绛栫暐鍚屾 mission.StartLogicLoop(); // 鍚姩涓氬姟閫昏緫 +mission.StopLoop(); // 鍋滄绛栫暐鍚屾 mission.StopLogicLoop(); // 鍋滄涓氬姟閫昏緫 + +## 甯搁噺閰嶇疆 + +| 甯搁噺 | 鍊 | 璇存槑 | + +|------|------|------| + +| `STRATEGY_SYNC_INTERVAL_MS` | 1000 | 绛栫暐鍚屾闂撮殧锛堟绉掞級 | +| `LOGIC_LOOP_INTERVAL_MS` | 500 | 涓氬姟閫昏緫闂撮殧锛堟绉掞級 | +| `FILE_CHANGE_DEBOUNCE_MS` | 50 | 鏂囦欢鍙樻洿闃叉姈寤惰繜锛堟绉掞級 | +| `ERROR_RECOVERY_DELAY_MS` | 3000 | 寮傚父鎭㈠绛夊緟鏃堕棿锛堟绉掞級 | +| `SCRIPT_ERROR_TRIGGER_DELAY_MS` | 3000 | 鑴氭湰寮傚父妫娴嬪欢杩燂紙姣锛 | + +## 杈呭姪鏂规硶 + +### 杞﹁締鏌ユ壘 + +// 鏌ユ壘鍒拌揪鎸囧畾绔欑偣鐨勮溅杈 Car car = FindCarArrivedAtSite(siteId); +// 鑾峰彇鍦ㄧ珯鎴栧墠寰绔欑偣鐨勮溅杈 var cars = GetCarsAtOrHeadingToSite(siteId); +// 缁熻杞﹁締鏁伴噺 int count = CountCarsAtOrHeadingToSite(siteId); +// 妫鏌ヨ溅杈嗘槸鍚︾┖闂 bool idle = IsCarIdle(car); + +### 杞﹁締鍒嗛厤 + +// 鍒嗛厤鐩爣绔欑偣 AssignCarToTarget(car, targetSiteId); +// 瀵艰埅鍒扮洰鏍囩珯鐐 await GoSite(car, targetSite, action: "/", reverse: false); + +## 寮傚父澶勭悊 + +### 鑴氭湰寮傚父鎭㈠ + +褰撹溅杈嗚剼鏈姸鎬佷负 `Error` 鎴 `Bad` 鏃讹紝鑷姩鎵ц鎭㈠娴佺▼锛 + +1. 鏍囪绔欑偣涓嶅彲鐢 +2. 绂佹杞﹁締璋冨害 +3. 閲嶇疆杞﹁締鐘舵 +4. 娓呯悊杞﹁締鏍囩 +5. 鎵ц杞﹁締閲嶇疆 diff --git a/QUICK_REFERENCE.md b/QUICK_REFERENCE.md new file mode 100644 index 0000000..724c87a --- /dev/null +++ b/QUICK_REFERENCE.md @@ -0,0 +1,152 @@ +锘# StandardScene 蹇熷弬鑰 + +## 1. 涓鐪肩湅鎳傝繖涓粨搴 + +| 椤圭洰椤 | 璇存槑 | +| --- | --- | +| 宸ョ▼绫诲瀷 | `StandardScene.dll` 鎻掍欢搴 | +| 杩愯鏂瑰紡 | 鐢 `build\SimpleComposer.exe` 鍔犺浇 | +| 鐩爣妗嗘灦 | `.NET Framework 4.8` | +| 鏍稿績鍏ュ彛 | `MissionType`銆乣CarType`銆乣WebApi.cs` | +| 鏂版墜璧锋鏂囦欢 | `Scheduler\HeartBeatMission.cs` | +| 杩涢樁璧锋鏂囦欢 | `Scheduler\RegionalTrafficControlMission.cs` | + +## 2. 鍏抽敭鐩綍閫熻 + +| 璺緞 | 浣犻氬父鍦ㄨ繖閲屽仛浠涔 | +| --- | --- | +| `CarTypes\` | 鏂拌溅鍨嬨佸崗璁帴鍏ャ佺姸鎬佸悓姝 | +| `Chained\` | 鎼繍浠诲姟銆佺幆绾夸换鍔°佽皟搴﹂昏緫 | +| `Charge\` | 鍏呯數绛栫暐銆佸厖鐢垫々绠$悊 | +| `InterLock\` | 浜掗攣涓庝氦閫氭帶鍒 | +| `Scheduler\` | 蹇冭烦銆佸畨鍏ㄣ佸尯鍩熸祦鎺х瓑鍚庡彴 Mission | +| `ExtendDevice\Door\` | 闂ㄦ帶鑱斿姩 | +| `Model\` | 閰嶇疆涓庢暟鎹ā鍨 | +| `WebApi.cs` | HTTP API | +| `Commons.cs` | 鏍囩銆佸瓧娈点侀夎溅銆佽矾寰勮緟鍔 | + +## 3. 鏋勫缓杩愯閫熻 + +1. 鎵撳紑 `StandardScene.sln` +2. 纭繚鏈満渚濊禆璺緞瀛樺湪 +3. 缂栬瘧瑙e喅鏂规 +4. 鎵撳紑 `build\SimpleComposer.exe` +5. 纭鎻掍欢宸蹭粠 `build\plugins` 鍔犺浇 + +### 娉ㄦ剰 + +褰撳墠宸ョ▼鏄棫寮 `.csproj`銆傛柊澧 `.cs` 鏂囦欢鍚庯紝濡傛灉娌℃湁閫氳繃 VS 姝g‘鍔犲叆宸ョ▼锛屽彲鑳介渶瑕佹墜宸ヨˉ锛 + +```xml + +``` + +## 4. 鏂板鍔熻兘鏃跺厛鐪嬭皝 + +| 鐩爣 | 浼樺厛鍙傝 | +| --- | --- | +| 鏂板鏈灏 Mission | `Scheduler\HeartBeatMission.cs` | +| 鍋氬尯鍩熼檺娴 | `Scheduler\RegionalTrafficControlMission.cs` | +| 鍋氳繍杈撹皟搴 | `Chained\TransportMission.cs` | +| 鍋氱幆绾 | `Chained\AbstractLoopMission.cs` | +| 鍋氬厖鐢 | `Charge\StandardChargeMission.cs` | +| 鍋氬厖鐢垫々绠$悊 | `Charge\ChargeStationManagementExample.cs` | +| 鍋 Web 鎺ュ彛 | `WebApi.cs` | + +## 5. 楂橀浠g爜鐗囨 + +### 鑾峰彇杞﹁締涓庣珯鐐 + +```csharp +var car = SimpleLib.GetCar(carId); +var allCars = SimpleLib.GetAllCars(); +var site = SimpleLib.GetSite(siteId); +var allSites = SimpleLib.GetAllSites(); +var currentSiteId = car.GetLastSite(); +``` + +### 鏇存柊鏍囩涓庡瓧娈 + +```csharp +Commons.AddOrUpdateTag(car.tags, "occupied", "yes"); +Commons.AddOrUpdateCarField(car, "group", "A"); +Commons.AddOrUpdateSiteField(site, "giveWay", "true"); +``` + +### 瑙勫垝骞舵墽琛岃矾寰 + +```csharp +var plan = new SegmentPlan { usingCar = car }; +plan.fields["action"] = "move"; +plan.fields["allow_destination_on_route"] = "true"; +plan.FindRoute(SimpleLib.GetSite(srcId), SimpleLib.GetSite(dstId)); +await plan.Compile("move").Queue(); +``` + +### 杈撳嚭鏃ュ織 + +```csharp +Diagnosis.Post("浠诲姟宸插惎鍔", "demo", true); +Diagnosis.Log("璇︾粏璋冭瘯淇℃伅", "demo", true); +car.AppendDebug("杞﹁締鐘舵佸彉鍖"); +``` + +## 6. 甯歌閰嶇疆鏂囦欢 + +| 鏂囦欢 | 鐢ㄩ | +| --- | --- | +| `Config\traffic.json` | 浜ら / 浜掗攣閰嶇疆 | +| `Config\ChargeStations.json` | 鍏呯數妗╂暟鎹 | +| `Config\ChargeStrategyConfig.json` | 鍏呯數绛栫暐 | +| `Config\AlarmConfigs.json` | 鍏呯數鎶ヨ | +| `DoorConfig.json` | 闂ㄦ帶閰嶇疆 | +| `tasklist.json` | 鐜嚎浠诲姟鍒楄〃 | +| `simple.json` | 瀹夸富鍩虹閰嶇疆 | + +## 7. 甯歌 API 璺敱 + +| 璺敱 | 浣滅敤 | +| --- | --- | +| `/car/createTask` | 鍒涘缓浠诲姟 | +| `/car/getAllCars` | 鑾峰彇杞﹁締鍒楄〃 | +| `/car/goSite` | 璁╄溅杈嗗墠寰绔欑偣 | +| `/map/getMap` | 鑾峰彇鍦板浘 | +| `/task/getTask` | 鏌ヨ浠诲姟 | +| `/mission_reflection/get_mission_list` | 鑾峰彇 Mission 鍒楄〃 | +| `/mission_reflection/execute/{id}/{method}` | 鍙嶅皠鎵ц Mission 鏂规硶 | + +## 8. 璋冭瘯浼樺厛绾 + +鍑虹幇闂鏃讹紝寤鸿鎸夎繖涓『搴忔帓鏌ワ細 + +1. 鎻掍欢鏄惁琚涓诲姞杞 +2. Mission / CarType 鏄惁琚瘑鍒 +3. 閰嶇疆鏂囦欢鍜 `fields` 鏄惁姝g‘ +4. 杞﹁締鏄惁鍦ㄧ嚎銆佹槸鍚﹁鏍囩鍗犵敤 +5. 璺緞瑙勫垝鏄惁鎴愬姛 +6. 鏄惁琚氦閫氭帶鍒舵垨鍖哄煙娴佹帶鎷︽埅 +7. 澶栭儴鎺ュ彛鏄惁鐪熺殑鎵撳埌浜 `WebApi.cs` + +## 9. 甯歌鏁呴殰閫熸煡 + +| 鐜拌薄 | 浼樺厛妫鏌 | +| --- | --- | +| 鏂板 Mission 鐪嬩笉鍒 | 鐗规с乣Create()`銆乣.csproj` 寮曠敤銆佹彃浠跺鍒 | +| 浠诲姟涓鐩翠笉鎵ц | 杞﹁締鍦ㄧ嚎鐘舵併佽矾寰勩佹爣绛惧崰鐢ㄣ佸墠缃潯浠 | +| 鍖哄煙闄愭祦鏃犳晥 | 绔欑偣瀛楁鏄惁浠 `Region` 寮澶达紝鍊兼槸鍚︿负鏁存暟 | +| API 璋冧笉閫 | 璺敱璺緞銆佸涓荤鍙c丯ancy 鏄惁宸插惎鍔 | +| 杞﹁締涓嶅姩 | 璺緞澶辫触銆佺▼搴忔湭涓嬪彂銆佸崗璁湭杩為 | + +## 10. 鎺ㄨ崘涓婃墜妗堜緥 + +### HelloMission + +- 鐩爣锛氱悊瑙f渶灏忔彃浠剁敓鍛藉懆鏈 +- 鍙傝冿細`Scheduler\HeartBeatMission.cs` +- 楠岃瘉锛氬惎鍔ㄥ悗 `status.status` 姣忕閫掑 + +### 鍖哄煙娴佹帶 + +- 鐩爣锛氱悊瑙e瓧娈甸┍鍔 + 浜嬩欢璁㈤槄 +- 鍙傝冿細`Scheduler\RegionalTrafficControlMission.cs` +- 楠岃瘉锛氱粰绔欑偣娣诲姞 `Region1=1` 鍚庯紝绗簩鍙拌溅杩涘叆鍚屽尯鍩熶細琚樆姝 \ No newline at end of file diff --git a/README.en.md b/README.en.md new file mode 100644 index 0000000..8d624df --- /dev/null +++ b/README.en.md @@ -0,0 +1,36 @@ +# StandardScene + +#### Description +{**When you're done, you can delete the content in this README and update the file with details for others getting started with your repository**} + +#### Software Architecture +Software architecture description + +#### Installation + +1. xxxx +2. xxxx +3. xxxx + +#### Instructions + +1. xxxx +2. xxxx +3. xxxx + +#### Contribution + +1. Fork the repository +2. Create Feat_xxx branch +3. Commit your code +4. Create Pull Request + + +#### Gitee Feature + +1. You can use Readme\_XXX.md to support different languages, such as Readme\_en.md, Readme\_zh.md +2. Gitee blog [blog.gitee.com](https://blog.gitee.com) +3. Explore open source project [https://gitee.com/explore](https://gitee.com/explore) +4. The most valuable open source project [GVP](https://gitee.com/gvp) +5. The manual of Gitee [https://gitee.com/help](https://gitee.com/help) +6. The most popular members [https://gitee.com/gitee-stars/](https://gitee.com/gitee-stars/) diff --git a/README.md b/README.md index 3b7a112..a0d83a2 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,63 @@ -# StandardSence +# StandardScene +`StandardScene` 鏄竴涓熀浜 `.NET Framework 4.8` 鐨勫満鏅彃浠跺簱锛岃繍琛屾椂鐢 `SimpleComposer.exe` 浣滀负瀹夸富鍔犺浇銆傞」鐩潰鍚 AGV/AMR 鍦哄唴璋冨害涓庤仈鍔ㄦ帶鍒讹紝瑕嗙洊鎼繍浠诲姟銆佺幆绾夸换鍔°佸尯鍩熶氦閫氱鍒躲佸厖鐢电鐞嗐侀棬绂佽仈鍔紝浠ュ強 HTTP / MQTT / Modbus 绛夊澶栭氫俊鑳藉姏銆 + +## 鏂囨。鍏ュ彛 + +- `DocumentHub.html`锛氬崟鏂囦欢鏂囨。闂ㄦ埛锛屽弻鍑诲嵆鍙湪娴忚鍣ㄤ腑鎵撳紑 +- `DEVELOPMENT_GUIDE.md`锛氶潰鍚戞暣涓粨搴撶殑寮鍙戞寚鍗 +- `QUICK_REFERENCE.md`锛氶珮棰戝紑鍙戦熸煡琛 +- `INDEX.md`锛氶槄璇婚『搴忎笌瀵艰埅璇存槑 + +## 椤圭洰瀹氫綅 + +- 宸ョ▼绫诲瀷锛氱被搴撴彃浠讹紝涓嶆槸鐙珛 EXE +- 瀹夸富绋嬪簭锛歚SimpleComposer.exe` +- 鐩爣妗嗘灦锛歚.NET Framework 4.8` +- 涓昏璇█锛歚C#` +- 鍏稿瀷鍦烘櫙锛氫粨鍌ㄦ惉杩愩佸尯鍩熸祦鎺с佸厖鐢靛崗鍚屻侀棬鎺ц仈鍔ㄣ佽溅杈嗗崗璁帴鍏 + +## 椤跺眰妯″潡 + +| 鐩綍 | 浣滅敤 | +| --- | --- | +| `CarTypes` | 鍚勮溅鍨嬩笌鍗忚閫傞厤锛屽鍙夎溅銆並iva銆乂DA5050銆佸杞﹁仈鍔 | +| `Chained` | 閾惧紡鎼繍涓庣幆绾夸换鍔′富娴佺▼ | +| `Charge` | 鍏呯數绛栫暐銆佸厖鐢垫々绠$悊銆佺姸鎬佺洃鎺 | +| `ChargeStationType` | 鍏呯數妗╃被鍨嬪疄鐜 | +| `InterLock` | 鍖哄煙浜掗攣涓庝氦閫氭帶鍒 | +| `Scheduler` | 鍦烘櫙绾ц緟鍔╀换鍔★紝濡傚績璺炽佸尯鍩熸祦鎺с佸畨鍏ㄤ俊鍙 | +| `ExtendDevice/Door` | 闂ㄦ帶璁惧鎺ュ叆涓庤仈鍔 | +| `Model` | 浠诲姟銆侀厤缃佸湴鍥剧瓑鏁版嵁妯″瀷 | +| `TCP` / `Utils` | TCP銆丣SON銆乄ebAPI銆丮odbus 绛夊熀纭宸ュ叿 | + +## 鏋勫缓涓庤繍琛 + +1. 浣跨敤 Visual Studio 鎵撳紑 `StandardScene.sln` +2. 纭鏈満渚濊禆璺緞瀛樺湪锛 + - `D:\MDCS\Dependencies\Commons\CommonUsage.dll` + - `D:\MDCS\Dependencies\deps\LessokajiWeaverUtilities.dll` + - `D:\MDCS\Dependencies\Simple\RefSimpleCore.dll` + - `D:\MDCS\Executables\Simple\SimpleComposer.exe` +3. 缂栬瘧 `Debug|Any CPU` 鎴 `Release|Any CPU` +4. 缂栬瘧鍚 `PostBuildEvent` 浼氬皢 `StandardScene.dll` 澶嶅埗鍒 `build\plugins\` +5. 杩愯 `build\SimpleComposer.exe`锛岀敱瀹夸富鍔犺浇鎻掍欢 + +## 寮鍙戦槄璇婚『搴 + +1. 鍏堢湅 `DocumentHub.html`锛屽揩閫熷缓绔嬪叏灞璁ょ煡 +2. 鍐嶇湅 `DEVELOPMENT_GUIDE.md`锛岀啛鎮夋灦鏋勩佹祦绋嬩笌閰嶇疆 +3. 寮鍙戞椂閰嶅悎 `QUICK_REFERENCE.md` 蹇熸煡甯哥敤鍏ュ彛 +4. 鏂板鍔熻兘鍓嶏紝鍏堝鐓 `Scheduler\HeartBeatMission.cs` 鎴 `Scheduler\RegionalTrafficControlMission.cs` + +## 鎺ㄨ崘璧锋鏂囦欢 + +- `Scheduler\HeartBeatMission.cs`锛氭渶灏 Mission 缁撴瀯鍙傝 +- `Scheduler\RegionalTrafficControlMission.cs`锛氫簨浠惰闃呭瀷 Mission 鍙傝 +- `Chained\TransportMission.cs`锛氭惉杩愪换鍔′富娴佺▼ +- `Charge\StandardChargeMission.cs`锛氬厖鐢典换鍔¢鏋 +- `WebApi.cs`锛欻TTP 鎺ュ彛鍏ュ彛 + +## 鏈湴鎵撳紑 HTML 鏂囨。 + +鐩存帴鍙屽嚮 `DocumentHub.html` 鍗冲彲锛屾棤闇閮ㄧ讲銆傞〉闈㈠凡浣跨敤 `UTF-8` 缂栫爜锛屽苟瀵逛腑鏂囥丒nglish 鍜 emoji 鍋氫簡鍏煎瀛椾綋璁剧疆銆 diff --git a/StandardScene.Core/CarTypes/BasicFields.cs b/StandardScene.Core/CarTypes/BasicFields.cs new file mode 100644 index 0000000..a194afb --- /dev/null +++ b/StandardScene.Core/CarTypes/BasicFields.cs @@ -0,0 +1,51 @@ +锘縰sing System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace StandardScene.CarTypes +{ + internal class BasicCarFields + { + public float MagSlowSpeed = 0; + public float MagFullSpeed = 0; + } + + internal class BasicSiteFields + { + public bool Shelf = false; + public float CarLength = -1; + public float CarWidth = -1; + public float CarCenterX = 0; + public float CarCenterY = 0; + public int tag = -1; + public int TagValue = -1;//纾佸鑸紝浜岀淮鐮佸硷紝鎴栬卹fid 鍊 + } + + internal class BasicTrackFields + { + public int IOArea = -1; + public int LidarArea = -2; + public float BiasAlarmThresh = -1; + public float DthAlarmThresh = -1; + public float Speed = 0.2f; + public bool Reverse = false; + public int ReverseDst = -1; + public bool SwitchBarrier = false; + public bool CalibrateWheelEncoder = false; + public float CarDirectionBias = 0; + public bool EnableCarAbsoluteDirection = false; + public float CarAbsoluteDirection = 0; + public float SlowDistance = -1; + public float StopDistance = -1; + } + + internal class BasicPlanFields + { + public string action = "/"; + + public float CarLength = -1; + public float CarWidth = -1; + } +} diff --git a/StandardScene.Core/CarTypes/DummyCar.cs b/StandardScene.Core/CarTypes/DummyCar.cs new file mode 100644 index 0000000..a46c964 --- /dev/null +++ b/StandardScene.Core/CarTypes/DummyCar.cs @@ -0,0 +1,740 @@ +锘縰sing AMRScene1; +using SimpleLite; +using SimpleLite.RCS; +using SimpleLite.RCS.CarTypes; +using SimpleLite.CADTools; +using SimpleLite.Props; +using SimpleLite.UI; +using SimpleCore; +using SimpleCore.BasicProps; +using SimpleCore.Compiler; +using SimpleCore.Extras; +using SimpleCore.Library; +using SimpleCore.PropType; +using SimpleCore.Traffic; +using StandardScene.CarTypes; +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Drawing; +using System.Drawing.Drawing2D; +using System.Linq; +using System.Numerics; +using System.Runtime.InteropServices.ComTypes; +using System.Threading; +using System.Threading.Tasks; +using System.Windows.Forms; + +namespace AMRScene1 +{ + class DummyCarTrackField + { + public float Speed = -1; + public bool Reverse = false; + public int ReverseDst = -1; + } + class DummyCarSiteField + { + public bool Shelf = false; + + } + class DummyCarPlanField + { + public string action = "/"; + + } + + [TemplateTrackCoderSettings( + priority = 0, + templateString = "agv.Go(${src.x},${src.y},${src.id},${dst.x},${dst.y},${dst.id},${track.id}," + + "${track.Speed},${track.Reverse || track.ReverseDst == dst.id},${track.typeInfo});", + trackFields = typeof(DummyCarTrackField), + siteFields = typeof(DummyCarSiteField))] + + + [TemplateTrackCoderSettings( + priority = 5, + useVerb = "plan.action=='put'&& dst.Shelf ", + templateString = "agv.Put(${src.x},${src.y},${src.id},${dst.x},${dst.y},${dst.id},${track.id}," + + "${track.Speed},${track.Reverse || track.ReverseDst == dst.id},${track.typeInfo});", + blockVerb = "true", + trackFields = typeof(DummyCarTrackField), + siteFields = typeof(DummyCarSiteField), + planFields = typeof(DummyCarPlanField))] + [TemplateTrackCoderSettings( + priority = 5, + useVerb = "plan.action=='fetch'&& dst.Shelf ", + templateString = "agv.Fetch(${src.x},${src.y},${src.id},${dst.x},${dst.y},${dst.id},${track.id}," + + "${track.Speed},${track.Reverse || track.ReverseDst == dst.id},${track.typeInfo});", + blockVerb = "true", + trackFields = typeof(DummyCarTrackField), + siteFields = typeof(DummyCarSiteField), + planFields = typeof(DummyCarPlanField))] + + [CarType(Name = "妯℃嫙杞-鍖呯粶")] + [EnvelopConfig(centerX = 0, centerY = 0, lengthX = 1000, lengthY = 700)] + public class DummyCar:Car + { + [FieldMember] public string conf = "realistic"; + public class RotateSiteEnvelope : SiteEnvelopeDefinition + { + public override bool Use() => true; + + public override void Prompt() + { + if (curSeg == 0|| plan.codeArr==null) + { + return; + } + var code = plan.codeArr[curSeg - 1]; + if (code.Contains("Fetch")) + { + reshape(1550, 1150, 0, 0); + } + else if (code.Contains("Put")) + { + reshape(1000,700, 0, 0); + } + } + + public override bool Block() => false; + + public override int priority => -1; + } + public virtual AGV getAGV(int id) => new AGV(id); + public class DummyCarStatus : CarStatus + { + public string simStat = "/"; + public double simulatedDistance = 0; + } + public override CarStatus status { get; set; } = new DummyCarStatus(); + + public static async Task Create() // boilerplate + { + return new() + { + lstatus = "姝e父", + name = $"妯℃嫙杞", + haveCoordination = true + }; + } + + + public override void rightClickAction(float mouseX, float mouseY) + { + x = mouseX; + y = mouseY; + } + + + public class AGV: AGVInterface + { + public DummyCar car; + + public AGV(int id) + { + car = (DummyCar) SimpleLib.GetCar(id); + } + public void Sleep(int millis) + { + Thread.Sleep(millis); + } + + protected class Segment + { + public int trackID, srcID, dstID; + public double srcX, srcY, dstX, dstY; + } + + + protected List routeCache = []; // use circular list. + + protected virtual Task following(double dstX, double dstY, double speed, bool reverse, params float[] typeInfo) + { + var promise = new TaskCompletionSource(); + int i = 0; + IEnumerable iterActions() + { + if (typeInfo.Length == 0 || (int)typeInfo[0] == 0) // line path + { + // car always run to completion. + while ((Math.Abs(car.x - dstX) > 10 || Math.Abs(car.y - dstY) > 10))// && car.running) + { + if (car.fields.TryGetValue("speed", out var sv)) + speed = float.Parse(sv); + var dx = dstX - car.x; + var dy = dstY - car.y; + var d = Math.Sqrt(dx * dx + dy * dy); + var ed = (DateTime.Now - car.lastRefresh).TotalSeconds * speed; + if (ed > d) + { + car.th = (float)(Math.Atan2(dstY - car.y, dstX - car.x) / Math.PI * 180 + (reverse ? 180 : 0)); + car.x = (float)dstX; + car.y = (float)dstY; + break; + } + + dx = (float)(dx / d * ed); + dy = (float)(dy / d * ed); + car.th = (float)(Math.Atan2(dstY - car.y, dstX - car.x) / Math.PI * 180 + (reverse ? 180 : 0)); + car.x += (float)dx; + car.y += (float)dy; + ((DummyCarStatus)car.status).simStat = $"following-iter-{i++}"; + //Console.WriteLine($"move one frame:{car.x},{car.y},{car.speed}"); + yield return true; + } + } + else if ((int)typeInfo[0] == 1) // circularArc path + { + var center = new Vector2(typeInfo[1], typeInfo[2]); + var radius = typeInfo[3]; + var angleStart = typeInfo[4]; + var angleEnd = typeInfo[5]; + + var dstAngle = (float)(Math.Atan2(dstY - center.Y, dstX - center.X) / Math.PI * 180); + var dir = 1; // counter-clockwise + if (Math.Abs(LessMath.thDiff(angleStart, dstAngle)) < + Math.Abs(LessMath.thDiff(angleEnd, dstAngle))) dir = -1; + + while (true)//(car.running) + { + var pTh = (float)(Math.Atan2(car.y - center.Y, car.x - center.X) / Math.PI * 180); + var ed = (DateTime.Now - car.lastRefresh).TotalSeconds * speed; + + var dRadius = Math.Abs(Vector2.Distance(center, new Vector2(car.x, car.y)) - radius); + if (dRadius > 10) + { + // first go to arc + var targetX = (float)(center.X + radius * Math.Cos(pTh / 180 * Math.PI)); + var targetY = (float)(center.Y + radius * Math.Sin(pTh / 180 * Math.PI)); + car.th = (float)(Math.Atan2(targetY - car.y, targetX - car.x) / Math.PI * 180 + (reverse ? 180 : 0)); + + if (ed > dRadius) + { + car.x = targetX; + car.y = targetY; + } + + car.x += (float)((targetX - car.x) / dRadius * ed); + car.y += (float)((targetY - car.y) / dRadius * ed); + } + else + { + // then go along arc + if (Math.Abs(LessMath.thDiff(dstAngle, pTh)) < 0.01) break; + var eth = (float)(ed / radius / Math.PI * 180); + var dth = LessMath.thDiff(dstAngle, pTh); + if (Math.Abs(eth) > Math.Abs(dth)) + { + car.th = dstAngle + 90 * dir + (reverse ? 180 : 0); + car.x = (float)(center.X + radius * Math.Cos(dstAngle / 180 * Math.PI)); + car.y = (float)(center.Y + radius * Math.Sin(dstAngle / 180 * Math.PI)); + break; + } + + var newTh = pTh + eth * dir; + car.th = newTh + 90 * dir + (reverse ? 180 : 0); + car.x = (float)(center.X + radius * Math.Cos(newTh / 180 * Math.PI)); + car.y = (float)(center.Y + radius * Math.Sin(newTh / 180 * Math.PI)); + } + + ((DummyCarStatus)car.status).simStat = $"following-iter-{i++}"; + yield return true; + } + } + + ((DummyCarStatus)car.status).simStat = $"following-done"; + car.moveAction = null; + // Console.WriteLine($"** following to {dstX},{dstY} done"); + promise.SetResult(1); + // Task.Run(()=>promise.SetResult(1)); + } + + car.moveAction = iterActions().GetEnumerator(); + return promise.Task; + } + + /// + /// 妯℃嫙杞︽湁TryLock鐨刟gv鍑芥暟锛岃繘鍏ュ嚱鏁伴渶鍏堣皟鐢ˋddRoute() + /// + /// + /// + /// + public void AddRoute(int srcid, int dstid, int trackid) + { + var route_id = routeCache.Count; + lock (routeCache) + routeCache.Add(new Segment() { trackID = trackid, srcID = srcid, dstID = dstid }); + if (car.route.Length == 0) + car.route = [srcid]; + car.route = car.route.Append(dstid).ToArray(); + } + + private static ConcurrentDictionary _taskmap = new(); + + public Task mvmtTsk + { + get + { + if (_taskmap.TryGetValue(car, out var tsk)) return tsk; + return _taskmap[car] = Task.CompletedTask; + } + set + { + _taskmap[car] = value; + } + } + + // trackType: 0 line, 1 circularArc + public void Go(double srcX, double srcY, int srcid, double dstX, double dstY, int dstid, int trackid, + int speed = -1, bool reverse = false, params float[] trackTypeInfo) + { + // car.AppendDebug($"Go() track typeInfo:{string.Join(",", trackTypeInfo)}"); + + AddRoute(srcid, dstid, trackid); + // ReSharper disable once PossiblyMistakenUseOfParamsMethod + var promise = new TaskCompletionSource(); + Queue(async () => + { + while (!TryLock(dstid)) + await Task.Delay(100); + // Console.WriteLine($"** ready to go to {dstid}({dstX},{dstY})"); + mvmtTsk = mvmtTsk.ContinueWith(async _ => + { + //Console.WriteLine($"** begin to go to {dstid}({dstX},{dstY})"); + await following(dstX, dstY, speed > 0 ? speed : car.speed, reverse, trackTypeInfo); + ((DummyCarStatus)car.status).simulatedDistance += LessMath.dist(srcX, srcY, dstX, dstY); + promise.SetResult(1); + // Task.Run(() => promise.SetResult(1)); // following finished. + }).Unwrap(); + //Console.WriteLine($"** issued to go to {dstid}({dstX},{dstY})"); + }, async () => + { + await promise.Task; + //Console.WriteLine($"** done go to {dstid}({dstX},{dstY})"); + Leave(srcid); + }); + } + public void Fetch(double srcX, double srcY, int srcid, double dstX, double dstY, int dstid, int trackid, + int speed = -1, bool reverse = false, params float[] trackTypeInfo) + { + // car.AppendDebug($"Go() track typeInfo:{string.Join(",", trackTypeInfo)}"); + + AddRoute(srcid, dstid, trackid); + // ReSharper disable once PossiblyMistakenUseOfParamsMethod + var promise = new TaskCompletionSource(); + Queue(async () => + { + while (!TryLock(dstid)) + await Task.Delay(100); + // Console.WriteLine($"** ready to go to {dstid}({dstX},{dstY})"); + mvmtTsk = mvmtTsk.ContinueWith(async _ => + { + //Console.WriteLine($"** begin to go to {dstid}({dstX},{dstY})"); + await following(dstX, dstY, speed > 0 ? speed : car.speed, reverse, trackTypeInfo); + ((DummyCarStatus)car.status).simulatedDistance += LessMath.dist(srcX, srcY, dstX, dstY); + promise.SetResult(1); + // Task.Run(() => promise.SetResult(1)); // following finished. + }).Unwrap(); + //Console.WriteLine($"** issued to go to {dstid}({dstX},{dstY})"); + }, async () => + { + await promise.Task; + //Console.WriteLine($"** done go to {dstid}({dstX},{dstY})"); + Leave(srcid); + }); + } + public void Put(double srcX, double srcY, int srcid, double dstX, double dstY, int dstid, int trackid, + int speed = -1, bool reverse = false, params float[] trackTypeInfo) + { + // car.AppendDebug($"Go() track typeInfo:{string.Join(",", trackTypeInfo)}"); + + AddRoute(srcid, dstid, trackid); + // ReSharper disable once PossiblyMistakenUseOfParamsMethod + var promise = new TaskCompletionSource(); + Queue(async () => + { + while (!TryLock(dstid)) + await Task.Delay(100); + // Console.WriteLine($"** ready to go to {dstid}({dstX},{dstY})"); + mvmtTsk = mvmtTsk.ContinueWith(async _ => + { + //Console.WriteLine($"** begin to go to {dstid}({dstX},{dstY})"); + await following(dstX, dstY, speed > 0 ? speed : car.speed, reverse, trackTypeInfo); + ((DummyCarStatus)car.status).simulatedDistance += LessMath.dist(srcX, srcY, dstX, dstY); + promise.SetResult(1); + // Task.Run(() => promise.SetResult(1)); // following finished. + }).Unwrap(); + //Console.WriteLine($"** issued to go to {dstid}({dstX},{dstY})"); + }, async () => + { + await promise.Task; + //Console.WriteLine($"** done go to {dstid}({dstX},{dstY})"); + Leave(srcid); + }); + } + + + public void Nop(int srcid, int dstid, int trackid) + { + var route_id = routeCache.Count; + lock (routeCache) + routeCache.Add(new Segment() { trackID = trackid, srcID = srcid, dstID = dstid }); + if (car.route.Length == 0) + car.route = [srcid]; + car.route = car.route.Append(dstid).ToArray(); + + Queue(async () => + { + while (!TryLock(dstid)) + await Task.Delay(5); + }, async () => + { + Leave(srcid); + }); + } + + public override bool TryLock(int siteId) + { + if (car.status.holdingLocks.Last() == siteId) return true; + if (!car.status.usage.Get().scheduling) + throw new Exception("abandoned"); + if (!car.status.usage.Get().scheduling) + throw new Exception("Stopped"); + lock (routeCache) + return TrafficControl.TryLock(car, siteId); + } + + public override void Leave(int siteID) + { + TrafficControl.Leave(car, siteID); + } + } + + private bool running = false; + + + public override async Task actualSendScript(string script) + { + if (running) + throw new Exception($"dummy car {id} already running script"); + running = true; + + try + { + route = new int[0]; + AppendDebug($"dummy car {id} use jint for simulation"); + currentAgv = getAGV(id); + var tcs = new TaskCompletionSource(); + new Thread(() => { + try + { + SelfEvaluating(currentAgv, script); + tcs.SetResult(1); + } + catch (Exception ex) + { + tcs.SetException(ex); + }} + ) { Name = $"eva_{name}({id}):{status.programs.now.name}" }.Start(); + + await tcs.Task; + await currentAgv.WaitAsync(); + + Console.WriteLine($"{name}({id}) self evaluating script completed"); + } + catch (Exception ex) + { + AppendDebug($"simulation error:{ExceptionFormatter.FormatEx(ex)}"); + Console.WriteLine($"* {id} evaluating script failed, ex:{ExceptionFormatter.FormatEx(ex)}"); + running = false; + throw; + } + + running = false; + } + + + + + [MethodMember(Name = "璁惧畾璺嚎", Description = "浠庡綋鍓嶄綅缃嚭鍙戯紝涓嶅仠鍦拌蛋璺嚎")] + public async void Path() + { + + Site starting = null; + float dist = float.MaxValue; + foreach (var site in SimpleLib.GetAllSites()) + { + var d = LessMath.dist(site.x, site.y, x, y); + if (d < dist) + { + dist = (float)d; + starting = site; + } + } + + var p = new Pen(Color.Red, 3); + var lineCap = + new AdjustableArrowCap(6, 6, true); + p.CustomEndCap = lineCap; + p.StartCap = LineCap.RoundAnchor; + + var segments = new List(); + + var painter = SimpleMonitor.getPainter("flat-goto"); + painter.clear(); + + Dictionary triggers = new(); + CarProgram program = null; + try + { + while (true) + { + var pt = await Program.UI.getPoint(new UIOps.getPointOptions() { site = true }); + var dstSite = (UISite)SimpleLib.GetSite(pt.site); + + var plan = new SegmentPlan() { usingCar = this }; + plan.fields["allow_destination_on_route"] = "true"; + if (segments.Count > 0) + plan.fields["useMustCanGo"] = "false"; + plan.FindRoute((UISite)starting, dstSite,false); + + segments = segments.Concat(plan.segments.Skip(segments.Count > 0 ? 1 : 0)).ToList(); + triggers[dstSite.id]= () => { Console.WriteLine($"VDASegment end {dstSite.id} reached"); }; + Console.WriteLine($"goto site {pt.site}"); + starting = dstSite; + + painter.clear(); + for (int i = 0; i + 2 < segments.Count; i += 2) + { + var st = (UISite)segments[i]; + var ed = (UISite)segments[i + 2]; + painter.drawLine(p, st.x, st.y, ed.x, ed.y); + } + + if (program == null) program = plan.Compile("walk", false); + else + program.Append(plan, (go) => + { + Task.Run(() => + { + MessageBox.Show("go?"); + go(); + }); + }); + } + } + catch (TaskCanceledException ex) + { + Console.WriteLine("end"); + } + + Console.WriteLine($"issue command"); + _ = Task.Factory.StartNew(() => + { + Thread.Sleep(3000); + painter.clear(); + }); + program.Forecast(); + // plan.segments = segments; + // if (plan.segments.Count == 0) + // { + // MessageBox.Show("鏈敓鎴愯矾寰勶紒"); + // return; + // } + // + // var program = plan.Compile("walk"); + foreach (var kvp in triggers) + { + program.TriggerOnSite(kvp.Key, kvp.Value); + // program.AwaitOnSite(kvp.Key, go => + // { + // kvp.Value(); + // + // Task.Run(() => + // { + // MessageBox.Show("go?"); + // go(); + // }); + // }); + } + Console.WriteLine(program.script); + G.pushStatus($"鍚慉GV:{name}({id})涓嬪彂琛岃蛋浠诲姟"); + var tsk = program.Queue(); + _ = Task.Factory.StartNew(() => + { + G.pushStatus($"AGV:{name}({id})寮濮嬫墽琛屼换鍔"); + tsk.Wait(); + G.pushStatus($"AGV:{name}({id})鎵ц浠诲姟瀹屾瘯"); + }); + siteID = segments.Last().id; + } + + [MethodMember(Name = "鍘绘煇鍦",Description="浠庡綋鍓嶄綅缃壘涓鏉¤矾寰勫幓鏌愮珯鐐")] + public void Goto() + { + async void goto_fun(){ + if (GetLastSite() == -1) + { + G.pushStatus("Car not initialized to any site!"); + return; + } + try + { + var pt = await Program.UI.getPoint(new UIOps.getPointOptions() { site = true }); + Console.WriteLine($"goto site {pt.site}"); + + Site site1 = SimpleLib.GetSite(GetLastSite()); + + var plan = new SegmentPlan() { usingCar = this }; + plan.FindRoute(site1, SimpleLib.GetSite(pt.site), findLoop:false); + // var code = $"{plan.Code()};agv.Wait();"; + // Console.WriteLine(code); + await plan.Compile($"goto_{pt.site}").Queue(); + Console.WriteLine("Done"); + } + catch (Exception ex) + { + Console.WriteLine(ExceptionFormatter.FormatEx(ex)); + } + } + Task.Run(goto_fun); + } + [MethodMember(Name = "澧炲姞灏忚溅enums", Description = "鍙屽嚮娣诲姞鏍囩")] + public void AddEnums() + { + + if (InputBox.ShowDialog("璇疯緭鍏ュ皬杞nums\" enums:value") != SimpleLite.DialogResult.OK) return; + string tag = InputBox.ResultValue; + if (tag.Contains("锛")) + { + MessageBox.Show("闇瑕佸垏鎹㈣嫳鏂囪緭鍏ユ硶杈撳叆:"); + return; + } + if (!string.IsNullOrEmpty(tag) && tag.Contains(":")) + { + string[] tagValue = tag.Split(':'); + this.status.enums[tagValue[0]] = tagValue[1]; + + } + + } + + [MethodMember(Name = "鍔寔", Description = "鎸囧畾涓涓捣鐐瑰拰缁堢偣锛屽綋灏忚溅鍒拌揪璧风偣鍚庡姭鎸佸皬杞﹁嚦缁堢偣")] + public void Hijack() + { + async void hijiack_fun() + { + G.pushStatus("Select starting point"); + var pt1 = await Program.UI.getPoint(new UIOps.getPointOptions() { site = true }); + G.pushStatus("Select ending point"); + var pt2 = await Program.UI.getPoint(new UIOps.getPointOptions() { site = true }); + if (GetLastSite() == -1) + { + G.pushStatus("Car not initialized to any site!"); + return; + } + try + { + var plan = new SegmentPlan() { usingCar = this }; + plan.FindRoute(SimpleLib.GetSite(pt1.site), SimpleLib.GetSite(pt2.site), findLoop: false); + await plan.Compile($"hijack", forecast:false).TryHijack().Queue(); + Console.WriteLine("Done"); + } + catch (Exception ex) + { + Console.WriteLine(ExceptionFormatter.FormatEx(ex)); + } + } + Task.Run(hijiack_fun); + } + + [MethodMember(Name = "璁剧疆浣嶅Э", Description = "鎷栨嫿浠ヨ缃綅濮")] + public void SetPosition() + { + SimpleMonitor.registerDownevent((sender, args) => + { + x = SimpleMonitor.mouseX; + y = SimpleMonitor.mouseY; + },null, (sender, args) => + { + th = (float)(Math.Atan2(SimpleMonitor.mouseY - y, SimpleMonitor.mouseX - x) / Math.PI * 180); + }, (sender, args) => SimpleMonitor.clearDownevent()); + } + + + + + [MethodMember(Name = "璧板埌鎸囧畾浣嶇疆骞惰缃瓻scape", Description = "鐐逛竴涓綅缃紝鍐嶇偣涓涓綅缃")] + public void GoEscaped() + { + async void goto_fun() + { + if (GetLastSite() == -1) + { + G.pushStatus("Car not initialized to any site!"); + return; + } + try + { + var pt = await Program.UI.getPoint(new UIOps.getPointOptions() { site = true }); + var ptesc = await Program.UI.getPoint(new UIOps.getPointOptions() { site = true }); + Console.WriteLine($"goto site {pt.site}, ptesc={ptesc.site}"); + + var planRoute = new SegmentPlan() { usingCar = this }; + planRoute.fields["forbid_cross"] = "false"; + planRoute.FindRoute(SimpleLib.GetSite(GetLastSite()), SimpleLib.GetSite(pt.site), findLoop: false); + + var planEsc = new SegmentPlan() { usingCar = this }; + planEsc.fields["forbid_cross"] = "false"; + planEsc.FindRoute(SimpleLib.GetSite(pt.site), SimpleLib.GetSite(ptesc.site), findLoop: false); + + await planRoute.Compile($"goto_{pt.site}", false).Forecast(planEsc).Queue(); + } + catch (Exception ex) + { + Console.WriteLine(ExceptionFormatter.FormatEx(ex)); + } + } + Task.Run(goto_fun); + } + + [MethodMember(Name = "璁剧疆瑙掑害", Description = "鎸囧畾妯℃嫙杞︾殑鏈濆悜瑙掑害")] + public void SetAngle() + { + if (InputBox.ShowDialog("杈撳叆瑙掑害", "妯℃嫙杞﹁缃搴", "0", InputBox.Buttons.OkCancel) == SimpleLite.DialogResult.Cancel) return; + if (!float.TryParse(InputBox.ResultValue, out var angle)) return; + th = angle; + } + + protected override void draw(Graphics eGraphics) + { + eGraphics.FillRectangle(Brushes.Gray, -320, -240 , 640 , 480 ); + eGraphics.DrawRectangle(Pens.White, -320, -240 , 640 , 480 ); + eGraphics.DrawLine(Pens.White, 0, -240, 320, 0); + eGraphics.DrawLine(Pens.White, 0, 240 , 320 , 0); + } + + public DateTime lastRefresh =DateTime.MinValue; + + + protected IEnumerator moveAction; + private AGV currentAgv; + + + public override void keepAlive() + { + if (lastRefresh == DateTime.MinValue) + { + lastRefresh = DateTime.Now; + return; + } + + if (moveAction != null) + moveAction.MoveNext(); + + haveCoordination = true; + lastRefresh = DateTime.Now; + } + } +} diff --git a/StandardScene.Core/CarTypes/IScriptErrorRecoverable.cs b/StandardScene.Core/CarTypes/IScriptErrorRecoverable.cs new file mode 100644 index 0000000..313ae16 --- /dev/null +++ b/StandardScene.Core/CarTypes/IScriptErrorRecoverable.cs @@ -0,0 +1,15 @@ +namespace StandardScene.CarTypes +{ + /// + /// 鑴氭湰寮傚父鑷仮澶嶈兘鍔涳紙opt-in锛夈 + /// 鑳屾櫙锛欰bstractLoopMission 鍘熶互 car is Kiva 纭紪鐮佺瓫閫夊弬涓庛岃剼鏈 Error/Bad 鐘舵 + /// 鑷姩涓嬬嚎 + 灏卞湴閲嶇疆銆嶇殑杞﹀瀷锛涜溅鍨嬫寜骞冲彴鎷嗗垎涓烘彃浠跺悗锛孋ore 涓嶈兘鍙嶅悜渚濊禆鎻掍欢鍐呯殑鍏蜂綋杞﹀瀷锛 + /// 鏀逛负鐢辫溅鍨嬪疄鐜版湰鎺ュ彛澹版槑璇ヨ兘鍔涳紙褰撳墠浠 Kiva 瀹炵幇锛岃涓轰笌鎷嗗垎鍓嶄竴鑷达級銆 + /// + public interface IScriptErrorRecoverable + { + /// 鑴氭湰寮傚父涓嬬嚎鍚庣殑灏卞湴閲嶇疆锛堝師 Kiva.newReset锛夈 + /// 閲嶇疆鐩爣绔欑偣 id锛0 琛ㄧず鎸夎溅杈嗗綋鍓嶄綅缃嚜鍔ㄦ壘鏈杩戠珯鐐广 + void RecoverReset(int resetSiteId = 0); + } +} diff --git a/StandardScene.Core/CarTypes/KivaFields.cs b/StandardScene.Core/CarTypes/KivaFields.cs new file mode 100644 index 0000000..f547ef2 --- /dev/null +++ b/StandardScene.Core/CarTypes/KivaFields.cs @@ -0,0 +1,80 @@ +namespace StandardScene.CarTypes +{ + // Kiva 绯诲瓧娈佃銆傚師鍐呰仈浜 Kiva.cs锛涜溅鍨嬫寜骞冲彴鎷嗗垎锛圞iva鈫扢agnetic锛孉rmCar鈫扱rLidar锛夊悗锛 + // ArmCarXxxFields 浠嶇户鎵胯繖浜涚被锛屾晠涓嬫矇鍩哄骇渚涗袱渚ф彃浠跺叡鐢紙internal + InternalsVisibleTo锛夈 + + class KivaCarFields : BasicCarFields + { + + } + + class KivaSiteFields : BasicSiteFields + { + public int AngleTarget = 0; + public bool Turn = false; + + public float FetchSpeed = 0; + public int FetchLidarArea = -2; + public int FetchIOArea = -1; + public bool FetchReverse = false; + public float FetchBlindMoveDist = 0; + public float FetchLiftDownTarget = -1; + public float FetchLiftUpTarget = -1; + public bool FetchUseQr = false; + public int FetchQrMode = -1; + public bool FetchIsUpQr = false; + public bool FetchUseDetector = false; + public int FetchDetector = 0; + public float FetchDetectWidth = -1; + public float FetchDetectDepth = -1; + public bool FetchLeaveSrcEarly = false; + public float FetchShieldObstacleDist = -1; + + public float PutSpeed = 0; + public int PutLidarArea = -1; + public int PutIOArea = -1; + public bool PutReverse = false; + public bool PutSyncRotate = false; + public float PutBlindMoveDist = 0; + public float PutLiftDownTarget = -1; + public float PutLiftUpTarget = -1; + public bool PutUseQr = false; + public int PutQrMode = -1; + public bool PutIsUpQr = false; + public bool PutUseDetector = false; + public int PutDetector = 0; + public float PutDetectWidth = -1; + public float PutDetectDepth = -1; + public bool PutLeaveSrcEarly = false; + public float PutShieldObstacleDist = -1; + + public float LeaveShelfSpeed = 0; + public int LeaveShelfLidarArea = -1; + public int LeaveShelfIOArea = -1; + public bool LeaveShelfReverse = false; + public bool LeaveShelfSyncRotate = false; + public float LeaveShelfBlindMoveDist = 0; + public float LeaveShelfLiftDownTarget = -1; + public float LeaveShelfRecoveryObstacleDist = -1; + } + + class KivaTrackFields : BasicTrackFields + { + public int ManeuverDir = 0; + public int ForwardDst = 0; + public int ForwardObChooseDst = -2; + public float BlindMoveDist = 0; + public bool UseDetector = false; + public int DetectorMode = -1; + public float DetectWidth = -1; + public float DetectDepth = -1; + public bool LeaveSrcEarly = false; + public float ShieldObstacleDist = -1; + } + + class KivaPlanFields : BasicPlanFields + { + public bool reverse = false; + public int level = 0; + } +} diff --git a/StandardScene.Core/CarTypes/MultiWheelLifterFields.cs b/StandardScene.Core/CarTypes/MultiWheelLifterFields.cs new file mode 100644 index 0000000..ad2964a --- /dev/null +++ b/StandardScene.Core/CarTypes/MultiWheelLifterFields.cs @@ -0,0 +1,35 @@ +namespace StandardScene.CarTypes +{ + // MultiWheelLifter 绯诲瓧娈佃銆傚師鍐呰仈浜 MultiWheelLifterCar.cs锛涜溅鍨嬫寜骞冲彴鎷嗗垎 + // 锛圡ultiWheelLifterCar鈫扢agnetic锛孧ultiVehicleCar鈫扱rLidar锛夊悗涓や晶鍏辩敤锛屾晠涓嬫矇鍩哄骇銆 + + class MultiWheelLifterCarFields : BasicCarFields + { + + } + + class MultiWheelLifterSiteFields : BasicSiteFields + { + public bool ChangeAvoidanceParam = false; + public bool ClampClose = false; + public bool NeedRotate = false; + } + + class MultiWheelLifterTrackFields : BasicTrackFields + { + public int SleepTime = 0; + public float TrayTarget = 0; + public int MagnetChoose = 0; + public bool MultiVehicleSync = false; + } + + class MultiWheelLifterPlanFields : BasicPlanFields + { + public float AngleTarget = 0; + public float TireNum = 0; + public bool FrontLidarDetect = false; + public bool FirstTire = false; + public bool Reverse = false; + + } +} diff --git a/StandardScene.Core/CarTypes/VehicleMonitor.Designer.cs b/StandardScene.Core/CarTypes/VehicleMonitor.Designer.cs new file mode 100644 index 0000000..4b2dccf --- /dev/null +++ b/StandardScene.Core/CarTypes/VehicleMonitor.Designer.cs @@ -0,0 +1,46 @@ +namespace StandardScene +{ + partial class VehicleMonitor + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.SuspendLayout(); + // + // VehicleMonitor + // + this.AutoScaleDimensions = new System.Drawing.SizeF(9F, 18F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(1800, 900); + this.Name = "VehicleMonitor"; + this.Text = "杞﹁締鐘舵佺洃鎺х郴缁"; + this.ResumeLayout(false); + } + + #endregion + } +} + diff --git a/StandardScene.Core/CarTypes/VehicleMonitor.cs b/StandardScene.Core/CarTypes/VehicleMonitor.cs new file mode 100644 index 0000000..1239b41 --- /dev/null +++ b/StandardScene.Core/CarTypes/VehicleMonitor.cs @@ -0,0 +1,1710 @@ +using SimpleLite; +using SimpleLite.RCS; +using SimpleLite.RCS.CarTypes; +using SimpleCore; +using SimpleCore.Library; +using StandardScene.Model; +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Diagnostics; +using System.Drawing; +using System.Drawing.Drawing2D; +using System.Linq; +using System.Reflection; +using System.Windows.Forms; +using static System.Windows.Forms.VisualStyles.VisualStyleElement.TextBox; + +namespace StandardScene +{ + public partial class VehicleMonitor : Form + { + private static VehicleMonitor instance; + private static readonly object lockObject = new object(); + + private Timer updateTimer; + private DataGridView vehicleGrid; + private Label titleLabel; + private Panel statusPanel; + private Panel alarmPanel; + private Panel actionPanel; + private Label totalLabel; + private Label onlineLabel; + private Label offlineLabel; + private Label runningLabel; + private Label chargingLabel; + private Label faultLabel; + private Label timeLabel; + private Label alarmLabel; + + private Button onlineButton; + private Button offlineButton; + private Button returnButton; + private Button simulateAlarmButton; + private Button cancelSimAlarmButton; + private Button toggleSecondaryButton; + + private Timer alarmScrollTimer; + private string lastAlarmText; + private string pendingAlarmText; + private Color pendingAlarmColor = Color.FromArgb(255, 60, 60); + private bool pendingAlarmScrolling; + private readonly List simulatedAlarmNames = new List(); + private int simulatedAlarmIndex = 1; + private const string SimulatedAlarmInfo = "鑴辫建寮傚父"; + + private bool showSecondaryColumns; + + private readonly Dictionary rowHeightCache = new Dictionary(); + private readonly HashSet selectedCarIds = new HashSet(); + private readonly List statusCards = new List(); + private string lastSortColumnName; + private SortOrder lastSortOrder = SortOrder.None; + private int lastFirstDisplayedRowIndex = -1; + + private CheckBox selectAllCheckBox; + private bool suppressSelectSync; + + private const int TitleBarHeight = 70; + private const int AlarmPanelHeight = 42; + private const int AlarmPanelMarginTop = 9; + private const int StatusPanelHeight = 90; + private const int ActionPanelHeight = 60; + private const int VerticalSpacing = 16; + private const string QuickOperationHeaderText = "杞︿綋蹇嵎鎿嶄綔"; + + private const int CombinedPanelPadding = 12; + private const int CombinedPanelSpacing = 10; + + private Panel statusActionPanel; + + private readonly Color pageBackColor = Color.FromArgb(236, 239, 243); + private readonly Color panelBackColor = Color.FromArgb(248, 249, 251); + private readonly Color panelBorderColor = Color.FromArgb(185, 192, 199); + private readonly Color titleTextColor = Color.FromArgb(35, 35, 40); + private readonly Color subTextColor = Color.FromArgb(110, 120, 130); + private readonly Color gridTextColor = Color.FromArgb(50, 55, 60); + private readonly Color gridHeaderBackColor = Color.FromArgb(238, 241, 245); + private readonly Color gridHeaderTextColor = Color.FromArgb(70, 80, 90); + private readonly Color gridAltRowColor = Color.FromArgb(246, 248, 251); + private readonly Color gridSelectionBackColor = Color.FromArgb(224, 236, 248); + private readonly Color gridSelectionTextColor = Color.FromArgb(30, 40, 50); + private readonly Color buttonBackColor = Color.FromArgb(246, 248, 251); + private readonly Color buttonBorderColor = Color.FromArgb(190, 198, 206); + private readonly Color buttonHoverColor = Color.FromArgb(238, 242, 247); + private readonly Color buttonDownColor = Color.FromArgb(228, 234, 240); + private readonly Color buttonTextColor = Color.FromArgb(40, 60, 80); + private readonly Color subtleLineColor = Color.FromArgb(230, 235, 240); + private readonly Color combinedBorderColor = Color.Black; + private readonly Color statusCardBackColor = Color.FromArgb(236, 239, 243); + private readonly Color statusCardBorderColor = Color.Black; + private const float OuterBorderWidth = 2f; + + private sealed class NoFocusPanel : Panel + { + public NoFocusPanel() + { + SetStyle(ControlStyles.Selectable, false); + SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.OptimizedDoubleBuffer | ControlStyles.UserPaint, true); + TabStop = false; + UpdateStyles(); + } + } + + public VehicleMonitor() + { + InitializeComponent(); + InitializeCustomComponents(); + } + + /// + /// 鏄剧ず鎴栨縺娲昏溅杈嗙洃鎺х獥鍙 + /// + public static void ShowMonitor() + { + lock (lockObject) + { + if (instance == null || instance.IsDisposed) + { + instance = new VehicleMonitor(); + } + + if (!instance.Visible) + { + instance.Show(); + } + + instance.WindowState = FormWindowState.Normal; + instance.BringToFront(); + instance.Activate(); + } + } + + private void InitializeCustomComponents() + { + // 璁剧疆绐椾綋灞炴 + this.Text = "杞﹁締鐘舵佺洃鎺х郴缁"; + this.WindowState = FormWindowState.Maximized; + this.BackColor = pageBackColor; + this.ForeColor = titleTextColor; + this.FormBorderStyle = FormBorderStyle.Sizable; + this.DoubleBuffered = true; + SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.OptimizedDoubleBuffer | ControlStyles.UserPaint, true); + UpdateStyles(); + + // 鍒涘缓鏍囬鏍 + CreateTitleBar(); + + // 鍒涘缓鎶ヨ婊氬姩鏉 + CreateAlarmTickerPanel(); + + // 鍒涘缓鐘舵佺粺璁¢潰鏉 + CreateStatusPanel(); + + // 鍒涘缓涓棿鎿嶄綔闈㈡澘 + CreateActionPanel(); + + // 鍒涘缓杞﹁締鏁版嵁琛ㄦ牸 + CreateVehicleGrid(); + + // 鍒涘缓鏇存柊瀹氭椂鍣 + updateTimer = new Timer + { + Interval = 1000, // 1绉掓洿鏂颁竴娆 + Enabled = true + }; + updateTimer.Tick += UpdateTimer_Tick; + + // 鍒濆鏇存柊 + UpdateVehicleData(); + } + + private void CreateTitleBar() + { + // 鏍囬鑳屾櫙闈㈡澘 + Panel titlePanel = new Panel + { + Location = new System.Drawing.Point(0, 0), + Size = new System.Drawing.Size(this.ClientSize.Width, TitleBarHeight), + BackColor = panelBackColor, + Anchor = AnchorStyles.Top | AnchorStyles.Left | AnchorStyles.Right + }; + AttachBottomBorder(titlePanel); + + // 鏍囬鏍囩 + titleLabel = new Label + { + Text = "杞﹁締鐘舵佺洃鎺х郴缁", + Font = new Font("寰蒋闆呴粦", 24F, FontStyle.Bold), + ForeColor = titleTextColor, + Location = new System.Drawing.Point(28, 16), + Size = new System.Drawing.Size(600, 45), + AutoSize = false + }; + titlePanel.Controls.Add(titleLabel); + + // 鏃堕棿鏍囩 + timeLabel = new Label + { + Text = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"), + Font = new Font("寰蒋闆呴粦", 11F), + ForeColor = subTextColor, + Location = new System.Drawing.Point(this.ClientSize.Width - 250, 25), + Size = new System.Drawing.Size(220, 25), + TextAlign = ContentAlignment.MiddleRight, + Anchor = AnchorStyles.Top | AnchorStyles.Right + }; + titlePanel.Controls.Add(timeLabel); + + this.Controls.Add(titlePanel); + } + + private void CreateAlarmTickerPanel() + { + alarmPanel = new NoFocusPanel + { + Location = new System.Drawing.Point(20, GetAlarmPanelTop()), + Size = new System.Drawing.Size(this.ClientSize.Width - 40, AlarmPanelHeight), + BackColor = panelBackColor, + BorderStyle = BorderStyle.None, + Anchor = AnchorStyles.Top | AnchorStyles.Left | AnchorStyles.Right + }; + alarmPanel.Paint += DrawPanelBorder; + + alarmLabel = new Label + { + AutoSize = true, + ForeColor = Color.FromArgb(235, 80, 80), + Font = new Font("寰蒋闆呴粦", 14F, FontStyle.Bold), + Location = new System.Drawing.Point(alarmPanel.Width, 6) + }; + + alarmPanel.Controls.Add(alarmLabel); + this.Controls.Add(alarmPanel); + + alarmScrollTimer = new Timer + { + Interval = 13, + Enabled = true + }; + alarmScrollTimer.Tick += AlarmScrollTimer_Tick; + } + + private void CreateStatusPanel() + { + statusPanel = new NoFocusPanel + { + Location = new System.Drawing.Point(20, GetStatusPanelTop()), + Size = new System.Drawing.Size(GetStatusPanelWidth(6), StatusPanelHeight), + BackColor = panelBackColor, + BorderStyle = BorderStyle.None, + Anchor = AnchorStyles.Top | AnchorStyles.Left | AnchorStyles.Right + }; + statusPanel.Paint += DrawPanelBorder; + + int xPos = 0; + int yPos = 0; + int cardWidth = 140; + int cardHeight = 60; + + // 鎬昏溅杈嗘暟 + totalLabel = CreateStatusCard("鎬昏溅杈", xPos, yPos, cardWidth, cardHeight, titleTextColor, panelBackColor); + + // 鍦ㄧ嚎杞﹁締 + onlineLabel = CreateStatusCard("鍦ㄧ嚎", xPos, yPos, cardWidth, cardHeight, Color.FromArgb(136, 205, 246), panelBackColor); + + // 绂荤嚎杞﹁締 + offlineLabel = CreateStatusCard("绂荤嚎", xPos, yPos, cardWidth, cardHeight, Color.FromArgb(150, 150, 150), panelBackColor); + + // 杩愯涓 + runningLabel = CreateStatusCard("杩愯涓", xPos, yPos, cardWidth, cardHeight, Color.FromArgb(0, 180, 90), panelBackColor); + + // 鍏呯數涓 + chargingLabel = CreateStatusCard("鍏呯數涓", xPos, yPos, cardWidth, cardHeight, Color.FromArgb(255, 220, 0), panelBackColor); + + // 鏁呴殰 + faultLabel = CreateStatusCard("鏁呴殰", xPos, yPos, cardWidth, cardHeight, Color.FromArgb(255, 80, 80), panelBackColor); + + this.Controls.Add(statusPanel); + UpdateStatusPanelLayout(); + } + + private Label CreateStatusCard(string text, int x, int y, int width, int height, Color textColor, Color bgColor) + { + Panel card = new Panel + { + Location = new System.Drawing.Point(x, y), + Size = new System.Drawing.Size(width, height), + BackColor = statusCardBackColor, + BorderStyle = BorderStyle.None + }; + card.Paint += DrawStatusCardBorder; + + // 鐘舵佹寚绀虹伅锛堝渾褰級 + Color indicatorColor = textColor; // 淇濆瓨棰滆壊寮曠敤 + Panel indicator = new Panel + { + Location = new System.Drawing.Point(12, 20), + Size = new System.Drawing.Size(14, 14), + BackColor = Color.Transparent + }; + // 浣挎寚绀虹伅鍛堝渾褰 + indicator.Paint += (s, evt) => + { + using (SolidBrush brush = new SolidBrush(indicatorColor)) + { + evt.Graphics.SmoothingMode = SmoothingMode.AntiAlias; + evt.Graphics.FillEllipse(brush, 0, 0, indicator.Width, indicator.Height); + } + }; + card.Controls.Add(indicator); + + // 鏂囨湰鏍囩 + var label = new Label + { + Text = $"{text}\n0", + Font = new Font("寰蒋闆呴粦", 11F, FontStyle.Bold), + ForeColor = textColor, + Dock = DockStyle.Fill, + Padding = new Padding(35, 8, 10, 8), + TextAlign = ContentAlignment.MiddleLeft + }; + card.Controls.Add(label); + + // 灏哖anel娣诲姞鍒皊tatusPanel锛屼絾杩斿洖Label浠ヤ究鏇存柊 + statusPanel.Controls.Add(card); + statusCards.Add(card); + return label; + } + + private void CreateActionPanel() + { + statusActionPanel = new NoFocusPanel + { + Location = new System.Drawing.Point(20, statusPanel.Bottom + VerticalSpacing), + Size = new System.Drawing.Size(this.ClientSize.Width - 40, this.ClientSize.Height - (statusPanel.Bottom + VerticalSpacing + 20)), + BackColor = panelBackColor, + BorderStyle = BorderStyle.None, + Anchor = AnchorStyles.Top | AnchorStyles.Bottom | AnchorStyles.Left | AnchorStyles.Right + }; + statusActionPanel.Paint += DrawCombinedBorder; + this.Controls.Add(statusActionPanel); + + actionPanel = new NoFocusPanel + { + Location = new System.Drawing.Point(CombinedPanelPadding, CombinedPanelPadding), + Size = new System.Drawing.Size(statusActionPanel.Width - CombinedPanelPadding * 2, ActionPanelHeight), + BackColor = panelBackColor, + BorderStyle = BorderStyle.None, + Anchor = AnchorStyles.Top | AnchorStyles.Left | AnchorStyles.Right + }; + + var flow = new FlowLayoutPanel + { + Dock = DockStyle.Fill, + FlowDirection = FlowDirection.LeftToRight, + WrapContents = false, + Padding = new Padding(15, 10, 15, 10) + }; + + onlineButton = CreateActionButton("灏忚溅涓婄嚎-鎵归噺"); + offlineButton = CreateActionButton("灏忚溅涓嬬嚎-鎵归噺"); + returnButton = CreateActionButton("鏁呴殰杩斿巶-鎵归噺"); + simulateAlarmButton = CreateActionButton("妯℃嫙鎶ヨ"); + cancelSimAlarmButton = CreateActionButton("鍙栨秷妯℃嫙鎶ヨ"); + toggleSecondaryButton = CreateActionButton("鏄剧ず娆¤鍒"); + + onlineButton.Click += (s, e) => ExecuteForSelectedCars(BringCarOnline, "灏忚溅涓婄嚎"); + offlineButton.Click += (s, e) => ExecuteForSelectedCars(BringCarOffline, "灏忚溅涓嬬嚎"); + returnButton.Click += (s, e) => ExecuteForSelectedCars(ReturnCarToFactory, "鏁呴殰杩斿巶"); + simulateAlarmButton.Click += (s, e) => AddSimulatedAlarm(); + cancelSimAlarmButton.Click += (s, e) => ClearSimulatedAlarms(); + toggleSecondaryButton.Click += (s, e) => ToggleSecondaryColumns(); + + flow.Controls.Add(onlineButton); + flow.Controls.Add(offlineButton); + flow.Controls.Add(returnButton); + flow.Controls.Add(simulateAlarmButton); + flow.Controls.Add(cancelSimAlarmButton); + flow.Controls.Add(toggleSecondaryButton); + + actionPanel.Controls.Add(flow); + statusActionPanel.Controls.Add(actionPanel); + } + + private Button CreateActionButton(string text) + { + var button = new Button + { + Text = text, + Width = 130, + Height = 38, + Margin = new Padding(8, 0, 8, 0), + FlatStyle = FlatStyle.Flat, + BackColor = buttonBackColor, + ForeColor = buttonTextColor, + Font = new Font("寰蒋闆呴粦", 10F, FontStyle.Bold) + }; + + button.FlatAppearance.BorderColor = buttonBorderColor; + button.FlatAppearance.BorderSize = 1; + button.FlatAppearance.MouseOverBackColor = buttonHoverColor; + button.FlatAppearance.MouseDownBackColor = buttonDownColor; + return button; + } + + private void CreateVehicleGrid() + { + vehicleGrid = new DataGridView + { + Location = new System.Drawing.Point(CombinedPanelPadding, actionPanel.Bottom + CombinedPanelSpacing), + Size = new System.Drawing.Size(statusActionPanel.Width - CombinedPanelPadding * 2, statusActionPanel.Height - (actionPanel.Bottom + CombinedPanelSpacing + CombinedPanelPadding)), + Anchor = AnchorStyles.Top | AnchorStyles.Bottom | AnchorStyles.Left | AnchorStyles.Right, + AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.AllCells, + AllowUserToAddRows = false, + AllowUserToDeleteRows = false, + AllowUserToResizeRows = true, + ReadOnly = false, + SelectionMode = DataGridViewSelectionMode.FullRowSelect, + MultiSelect = false, + BackgroundColor = panelBackColor, + GridColor = panelBorderColor, + BorderStyle = BorderStyle.FixedSingle, + Font = new Font("寰蒋闆呴粦", 10F), + EnableHeadersVisualStyles = false, + RowHeadersVisible = false, + ColumnHeadersHeight = 42, + ColumnHeadersHeightSizeMode = DataGridViewColumnHeadersHeightSizeMode.DisableResizing, // 绂佹璋冩暣楂樺害 + DefaultCellStyle = new DataGridViewCellStyle + { + BackColor = panelBackColor, + ForeColor = gridTextColor, + SelectionBackColor = gridSelectionBackColor, + SelectionForeColor = gridSelectionTextColor, + Padding = new Padding(5, 2, 5, 2) + }, + ColumnHeadersDefaultCellStyle = new DataGridViewCellStyle + { + BackColor = gridHeaderBackColor, + ForeColor = gridHeaderTextColor, + Font = new Font("寰蒋闆呴粦", 11F, FontStyle.Bold), + Alignment = DataGridViewContentAlignment.MiddleCenter, + Padding = new Padding(5, 8, 5, 8), // 澧炲姞涓婁笅鍐呰竟璺 + WrapMode = DataGridViewTriState.False // 绂佹鏂囧瓧鎹㈣ + }, + AlternatingRowsDefaultCellStyle = new DataGridViewCellStyle + { + BackColor = gridAltRowColor + } + }; + + SetGridDoubleBuffered(vehicleGrid); + vehicleGrid.RowTemplate.Height = (int)(vehicleGrid.RowTemplate.Height * 1.5); + + // 娣诲姞鍒 + var selectColumn = new DataGridViewCheckBoxColumn + { + Name = "Select", + HeaderText = "", + Width = 40, + ReadOnly = false, + SortMode = DataGridViewColumnSortMode.NotSortable + }; + vehicleGrid.Columns.Add(selectColumn); + + var remoteColumn = new DataGridViewButtonColumn + { + Name = "Remote", + HeaderText = "杩滅▼", + Width = 60, + Text = "杩滅▼", + UseColumnTextForButtonValue = true, + FlatStyle = FlatStyle.Flat, + SortMode = DataGridViewColumnSortMode.NotSortable + }; + vehicleGrid.Columns.Add(remoteColumn); + + var onlineColumn = new DataGridViewButtonColumn + { + Name = "Online", + HeaderText = "涓婄嚎", + Width = 60, + Text = "涓婄嚎", + UseColumnTextForButtonValue = true, + FlatStyle = FlatStyle.Flat, + SortMode = DataGridViewColumnSortMode.NotSortable + }; + vehicleGrid.Columns.Add(onlineColumn); + + var offlineColumn = new DataGridViewButtonColumn + { + Name = "Offline", + HeaderText = "涓嬬嚎", + Width = 60, + Text = "涓嬬嚎", + UseColumnTextForButtonValue = true, + FlatStyle = FlatStyle.Flat, + SortMode = DataGridViewColumnSortMode.NotSortable + }; + vehicleGrid.Columns.Add(offlineColumn); + + var returnColumn = new DataGridViewButtonColumn + { + Name = "Return", + HeaderText = "杩斿巶", + Width = 60, + Text = "杩斿巶", + UseColumnTextForButtonValue = true, + FlatStyle = FlatStyle.Flat, + SortMode = DataGridViewColumnSortMode.NotSortable + }; + vehicleGrid.Columns.Add(returnColumn); + + vehicleGrid.Columns.Add("CarId", "杞﹁締ID"); + vehicleGrid.Columns.Add("CarName", "杞﹁締鍚嶇О"); + vehicleGrid.Columns.Add("CarType", "杞﹁締绫诲瀷"); + vehicleGrid.Columns.Add("Status", "杩愯鐘舵"); + vehicleGrid.Columns.Add("IsOnline", "鍦ㄧ嚎"); + vehicleGrid.Columns.Add("X", "X鍧愭爣"); + vehicleGrid.Columns.Add("Y", "Y鍧愭爣"); + vehicleGrid.Columns.Add("Theta", "瑙掑害(掳)"); + vehicleGrid.Columns.Add("Battery", "鐢甸噺"); + vehicleGrid.Columns.Add("BatteryBar", "鐢甸噺杩涘害"); + vehicleGrid.Columns.Add("Voltage", "鐢靛帇(V)"); + vehicleGrid.Columns.Add("Current", "鐢垫祦(A)"); + vehicleGrid.Columns.Add("Speed", "閫熷害(m/s)"); + vehicleGrid.Columns.Add("SiteId", "绔欑偣ID"); + vehicleGrid.Columns.Add("TaskStatus", "浠诲姟鐘舵"); + vehicleGrid.Columns.Add("CPUUsage", "CPU"); + vehicleGrid.Columns.Add("CPUBar", "CPU杩涘害"); + vehicleGrid.Columns.Add("Memory", "鍐呭瓨"); + vehicleGrid.Columns.Add("Address", "IP鍦板潃"); + + // 璁剧疆鏁板煎垪鐨勫榻愭柟寮 + vehicleGrid.Columns["Select"].DefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleCenter; + vehicleGrid.Columns["Remote"].DefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleCenter; + vehicleGrid.Columns["Online"].DefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleCenter; + vehicleGrid.Columns["Offline"].DefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleCenter; + vehicleGrid.Columns["Return"].DefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleCenter; + ApplyGridButtonStyle(vehicleGrid.Columns["Remote"]); + ApplyGridButtonStyle(vehicleGrid.Columns["Online"]); + ApplyGridButtonStyle(vehicleGrid.Columns["Offline"]); + ApplyGridButtonStyle(vehicleGrid.Columns["Return"]); + vehicleGrid.Columns["X"].DefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleRight; + vehicleGrid.Columns["Y"].DefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleRight; + vehicleGrid.Columns["Theta"].DefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleRight; + vehicleGrid.Columns["Battery"].DefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleRight; + vehicleGrid.Columns["Voltage"].DefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleRight; + vehicleGrid.Columns["Current"].DefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleRight; + vehicleGrid.Columns["Speed"].DefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleRight; + vehicleGrid.Columns["CPUUsage"].DefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleRight; + vehicleGrid.Columns["Memory"].DefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleRight; + + // 璁剧疆杩涘害鏉″垪涓哄彧璇 + vehicleGrid.Columns["BatteryBar"].ReadOnly = true; + vehicleGrid.Columns["CPUBar"].ReadOnly = true; + + foreach (DataGridViewColumn column in vehicleGrid.Columns) + { + if (column.Name == "Select" || column.Name == "Remote" || column.Name == "Online" || column.Name == "Offline" || column.Name == "Return") + { + column.ReadOnly = column.Name != "Select"; + continue; + } + + column.ReadOnly = true; + } + + foreach (DataGridViewColumn column in vehicleGrid.Columns) + { + if (column.SortMode == DataGridViewColumnSortMode.NotSortable) continue; + column.SortMode = DataGridViewColumnSortMode.Automatic; + } + + ApplySecondaryColumnVisibility(); + + // 娉ㄥ唽CellFormatting浜嬩欢鏉ョ粯鍒惰繘搴︽潯 + vehicleGrid.CellFormatting += VehicleGrid_CellFormatting; + vehicleGrid.CellPainting += VehicleGrid_CellPainting; + vehicleGrid.CellContentClick += VehicleGrid_CellContentClick; + vehicleGrid.CurrentCellDirtyStateChanged += VehicleGrid_CurrentCellDirtyStateChanged; + vehicleGrid.ColumnWidthChanged += VehicleGrid_ColumnWidthChanged; + vehicleGrid.Scroll += VehicleGrid_Scroll; + vehicleGrid.SizeChanged += VehicleGrid_SizeChanged; + + InitializeSelectAllCheckBox(); + + statusActionPanel.Controls.Add(vehicleGrid); + UpdateActionGridPanelLayout(); + } + + private void AttachBottomBorder(Panel panel) + { + if (panel == null) return; + panel.Paint += (s, e) => + { + using (var pen = new Pen(subtleLineColor)) + { + e.Graphics.DrawLine(pen, 0, panel.Height - 1, panel.Width, panel.Height - 1); + } + }; + } + + private void DrawPanelBorder(object sender, PaintEventArgs e) + { + if (!(sender is Panel panel)) return; + using (var pen = new Pen(panelBorderColor, OuterBorderWidth)) + { + var rect = panel.ClientRectangle; + rect.Width -= 1; + rect.Height -= 1; + e.Graphics.DrawRectangle(pen, rect); + } + } + + private void DrawCombinedBorder(object sender, PaintEventArgs e) + { + if (!(sender is Panel panel)) return; + using (var pen = new Pen(combinedBorderColor, OuterBorderWidth)) + { + var rect = panel.ClientRectangle; + rect.Width -= 1; + rect.Height -= 1; + e.Graphics.DrawRectangle(pen, rect); + } + } + + private void DrawStatusCardBorder(object sender, PaintEventArgs e) + { + if (!(sender is Panel panel)) return; + using (var pen = new Pen(statusCardBorderColor)) + { + var rect = panel.ClientRectangle; + rect.Width -= 1; + rect.Height -= 1; + e.Graphics.DrawRectangle(pen, rect); + } + } + + private void SetGridDoubleBuffered(DataGridView grid) + { + typeof(DataGridView).GetProperty("DoubleBuffered", BindingFlags.Instance | BindingFlags.NonPublic) + ?.SetValue(grid, true, null); + } + + private void ApplyGridButtonStyle(DataGridViewColumn column) + { + if (column == null) return; + + column.DefaultCellStyle.BackColor = buttonBackColor; + column.DefaultCellStyle.ForeColor = buttonTextColor; + column.DefaultCellStyle.SelectionBackColor = buttonHoverColor; + column.DefaultCellStyle.SelectionForeColor = buttonTextColor; + } + + private void VehicleGrid_CellFormatting(object sender, DataGridViewCellFormattingEventArgs e) + { + if (vehicleGrid.Columns[e.ColumnIndex].Name == "BatteryBar" || + vehicleGrid.Columns[e.ColumnIndex].Name == "CPUBar") + { + e.Value = ""; // 娓呯┖鏂囨湰锛屾垜浠皢缁樺埗杩涘害鏉 + } + } + + private void VehicleGrid_CellPainting(object sender, DataGridViewCellPaintingEventArgs e) + { + if (e.RowIndex == -1) + { + var columnName = vehicleGrid.Columns[e.ColumnIndex].Name; + if (columnName == "Remote" || columnName == "Online" || columnName == "Offline" || columnName == "Return") + { + if (columnName == "Remote") + { + var firstRect = vehicleGrid.GetCellDisplayRectangle(vehicleGrid.Columns["Remote"].Index, -1, true); + var lastRect = vehicleGrid.GetCellDisplayRectangle(vehicleGrid.Columns["Return"].Index, -1, true); + if (firstRect.Width > 0 && lastRect.Width > 0) + { + var mergedRect = new Rectangle(firstRect.Left, firstRect.Top, lastRect.Right - firstRect.Left, firstRect.Height); + using (var backBrush = new SolidBrush(vehicleGrid.ColumnHeadersDefaultCellStyle.BackColor)) + using (var textBrush = new SolidBrush(vehicleGrid.ColumnHeadersDefaultCellStyle.ForeColor)) + using (var borderPen = new Pen(panelBorderColor)) + using (var format = new StringFormat { Alignment = StringAlignment.Center, LineAlignment = StringAlignment.Center }) + { + e.Graphics.FillRectangle(backBrush, mergedRect); + var font = vehicleGrid.ColumnHeadersDefaultCellStyle.Font ?? vehicleGrid.Font; + e.Graphics.DrawString(QuickOperationHeaderText, font, textBrush, mergedRect, format); + e.Graphics.DrawRectangle(borderPen, mergedRect.X, mergedRect.Y, mergedRect.Width - 1, mergedRect.Height - 1); + } + } + } + + e.Handled = true; + return; + } + } + + if (e.RowIndex < 0) return; + + // 缁樺埗鐢甸噺杩涘害鏉 + if (e.ColumnIndex == vehicleGrid.Columns["BatteryBar"].Index) + { + e.Paint(e.CellBounds, DataGridViewPaintParts.All & ~DataGridViewPaintParts.ContentForeground); + + double battery = 0; + if (e.RowIndex < vehicleGrid.Rows.Count) + { + var batteryCell = vehicleGrid.Rows[e.RowIndex].Cells["Battery"]; + if (batteryCell != null && batteryCell.Value != null) + { + string batteryStr = batteryCell.Value.ToString().Replace("%", ""); + double.TryParse(batteryStr, out battery); + } + } + + // 鏍规嵁鐢甸噺璁剧疆棰滆壊 + Color barColor; + if (battery < 20) + barColor = Color.FromArgb(255, 80, 80); + else if (battery < 30) + barColor = Color.FromArgb(255, 200, 0); + else if (battery < 50) + barColor = Color.FromArgb(255, 220, 100); + else + barColor = Color.FromArgb(0, 220, 100); + + int barWidth = (int)((e.CellBounds.Width - 10) * battery / 100.0); + Rectangle barRect = new Rectangle(e.CellBounds.X + 5, e.CellBounds.Y + 4, barWidth, e.CellBounds.Height - 8); + + using (SolidBrush brush = new SolidBrush(barColor)) + { + e.Graphics.FillRectangle(brush, barRect); + } + + // 缁樺埗杈规 + using (Pen pen = new Pen(panelBorderColor, 1)) + { + e.Graphics.DrawRectangle(pen, e.CellBounds.X + 5, e.CellBounds.Y + 4, e.CellBounds.Width - 10, e.CellBounds.Height - 8); + } + + e.Handled = true; + } + + // 缁樺埗CPU杩涘害鏉 + if (e.ColumnIndex == vehicleGrid.Columns["CPUBar"].Index) + { + e.Paint(e.CellBounds, DataGridViewPaintParts.All & ~DataGridViewPaintParts.ContentForeground); + + double cpuUsage = 0; + if (e.RowIndex < vehicleGrid.Rows.Count) + { + var cpuCell = vehicleGrid.Rows[e.RowIndex].Cells["CPUUsage"]; + if (cpuCell != null && cpuCell.Value != null) + { + string cpuStr = cpuCell.Value.ToString(); + if (cpuStr != "N/A") + { + cpuStr = cpuStr.Replace("%", ""); + double.TryParse(cpuStr, out cpuUsage); + } + } + } + + // 鏍规嵁CPU浣跨敤鐜囪缃鑹 + Color barColor; + if (cpuUsage > 80) + barColor = Color.FromArgb(255, 80, 80); + else if (cpuUsage > 60) + barColor = Color.FromArgb(255, 200, 0); + else if (cpuUsage > 40) + barColor = Color.FromArgb(255, 220, 100); + else + barColor = Color.FromArgb(0, 220, 100); + + int barWidth = (int)((e.CellBounds.Width - 10) * cpuUsage / 100.0); + Rectangle barRect = new Rectangle(e.CellBounds.X + 5, e.CellBounds.Y + 4, barWidth, e.CellBounds.Height - 8); + + using (SolidBrush brush = new SolidBrush(barColor)) + { + e.Graphics.FillRectangle(brush, barRect); + } + + // 缁樺埗杈规 + using (Pen pen = new Pen(panelBorderColor, 1)) + { + e.Graphics.DrawRectangle(pen, e.CellBounds.X + 5, e.CellBounds.Y + 4, e.CellBounds.Width - 10, e.CellBounds.Height - 8); + } + + e.Handled = true; + } + } + + private void UpdateTimer_Tick(object sender, EventArgs e) + { + UpdateVehicleData(); + // 鏇存柊鏃堕棿鏄剧ず + if (timeLabel != null) + { + timeLabel.Text = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"); + } + Invalidate(true); + } + + private void UpdateVehicleData() + { + try + { + CaptureGridViewState(); + + var cars = SimpleLib.GetAllCars().OfType().ToList(); + if (string.IsNullOrEmpty(lastSortColumnName)) + { + cars = cars.OrderBy(c => c.id).ToList(); + } + + // 鏇存柊缁熻淇℃伅 + UpdateStatistics(cars); + UpdateAlarmTicker(cars); + + // 鏇存柊琛ㄦ牸鏁版嵁 + vehicleGrid.SuspendLayout(); + vehicleGrid.Rows.Clear(); + foreach (var car in cars) + { + var carInfo = CarBaseInfo.FromCar(car); + var isOnline = carInfo.IsOnline; + var state = carInfo.CurrState; + + // 鑾峰彇CPU浣跨敤鐜囷紙濡傛灉瀛樺湪锛 + string cpuUsage = "N/A"; + double cpuValue = 0; + if (car.status.enums.ContainsKey("CPUUsage")) + { + cpuUsage = car.status.enums["CPUUsage"]; + double.TryParse(cpuUsage, out cpuValue); + } + else if (car.status.enums.ContainsKey("CpuUsage")) + { + cpuUsage = car.status.enums["CpuUsage"]; + double.TryParse(cpuUsage, out cpuValue); + } + else if (car.status.enums.ContainsKey("CPU")) + { + cpuUsage = car.status.enums["CPU"]; + double.TryParse(cpuUsage, out cpuValue); + } + + // 鑾峰彇鍐呭瓨浣跨敤鐜囷紙濡傛灉瀛樺湪锛 + string memoryUsage = "N/A"; + if (car.status.enums.ContainsKey("MemoryUsage")) + { + memoryUsage = car.status.enums["MemoryUsage"] + "%"; + } + else if (car.status.enums.ContainsKey("Memory")) + { + memoryUsage = car.status.enums["Memory"] + "%"; + } + else if (car.status.enums.ContainsKey("RAM")) + { + memoryUsage = car.status.enums["RAM"] + "%"; + } + + // 鑾峰彇浠诲姟鐘舵 + string taskStatus = GetTaskStatus(car); + + // 鑾峰彇鐢甸噺 + double battery = carInfo.Battery; + if (car.status.enums.ContainsKey("Soc")) + { + double.TryParse(car.status.enums["Soc"], out battery); + } + else if (car.status.enums.ContainsKey("soc")) + { + double.TryParse(car.status.enums["soc"], out battery); + } + + // 鏍煎紡鍖栫姸鎬佹樉绀 + string statusDisplay = GetStatusDisplay(state); + string onlineDisplay = isOnline ? "鈼" : "鈼"; + string batteryDisplay = battery.ToString("F1") + "%"; + string carId = car.id.ToString(); + + int rowIndex = vehicleGrid.Rows.Add( + selectedCarIds.Contains(carId), + "杩滅▼", + "涓婄嚎", + "涓嬬嚎", + "杩斿巶", + car.id.ToString(), + car.name, + carInfo.CarType, + statusDisplay, + onlineDisplay, + car.x.ToString("F2"), + car.y.ToString("F2"), + (car.th * 180 / Math.PI).ToString("F1"), // 杞崲涓哄害鏁 + batteryDisplay, + "", // BatteryBar - 鐢盋ellPainting缁樺埗 + carInfo.Voltage.ToString("F2"), + carInfo.ElectricCurrent.ToString("F2"), + car.speed.ToString("F2"), + car.GetLastSite().ToString(), + taskStatus, + cpuUsage == "N/A" ? cpuUsage : cpuValue.ToString("F1") + "%", + "", // CPUBar - 鐢盋ellPainting缁樺埗 + memoryUsage, + carInfo.Address + ); + + // 鏍规嵁鐘舵佽缃棰滆壊 + var row = vehicleGrid.Rows[rowIndex]; + if (rowHeightCache.TryGetValue(carId, out var rowHeight)) + { + row.Height = rowHeight; + } + SetRowColor(row, state, isOnline, battery, cpuValue); + } + + ApplyGridViewState(); + vehicleGrid.ResumeLayout(); + } + catch (Exception ex) + { + Diagnosis.Log($"鏇存柊杞﹁締鏁版嵁寮傚父: {ex.Message}", "VehicleMonitor", true); + } + } + + private string GetStatusDisplay(string state) + { + switch (state) + { + case "Running": + return "杩愯涓"; + case "Charging": + return "鍏呯數涓"; + case "Faulting": + return "鏁呴殰"; + case "Idle": + return "绌洪棽"; + case "Offline": + return "绂荤嚎"; + default: + return state; + } + } + + private void UpdateStatistics(List cars) + { + int total = cars.Count; + int online = cars.Count(c => CarBaseInfo.FromCar(c).IsOnline); + int offline = total - online; + int running = cars.Count(c => + { + var info = CarBaseInfo.FromCar(c); + return info.IsOnline && info.CurrState == "Running"; + }); + int charging = cars.Count(c => + { + var info = CarBaseInfo.FromCar(c); + return info.IsOnline && info.CurrState == "Charging"; + }); + int fault = cars.Count(c => + { + var info = CarBaseInfo.FromCar(c); + return info.IsOnline && info.CurrState == "Faulting"; + }); + + totalLabel.Text = $"鎬昏溅杈哱n{total}"; + onlineLabel.Text = $"鍦ㄧ嚎\n{online}"; + offlineLabel.Text = $"绂荤嚎\n{offline}"; + runningLabel.Text = $"杩愯涓璡n{running}"; + chargingLabel.Text = $"鍏呯數涓璡n{charging}"; + faultLabel.Text = $"鏁呴殰\n{fault}"; + } + + private string GetTaskStatus(Car car) + { + if (car.tags.Contains("occupied")) + { + if (car.tags.Contains("charging")) + return "鍏呯數涓"; + if (car.tags.Contains("deliver")) + return "鎵ц浠诲姟"; + if (car.tags.Contains("redirect")) + return "閬胯涓"; + return "鍗犵敤涓"; + } + if (car.tags.Contains("idle")) + return "绌洪棽"; + if (car.tags.Contains("blocking")) + return "闃诲涓"; + if (car.status.pendingLocks.Length > 0) + return "璺緞瑙勫垝涓"; + return "寰呮満"; + } + + private void SetRowColor(DataGridViewRow row, string state, bool isOnline, double battery, double cpuUsage) + { + if (!isOnline) + { + row.DefaultCellStyle.BackColor = Color.FromArgb(244, 246, 248); + row.DefaultCellStyle.ForeColor = Color.FromArgb(150, 160, 170); + } + else + { + switch (state) + { + case "Running": + row.DefaultCellStyle.BackColor = Color.FromArgb(231, 248, 236); + row.DefaultCellStyle.ForeColor = Color.FromArgb(0, 180, 90); + break; + case "Charging": + row.DefaultCellStyle.BackColor = Color.FromArgb(255, 250, 230); + row.DefaultCellStyle.ForeColor = Color.FromArgb(255, 220, 0); + break; + case "Faulting": + row.DefaultCellStyle.BackColor = Color.FromArgb(255, 236, 236); + row.DefaultCellStyle.ForeColor = Color.FromArgb(255, 80, 80); + break; + default: + row.DefaultCellStyle.BackColor = panelBackColor; + row.DefaultCellStyle.ForeColor = gridTextColor; + break; + } + + // 鍦ㄧ嚎鐘舵佹寚绀哄櫒棰滆壊 + if (row.Cells["IsOnline"] != null) + { + row.Cells["IsOnline"].Style.ForeColor = Color.FromArgb(0, 255, 100); + row.Cells["IsOnline"].Style.Font = new Font("寰蒋闆呴粦", 12F, FontStyle.Bold); + } + + if (row.Cells["Battery"] != null) + { + row.Cells["Battery"].Style.BackColor = row.DefaultCellStyle.BackColor; + row.Cells["Battery"].Style.ForeColor = row.DefaultCellStyle.ForeColor; + } + + if (row.Cells["CPUUsage"] != null) + { + row.Cells["CPUUsage"].Style.BackColor = row.DefaultCellStyle.BackColor; + row.Cells["CPUUsage"].Style.ForeColor = row.DefaultCellStyle.ForeColor; + } + + // 鐢甸噺浣庤鍛 + if (battery < 20) + { + row.Cells["Battery"].Style.BackColor = Color.FromArgb(255, 235, 235); + row.Cells["Battery"].Style.ForeColor = Color.FromArgb(220, 60, 60); + } + else if (battery < 30) + { + row.Cells["Battery"].Style.BackColor = Color.FromArgb(255, 247, 225); + row.Cells["Battery"].Style.ForeColor = Color.FromArgb(200, 140, 0); + } + + // CPU浣跨敤鐜囬珮璀﹀憡 + if (cpuUsage > 80) + { + row.Cells["CPUUsage"].Style.BackColor = Color.FromArgb(255, 235, 235); + row.Cells["CPUUsage"].Style.ForeColor = Color.FromArgb(220, 60, 60); + } + else if (cpuUsage > 60) + { + row.Cells["CPUUsage"].Style.BackColor = Color.FromArgb(255, 247, 225); + row.Cells["CPUUsage"].Style.ForeColor = Color.FromArgb(200, 140, 0); + } + } + } + + protected override void OnFormClosing(FormClosingEventArgs e) + { + if (e.CloseReason == CloseReason.UserClosing) + { + e.Cancel = true; + this.Hide(); + } + else + { + // 濡傛灉涓嶆槸鐢ㄦ埛鍏抽棴锛屽垯閲婃斁璧勬簮 + instance = null; + } + base.OnFormClosing(e); + } + + protected override void OnResize(EventArgs e) + { + base.OnResize(e); + + if (alarmPanel != null) + { + alarmPanel.Left = 20; + alarmPanel.Top = GetAlarmPanelTop(); + alarmPanel.Width = this.ClientSize.Width - 40; + PositionAlarmLabel(); + } + + // 鏇存柊鐘舵侀潰鏉垮搴 + UpdateStatusPanelLayout(); + + if (vehicleGrid != null) + { + UpdateActionGridPanelLayout(); + } + + // 鏇存柊鏃堕棿鏍囩浣嶇疆 + if (timeLabel != null && this.Controls.Count > 0) + { + var titlePanel = this.Controls[0] as Panel; + if (titlePanel != null) + { + timeLabel.Left = this.ClientSize.Width - 250; + } + } + } + + private int GetStatusPanelWidth(int cardCount) + { + int fullWidth = this.ClientSize.Width - 40; + int preferred = (int)(fullWidth * 2.0 / 3.0); + int minCardWidth = 100; + int padding = 12; + int spacing = 8; + int minWidth = cardCount * minCardWidth + spacing * (cardCount - 1) + padding * 2; + + int target = Math.Max(preferred, minWidth); + return Math.Min(fullWidth, target); + } + + private void UpdateStatusPanelLayout() + { + if (statusPanel == null) return; + + int targetWidth = alarmPanel != null ? alarmPanel.Width : (this.ClientSize.Width - 40); + statusPanel.Width = targetWidth; + statusPanel.Left = 20; + statusPanel.Top = GetStatusPanelTop(); + LayoutStatusCards(); + } + + private void UpdateActionGridPanelLayout() + { + if (statusActionPanel == null || actionPanel == null || vehicleGrid == null) return; + + statusActionPanel.Left = 20; + statusActionPanel.Top = statusPanel.Bottom + VerticalSpacing; + statusActionPanel.Width = this.ClientSize.Width - 40; + statusActionPanel.Height = this.ClientSize.Height - (statusActionPanel.Top + 20); + + actionPanel.Left = CombinedPanelPadding; + actionPanel.Top = CombinedPanelPadding; + actionPanel.Width = statusActionPanel.Width - CombinedPanelPadding * 2; + + vehicleGrid.Left = CombinedPanelPadding; + vehicleGrid.Top = actionPanel.Bottom + CombinedPanelSpacing; + vehicleGrid.Width = statusActionPanel.Width - CombinedPanelPadding * 2; + vehicleGrid.Height = statusActionPanel.Height - (vehicleGrid.Top + CombinedPanelPadding); + } + + private int GetAlarmPanelTop() + { + return TitleBarHeight + AlarmPanelMarginTop; + } + + private int GetStatusPanelTop() + { + return GetAlarmPanelTop() + AlarmPanelHeight + VerticalSpacing; + } + + private void LayoutStatusCards() + { + if (statusPanel == null || statusCards.Count == 0) return; + + int cardCount = statusCards.Count; + int padding = 12; + int spacing = 8; + int availableWidth = statusPanel.Width - padding * 2 - spacing * (cardCount - 1); + int cardWidth = Math.Max(100, availableWidth / cardCount); + int cardHeight = 60; + int x = padding; + int y = (statusPanel.Height - cardHeight) / 2; + + foreach (var card in statusCards) + { + card.Location = new System.Drawing.Point(x, y); + card.Size = new System.Drawing.Size(cardWidth, cardHeight); + + var indicator = card.Controls.OfType().FirstOrDefault(); + if (indicator != null) + { + indicator.Top = (cardHeight - indicator.Height) / 2; + } + + x += cardWidth + spacing; + } + } + + private void VehicleGrid_CurrentCellDirtyStateChanged(object sender, EventArgs e) + { + if (vehicleGrid.IsCurrentCellDirty) + { + vehicleGrid.CommitEdit(DataGridViewDataErrorContexts.Commit); + } + } + + private void VehicleGrid_CellContentClick(object sender, DataGridViewCellEventArgs e) + { + if (e.RowIndex < 0) return; + + var columnName = vehicleGrid.Columns[e.ColumnIndex].Name; + if (columnName == "Select") + { + UpdateSelectedCarIdFromRow(e.RowIndex); + UpdateHeaderCheckBoxState(); + } + else if (columnName == "Remote") + { + var ip = vehicleGrid.Rows[e.RowIndex].Cells["Address"]?.Value?.ToString(); + if (!string.IsNullOrWhiteSpace(ip)) + { + LaunchRemoteDesktop(ip); + } + } + else if (columnName == "Online") + { + ExecuteForSingleRow(e.RowIndex, BringCarOnline, "灏忚溅涓婄嚎"); + } + else if (columnName == "Offline") + { + ExecuteForSingleRow(e.RowIndex, BringCarOffline, "灏忚溅涓嬬嚎"); + } + else if (columnName == "Return") + { + ExecuteForSingleRow(e.RowIndex, ReturnCarToFactory, "鏁呴殰杩斿巶"); + } + } + + private void UpdateSelectedCarIdFromRow(int rowIndex) + { + var row = vehicleGrid.Rows[rowIndex]; + var id = row.Cells["CarId"]?.Value?.ToString(); + if (string.IsNullOrWhiteSpace(id)) return; + + var selected = row.Cells["Select"]?.Value is bool flag && flag; + if (selected) + { + selectedCarIds.Add(id); + } + else + { + selectedCarIds.Remove(id); + } + } + + private void CaptureGridViewState() + { + if (vehicleGrid == null) return; + + lastSortColumnName = vehicleGrid.SortedColumn?.Name; + lastSortOrder = vehicleGrid.SortOrder; + + try + { + lastFirstDisplayedRowIndex = vehicleGrid.FirstDisplayedScrollingRowIndex; + } + catch + { + lastFirstDisplayedRowIndex = -1; + } + + rowHeightCache.Clear(); + selectedCarIds.Clear(); + + foreach (DataGridViewRow row in vehicleGrid.Rows) + { + if (row.IsNewRow) continue; + var id = row.Cells["CarId"]?.Value?.ToString(); + if (string.IsNullOrWhiteSpace(id)) continue; + + rowHeightCache[id] = row.Height; + + var selected = row.Cells["Select"]?.Value is bool flag && flag; + if (selected) + { + selectedCarIds.Add(id); + } + } + } + + private void ApplyGridViewState() + { + if (vehicleGrid == null) return; + + if (!string.IsNullOrEmpty(lastSortColumnName) && vehicleGrid.Columns.Contains(lastSortColumnName) && + lastSortOrder != SortOrder.None) + { + var direction = lastSortOrder == SortOrder.Descending + ? ListSortDirection.Descending + : ListSortDirection.Ascending; + vehicleGrid.Sort(vehicleGrid.Columns[lastSortColumnName], direction); + } + + if (lastFirstDisplayedRowIndex >= 0 && lastFirstDisplayedRowIndex < vehicleGrid.Rows.Count) + { + vehicleGrid.FirstDisplayedScrollingRowIndex = lastFirstDisplayedRowIndex; + } + + UpdateHeaderCheckBoxState(); + PositionSelectAllCheckBox(); + } + + private void InitializeSelectAllCheckBox() + { + selectAllCheckBox = new CheckBox + { + Size = new System.Drawing.Size(14, 14), + BackColor = Color.Transparent + }; + + selectAllCheckBox.CheckedChanged += SelectAllCheckBox_CheckedChanged; + vehicleGrid.Controls.Add(selectAllCheckBox); + PositionSelectAllCheckBox(); + } + + private void PositionSelectAllCheckBox() + { + if (selectAllCheckBox == null || vehicleGrid == null) return; + if (!vehicleGrid.Columns.Contains("Select")) return; + + var rect = vehicleGrid.GetCellDisplayRectangle(vehicleGrid.Columns["Select"].Index, -1, true); + if (rect.Width <= 0 || rect.Height <= 0) return; + + int x = rect.X + (rect.Width - selectAllCheckBox.Width) / 2; + int y = rect.Y + (rect.Height - selectAllCheckBox.Height) / 2; + selectAllCheckBox.Location = new System.Drawing.Point(x, y); + } + + private void SelectAllCheckBox_CheckedChanged(object sender, EventArgs e) + { + if (suppressSelectSync) return; + + suppressSelectSync = true; + bool isChecked = selectAllCheckBox.Checked; + + foreach (DataGridViewRow row in vehicleGrid.Rows) + { + if (row.IsNewRow) continue; + row.Cells["Select"].Value = isChecked; + + var id = row.Cells["CarId"]?.Value?.ToString(); + if (string.IsNullOrWhiteSpace(id)) continue; + + if (isChecked) + { + selectedCarIds.Add(id); + } + else + { + selectedCarIds.Remove(id); + } + } + + suppressSelectSync = false; + } + + private void UpdateHeaderCheckBoxState() + { + if (selectAllCheckBox == null || vehicleGrid == null) return; + if (suppressSelectSync) return; + + int total = 0; + int selected = 0; + foreach (DataGridViewRow row in vehicleGrid.Rows) + { + if (row.IsNewRow) continue; + total++; + var isSelected = row.Cells["Select"]?.Value is bool flag && flag; + if (isSelected) selected++; + } + + suppressSelectSync = true; + selectAllCheckBox.Checked = total > 0 && selected == total; + suppressSelectSync = false; + } + + private void VehicleGrid_ColumnWidthChanged(object sender, DataGridViewColumnEventArgs e) + { + if (e.Column.Name == "Select") + { + PositionSelectAllCheckBox(); + } + } + + private void VehicleGrid_Scroll(object sender, ScrollEventArgs e) + { + if (e.ScrollOrientation == ScrollOrientation.HorizontalScroll) + { + PositionSelectAllCheckBox(); + } + } + + private void VehicleGrid_SizeChanged(object sender, EventArgs e) + { + PositionSelectAllCheckBox(); + } + + private void AlarmScrollTimer_Tick(object sender, EventArgs e) + { + if (alarmLabel == null || alarmPanel == null) return; + if (string.IsNullOrWhiteSpace(alarmLabel.Text)) return; + + alarmLabel.Left -= 3; + if (alarmLabel.Right < 0) + { + if (!string.IsNullOrEmpty(pendingAlarmText)) + { + ApplyAlarmText(pendingAlarmText, pendingAlarmColor, pendingAlarmScrolling); + pendingAlarmText = null; + } + alarmLabel.Left = alarmPanel.Width; + } + } + + private void UpdateAlarmTicker(List cars) + { + var messages = new List(); + if (cars != null) + { + foreach (var car in cars) + { + var alarmInfo = GetCarAlarmInfo(car); + if (!string.IsNullOrWhiteSpace(alarmInfo)) + { + messages.Add($"{car.name}: {alarmInfo}"); + } + } + } + + foreach (var name in simulatedAlarmNames) + { + messages.Add($"{name}: {SimulatedAlarmInfo}"); + } + + if (messages.Count == 0) + { + SetAlarmText("鏆傛棤鎶ヨ淇℃伅", subTextColor, scrolling: false); + return; + } + + SetAlarmText(string.Join(" | ", messages), Color.FromArgb(255, 60, 60), scrolling: true); + } + + private string GetCarAlarmInfo(Car car) + { + if (car?.status?.enums == null) return string.Empty; + + string alarmInfo; + if (car.status.enums.TryGetValue("AlarmInfo", out alarmInfo) && !string.IsNullOrWhiteSpace(alarmInfo)) + return alarmInfo; + if (car.status.enums.TryGetValue("alarmInfo", out alarmInfo) && !string.IsNullOrWhiteSpace(alarmInfo)) + return alarmInfo; + if (car.status.enums.TryGetValue("Alarm", out alarmInfo) && !string.IsNullOrWhiteSpace(alarmInfo)) + return alarmInfo; + if (car.status.enums.TryGetValue("AlarmMsg", out alarmInfo) && !string.IsNullOrWhiteSpace(alarmInfo)) + return alarmInfo; + if (car.status.enums.TryGetValue("AlarmMessage", out alarmInfo) && !string.IsNullOrWhiteSpace(alarmInfo)) + return alarmInfo; + if (car.status.enums.TryGetValue("AlarmText", out alarmInfo) && !string.IsNullOrWhiteSpace(alarmInfo)) + return alarmInfo; + + return string.Empty; + } + + private void SetAlarmText(string text, Color color, bool scrolling) + { + if (alarmLabel == null || alarmPanel == null) return; + if (lastAlarmText == text && alarmScrollTimer.Enabled == scrolling) + { + return; + } + + if (alarmScrollTimer.Enabled && scrolling) + { + pendingAlarmText = text; + pendingAlarmColor = color; + pendingAlarmScrolling = scrolling; + return; + } + + ApplyAlarmText(text, color, scrolling); + } + + private void ApplyAlarmText(string text, Color color, bool scrolling) + { + alarmLabel.ForeColor = color; + alarmLabel.Text = text; + lastAlarmText = text; + alarmScrollTimer.Enabled = scrolling; + PositionAlarmLabel(); + } + + private void ToggleSecondaryColumns() + { + showSecondaryColumns = !showSecondaryColumns; + ApplySecondaryColumnVisibility(); + } + + private void ApplySecondaryColumnVisibility() + { + if (vehicleGrid == null) return; + SetColumnVisible("CarId", showSecondaryColumns); + SetColumnVisible("X", showSecondaryColumns); + SetColumnVisible("Y", showSecondaryColumns); + SetColumnVisible("Theta", showSecondaryColumns); + SetColumnVisible("Voltage", showSecondaryColumns); + SetColumnVisible("Current", showSecondaryColumns); + SetColumnVisible("CPUUsage", showSecondaryColumns); + SetColumnVisible("CPUBar", showSecondaryColumns); + //SetColumnVisible("Speed", showSecondaryColumns); + SetColumnVisible("Memory", showSecondaryColumns); + //SetColumnVisible("Address", showSecondaryColumns); + + if (toggleSecondaryButton != null) + { + toggleSecondaryButton.Text = showSecondaryColumns ? "闅愯棌鏇村灞炴" : "鏄剧ず鏇村灞炴"; + } + } + + private void SetColumnVisible(string name, bool visible) + { + if (!vehicleGrid.Columns.Contains(name)) return; + vehicleGrid.Columns[name].Visible = visible; + } + + private void PositionAlarmLabel() + { + if (alarmLabel == null || alarmPanel == null) return; + + if (!alarmScrollTimer.Enabled) + { + alarmLabel.Left = (alarmPanel.Width - alarmLabel.Width) / 2; + } + else + { + alarmLabel.Left = alarmPanel.Width; + } + alarmLabel.Top = (alarmPanel.Height - alarmLabel.Height) / 2; + } + + private void AddSimulatedAlarm() + { + var name = $"NS{simulatedAlarmIndex:000}"; + simulatedAlarmNames.Add(name); + simulatedAlarmIndex++; + } + + private void ClearSimulatedAlarms() + { + simulatedAlarmNames.Clear(); + simulatedAlarmIndex = 1; + } + + private void ExecuteForSelectedCars(Action action, string actionName) + { + var cars = GetSelectedCars(); + if (cars.Count == 0) + { + MessageBox.Show("璇峰厛鍕鹃夎溅杈嗐"); + return; + } + + foreach (var car in cars) + { + try + { + action(car); + } + catch (Exception ex) + { + Diagnosis.Log($"{actionName}澶辫触: {ex.Message}", "VehicleMonitor", true); + } + } + } + + private void ExecuteForSingleRow(int rowIndex, Action action, string actionName) + { + var car = GetCarFromRow(rowIndex); + if (car == null) + { + MessageBox.Show("鏈壘鍒板搴旇溅杈嗐"); + return; + } + + try + { + action(car); + } + catch (Exception ex) + { + Diagnosis.Log($"{actionName}澶辫触: {ex.Message}", "VehicleMonitor", true); + } + } + + private Car GetCarFromRow(int rowIndex) + { + var row = vehicleGrid.Rows[rowIndex]; + var idStr = row.Cells["CarId"]?.Value?.ToString(); + if (string.IsNullOrWhiteSpace(idStr)) return null; + + if (!int.TryParse(idStr, out var id)) return null; + + return SimpleLib.GetAllCars().OfType().FirstOrDefault(c => c.id == id); + } + + private List GetSelectedCars() + { + RefreshSelectedCarIdsFromGrid(); + var selected = new HashSet(selectedCarIds); + return SimpleLib.GetAllCars().OfType() + .Where(c => selected.Contains(c.id.ToString())) + .ToList(); + } + + private void RefreshSelectedCarIdsFromGrid() + { + if (vehicleGrid == null) return; + + selectedCarIds.Clear(); + foreach (DataGridViewRow row in vehicleGrid.Rows) + { + if (row.IsNewRow) continue; + var id = row.Cells["CarId"]?.Value?.ToString(); + if (string.IsNullOrWhiteSpace(id)) continue; + + var selected = row.Cells["Select"]?.Value is bool flag && flag; + if (selected) + { + selectedCarIds.Add(id); + } + } + } + + private void BringCarOnline(Car car) + { + if (TryInvokeCarMethod(car, "newReset", new object[] { 0 })) return; + if (TryInvokeCarMethod(car, "newReset", Array.Empty())) return; + } + + private void BringCarOffline(Car car) + { + TryInvokeCarMethod(car, "ForceStop", Array.Empty()); + } + + private void ReturnCarToFactory(Car car) + { + TryInvokeCarMethod(car, "Blown", Array.Empty()); + } + + private bool TryInvokeCarMethod(Car car, string methodName, object[] args) + { + try + { + var method = car.GetType().GetMethod(methodName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); + if (method == null) return false; + + var parameters = method.GetParameters(); + if (args.Length == parameters.Length) + { + method.Invoke(car, args); + return true; + } + + if (args.Length == 0 && parameters.Length > 0 && parameters.All(p => p.IsOptional)) + { + var optionalArgs = parameters.Select(p => Type.Missing).ToArray(); + method.Invoke(car, optionalArgs); + return true; + } + } + catch (Exception ex) + { + Diagnosis.Log($"璋冪敤{methodName}澶辫触: {ex.Message}", "VehicleMonitor", true); + } + + return false; + } + + private void LaunchRemoteDesktop(string ip) + { + Process.Start( + new ProcessStartInfo + { + FileName = "mstsc", + Arguments = $"/v:{ip}", + UseShellExecute = false, + CreateNoWindow = true + } + ); + } + } +} + diff --git a/StandardScene.Core/Chained/AbstractLoopMission.cs b/StandardScene.Core/Chained/AbstractLoopMission.cs new file mode 100644 index 0000000..619f7e9 --- /dev/null +++ b/StandardScene.Core/Chained/AbstractLoopMission.cs @@ -0,0 +1,2166 @@ +using LoopViewerApp; +using Newtonsoft.Json; +using SimpleLite.RCS; +using SimpleLite.RCS.CarTypes; +using SimpleCore; +using SimpleCore.Compiler; +using SimpleCore.Library; +using SimpleCore.PropType; +using StandardScene.CarTypes; +using StandardScene.Chained.Loop; +using StandardScene.Model; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using System.Windows.Forms; + +namespace StandardScene.Chained +{ + /// + /// 鎶借薄鐜嚎浠诲姟鍩虹被 + /// 鏍稿績鍔熻兘锛 + /// + /// 鏀寔澶氱鍚姩绫诲瀷锛欰utoLoop锛堣嚜鍔ㄥ惊鐜級銆丄pi銆丳lc銆丅uttonBox锛堝閮ㄨЕ鍙戯級 + /// 鏀寔澶氱浠诲姟绫诲埆锛歀oop锛堟櫘閫氬惊鐜級銆丅ranchPoint锛堝垎娴佺偣锛夈丣oinPoint锛堟眹鍚堢偣锛 + /// 鏀寔娴侀噺鎺у埗锛氶檺鍒剁洰鏍囩珯鐐圭殑鏈澶ц溅杈嗘暟 + /// 鏀寔浼樺厛绾ц皟搴︼細楂樹紭鍏堢骇浠诲姟浼樺厛澶勭悊 + /// 鏀寔閰嶇疆鐑洿鏂帮細tasklist.json 鏂囦欢鍙樻洿鑷姩鍒锋柊 + /// 澶栭儴瑙﹀彂鎺ュ彛锛氬瓙绫诲彲閲嶅啓 OnApiTrigger/OnPlcTrigger/OnButtonTrigger 瀹炵幇涓氬姟閫昏緫 + /// + /// + /// + /// 瀛愮被瀹炵幇绀轰緥锛 + /// + /// public class MyLoopMission : AbstractLoopMission + /// { + /// protected override ExternalTriggerResult OnApiTrigger(int currentSiteId, LoopTask task, Car car) + /// { + /// // 杩斿洖 UseTarget(鐩爣绔欑偣) - 浣跨敤瀛愮被鎸囧畾鐨勭洰鏍 + /// // 杩斿洖 UseConfigTarget() - 浣跨敤閰嶇疆鏂囦欢鐩爣 + /// // 杩斿洖 Fail() - 涓嶅垎閰嶄换鍔 + /// return ExternalTriggerResult.UseConfigTarget(); + /// } + /// } + /// + /// + public abstract class AbstractLoopMission : Mission, IDisposable + { + #region 甯搁噺瀹氫箟 + + /// 绛栫暐鍚屾绾跨▼杞闂撮殧锛堟绉掞級 + private const int STRATEGY_SYNC_INTERVAL_MS = 1000; + + /// 涓氬姟閫昏緫绾跨▼杞闂撮殧锛堟绉掞級 + private const int LOGIC_LOOP_INTERVAL_MS = 300; + + /// 鏂囦欢鍙樻洿闃叉姈寤惰繜锛堟绉掞級 + private const int FILE_CHANGE_DEBOUNCE_MS = 50; + + /// 寮傚父鎭㈠绛夊緟鏃堕棿锛堟绉掞級 + private const int ERROR_RECOVERY_DELAY_MS = 3000; + + /// 鑴氭湰寮傚父妫娴嬭Е鍙戝欢杩燂紙姣锛 + private const int SCRIPT_ERROR_TRIGGER_DELAY_MS = 3000; + + #endregion + + #region 澶栭儴瑙﹀彂缁撴灉绫 + + /// + /// 澶栭儴瑙﹀彂澶勭悊缁撴灉绫 + /// 鐢ㄤ簬瀛愮被閲嶅啓澶栭儴瑙﹀彂鎺ュ彛鏃惰繑鍥炲鐞嗙粨鏋滐紝鍐冲畾鐩爣绔欑偣鐨勫垎閰嶆柟寮 + /// + /// + /// 浣跨敤绀轰緥锛 + /// + /// // 鏂瑰紡1锛氫娇鐢ㄥ瓙绫绘寚瀹氱殑鐩爣绔欑偣 + /// return ExternalTriggerResult.UseTarget(200); + /// + /// // 鏂瑰紡2锛氫娇鐢ㄩ厤缃枃浠朵腑鐨勭洰鏍囩珯鐐癸紙鐩稿綋浜庤繑鍥 true锛 + /// return ExternalTriggerResult.UseConfigTarget(); + /// + /// // 鏂瑰紡3锛氫笟鍔″け璐ワ紝涓嶅垎閰嶄换鍔 + /// return ExternalTriggerResult.Fail(); + /// + /// + public class ExternalTriggerResult + { + /// + /// 鏄惁澶勭悊鎴愬姛 + /// true = 鎴愬姛锛屽皢鏍规嵁 CustomTargetSiteId 鍒嗛厤鐩爣绔欑偣 + /// false = 澶辫触鎴栬烦杩囷紝涓嶅垎閰嶄换鍔 + /// + public bool Success { get; set; } + + /// + /// 瀛愮被鎸囧畾鐨勭洰鏍囩珯鐐笽D + /// 鏈夊兼椂浣跨敤姝ょ洰鏍囩珯鐐 + /// null 鏃朵娇鐢ㄩ厤缃枃浠朵腑鐨 TargetStationId + /// + public int? CustomTargetSiteId { get; set; } + + /// + /// 鍒涘缓鎴愬姛缁撴灉锛屼娇鐢ㄥ瓙绫绘寚瀹氱殑鐩爣绔欑偣 + /// + /// 鐩爣绔欑偣ID + /// 鍖呭惈鎸囧畾鐩爣鐨勬垚鍔熺粨鏋 + public static ExternalTriggerResult UseTarget(int targetSiteId) + { + return new ExternalTriggerResult + { + Success = true, + CustomTargetSiteId = targetSiteId + }; + } + + /// + /// 鍒涘缓鎴愬姛缁撴灉锛屼娇鐢ㄩ厤缃枃浠朵腑鐨勭洰鏍囩珯鐐 + /// 绛夊悓浜庝笟鍔″鐞嗘垚鍔熻繑鍥 true 鐨勬晥鏋 + /// + /// 浣跨敤閰嶇疆鐩爣鐨勬垚鍔熺粨鏋 + public static ExternalTriggerResult UseConfigTarget() + { + return new ExternalTriggerResult + { + Success = true, + CustomTargetSiteId = null + }; + } + + /// + /// 鍒涘缓澶辫触缁撴灉锛屼笉鍒嗛厤浠诲姟 + /// 绛夊悓浜庝笟鍔″鐞嗗け璐ヨ繑鍥 false 鐨勬晥鏋 + /// + /// 澶辫触缁撴灉 + public static ExternalTriggerResult Fail() + { + return new ExternalTriggerResult { Success = false }; + } + } + + #endregion + + #region 鐘舵佷笌瀛楁 + + /// + /// 鐜嚎浠诲姟鐘舵佺被 + /// + public class AbstractLoopMissionStatus : MissionStatus + { + /// 褰撳墠浠诲姟鏁伴噺 + public int TaskCount { get; set; } + + /// 鏈鍚庝竴娆¢敊璇俊鎭 + public string LastError { get; set; } = "/"; + } + + /// 浠诲姟鐘舵佸璞 + /* [JsonIgnore] + public override MissionStatus status { get; set; } = new LoopMissionStatus();*/ + + /// 绛栫暐鍚屾绾跨▼ + [JsonIgnore] + private Thread _strategyThread; + + /// 绛栫暐鍚屾绾跨▼杩愯鏍囧織 + [JsonIgnore] + private volatile bool _strategyRunning; + + /// 涓氬姟閫昏緫绾跨▼ + [JsonIgnore] + private Thread _logicThread; + + /// 涓氬姟閫昏緫绾跨▼杩愯鏍囧織 + [JsonIgnore] + private volatile bool _logicRunning; + + /// 浠诲姟闆嗗悎鍚屾閿 + [JsonIgnore] + private readonly object _tasksLock = new object(); + + /// 浠诲姟鍒楄〃锛堢嚎绋嬪畨鍏ㄨ闂渶浣跨敤 _tasksLock锛 + [JsonIgnore] + protected readonly List Tasks = new List(); + + /// 瑙﹀彂鍣ㄩ傞厤鍣ㄥ垪琛 + [JsonIgnore] + private readonly List _adapters = new List(); + + /// 浠诲姟绛栫暐瀹炰緥 + private ITaskStrategy _taskStrategy; + + /// + /// 浠诲姟绛栫暐灞炴 + /// 鏀寔鐑洿鏂帮紝璁剧疆鏃惰嚜鍔ㄨ闃/鍙栨秷璁㈤槄鍙樻洿浜嬩欢 + /// + public ITaskStrategy TaskStrategy + { + get => _taskStrategy; + set + { + // 鍙栨秷鏃х瓥鐣ョ殑浜嬩欢璁㈤槄 + if (_taskStrategy is JsonFileTaskStrategy oldStrategy) + { + oldStrategy.Changed -= OnTaskStrategyChanged; + oldStrategy.DisposeWatcher(); + } + + // 璁剧疆鏂扮瓥鐣ワ紙null 鏃朵娇鐢ㄩ粯璁ょ瓥鐣ワ級 + _taskStrategy = value ?? new JsonFileTaskStrategy(); + + // 璁㈤槄鏂扮瓥鐣ョ殑鍙樻洿浜嬩欢 + if (_taskStrategy is JsonFileTaskStrategy newStrategy) + { + newStrategy.Changed += OnTaskStrategyChanged; + newStrategy.EnsureWatcher(); + } + } + } + + /// 浠诲姟鍒楄〃鍙樻洿浜嬩欢 + public event EventHandler TaskListChanged; + + #endregion + + #region 鏋勯犲嚱鏁 + + /// + /// 鍒濆鍖栨娊璞$幆绾夸换鍔″熀绫 + /// 鑷姩浣跨敤榛樿鐨 JSON 鏂囦欢绛栫暐骞跺姞杞藉垵濮嬩换鍔¢厤缃 + /// + protected AbstractLoopMission() + { + TaskStrategy = new JsonFileTaskStrategy(); + ApplyTaskStrategy(); + } + + #endregion + + #region 浠诲姟绠$悊鏂规硶 + + /// + /// 鑾峰彇褰撳墠浠诲姟鍒楄〃鐨勫彧璇诲壇鏈紙绾跨▼瀹夊叏锛 + /// + /// 浠诲姟鍒楄〃蹇収 + public IReadOnlyList GetTasks() + { + lock (_tasksLock) + { + return Tasks.ToArray(); + } + } + + /// + /// 娣诲姞浠诲姟鍒板垪琛ㄦ湯灏 + /// + /// 瑕佹坊鍔犵殑浠诲姟 + /// 鏂颁换鍔$殑绱㈠紩 + /// task 涓 null 鏃舵姏鍑 + public int EnqueueTask(LoopTask task) + { + if (task == null) + throw new ArgumentNullException(nameof(task)); + + lock (_tasksLock) + { + Tasks.Add(task); + int index = Tasks.Count - 1; + + // 瑙﹀彂浠诲姟娣诲姞浜嬩欢 + TaskListChanged?.Invoke(this, new TaskListChangedEventArgs(TaskChangeType.Added, index, task)); + + // 鏇存柊鐘舵佷腑鐨勪换鍔¤鏁 + ((AbstractLoopMissionStatus)status).TaskCount = Tasks.Count; + + return index; + } + } + + /// + /// 鏇存柊鎸囧畾绱㈠紩鐨勪换鍔 + /// + /// 浠诲姟绱㈠紩 + /// 鏂扮殑浠诲姟鏁版嵁 + /// 鏄惁鏇存柊鎴愬姛 + public bool UpdateTask(int index, LoopTask task) + { + lock (_tasksLock) + { + if (index < 0 || index >= Tasks.Count) + return false; + + Tasks[index] = task; + + // 瑙﹀彂浠诲姟鏇存柊浜嬩欢 + TaskListChanged?.Invoke(this, new TaskListChangedEventArgs(TaskChangeType.Updated, index, task)); + + return true; + } + } + + /// + /// 绉婚櫎鎸囧畾绱㈠紩鐨勪换鍔 + /// + /// 浠诲姟绱㈠紩 + /// 鏄惁绉婚櫎鎴愬姛 + public bool RemoveTask(int index) + { + lock (_tasksLock) + { + if (index < 0 || index >= Tasks.Count) + return false; + + LoopTask task = Tasks[index]; + Tasks.RemoveAt(index); + + // 瑙﹀彂浠诲姟绉婚櫎浜嬩欢 + TaskListChanged?.Invoke(this, new TaskListChangedEventArgs(TaskChangeType.Removed, index, task)); + + // 鏇存柊鐘舵佷腑鐨勪换鍔¤鏁 + ((AbstractLoopMissionStatus)status).TaskCount = Tasks.Count; + + return true; + } + } + + #endregion + + #region 杞﹁締鏌ユ壘杈呭姪鏂规硶 + + /// + /// 鏌ユ壘鎭板ソ鍋滃湪鎸囧畾绔欑偣鐨勮溅杈 + /// 鏉′欢锛氳溅杈嗙殑 holdingLocks 鍙寘鍚绔欑偣锛堣〃绀鸿溅杈嗗凡鍒拌揪骞跺仠绋筹級 + /// + /// 绔欑偣ID + /// 鎵惧埌鐨勮溅杈嗭紝鏈壘鍒拌繑鍥 null + protected Car FindCarArrivedAtSite(int siteId) + { + try + { + return SimpleLib.GetAllCars() + .OfType() + .FirstOrDefault(car => + car?.status?.holdingLocks != null && + car.status.holdingLocks.Length == 1 && + car.status.holdingLocks[0] == siteId); + } + catch (Exception ex) + { + System.Diagnostics.Debug.WriteLine($"[AbstractLoopMission] FindCarArrivedAtSite({siteId}) error: {ex.Message}"); + return null; + } + } + + /// + /// 鑾峰彇褰撳墠鍦ㄧ珯鎴栨鍦ㄥ墠寰鎸囧畾绔欑偣鐨勬墍鏈夎溅杈 + /// 鐢ㄤ簬娴侀噺鎺у埗缁熻 + /// + /// 绔欑偣ID + /// 杞﹁締闆嗗悎 + protected IEnumerable GetCarsAtOrHeadingToSite(int siteId) + { + try + { + return SimpleLib.GetAllCars() + .OfType() + .Where(car => + { + if (car?.status == null) + return false; + + // 妫鏌ユ槸鍚﹀湪绔欙細holdingLocks 鍖呭惈璇ョ珯鐐 + if (car.status.holdingLocks?.Contains(siteId) == true) + return true; + + // 妫鏌ユ槸鍚﹀墠寰锛歱endingLocks 鏈鍚庝竴涓攣瀹氱偣涓鸿绔欑偣 + if (car.status.pendingLocks.LastOrDefault()==siteId) + { + return true; + } + // 妫鏌ユ槸鍚﹀墠寰锛歡oalSite 鏍囩鎸囧悜璇ョ珯鐐 + /* if (car.tags?.TryGetValue("goalSite", out var destStr) == true && + int.TryParse(destStr, out int destId) && + destId == siteId) + return true;*/ + + return false; + }) + .ToList(); + } + catch (Exception ex) + { + System.Diagnostics.Debug.WriteLine($"[AbstractLoopMission] GetCarsAtOrHeadingToSite({siteId}) error: {ex.Message}"); + return Array.Empty(); + } + } + + /// + /// 缁熻褰撳墠鍦ㄧ珯鎴栨鍦ㄥ墠寰鎸囧畾绔欑偣鐨勮溅杈嗘暟閲 + /// + /// 绔欑偣ID + /// 杞﹁締鏁伴噺 + protected int CountCarsAtOrHeadingToSite(int siteId) + { + return GetCarsAtOrHeadingToSite(siteId).Count(); + } + + /// + /// 妫鏌ヨ溅杈嗘槸鍚︾┖闂诧紙鏈鍗犵敤銆佹湭鏈夌洰鏍囥佹湭鎵ц浠诲姟锛 + /// + /// 杞﹁締 + /// 鏄惁绌洪棽 + protected bool IsCarIdle(Car car) + { + if (car?.tags == null) + return false; + + try + { + // 妫鏌ユ槸鍚﹀瓨鍦ㄨ〃绀哄繖纰岀姸鎬佺殑鏍囩 + if (car.tags.ContainsKey("occupied")) return false; + if (car.tags.ContainsKey("dest")) return false; + if (car.tags.ContainsKey("deliver")) return false; + + return true; + } + catch + { + return false; + } + } + + #endregion + + #region 娴侀噺鎺у埗妫鏌 + + /// + /// 妫鏌ョ洰鏍囩珯鐐规槸鍚︽弧瓒虫祦閲忔帶鍒舵潯浠 + /// 缁熻褰撳墠鍦ㄧ珯鎴栨鍦ㄥ墠寰璇ョ珯鐐圭殑杞﹁締鏁伴噺锛屼笌鏈澶у厑璁告暟姣旇緝 + /// + /// 鐩爣绔欑偣ID + /// 鏈澶у厑璁歌溅杈嗘暟锛0鎴栬礋鏁拌〃绀轰笉闄愬埗锛 + /// true=鍙互鍙戣溅锛宖alse=宸茶揪涓婇檺闇绛夊緟 + protected bool CheckTrafficControl(int targetSiteId, int maxAllowed) + { + // 涓嶉檺鍒舵祦閲忕殑鎯呭喌 + if (maxAllowed <= 0) + return true; + + int currentCount = CountCarsAtOrHeadingToSite(targetSiteId); + return currentCount < maxAllowed; + } + + #endregion + + #region 澶栭儴瑙﹀彂鎺ュ彛锛堝瓙绫婚噸鍐欙級 + + /// + /// API 瑙﹀彂澶勭悊鎺ュ彛 + /// 褰撲换鍔$殑 StartType 涓 Api 鏃讹紝杞﹁締鍒拌揪褰撳墠绔欑偣鍚庝細璋冪敤姝ゆ柟娉 + /// 瀛愮被閲嶅啓姝ゆ柟娉曞疄鐜 API 瑙﹀彂鐨勪笟鍔¢昏緫 + /// + /// 褰撳墠绔欑偣ID + /// 鍖归厤鐨勪换鍔¢厤缃 + /// 鍒拌揪鐨勮溅杈 + /// + /// 澶勭悊缁撴灉锛 + /// + /// - 浣跨敤瀛愮被鎸囧畾鐨勭洰鏍囩珯鐐 + /// - 浣跨敤閰嶇疆鏂囦欢涓殑鐩爣绔欑偣 + /// - 涓氬姟澶辫触锛屼笉鍒嗛厤浠诲姟 + /// + /// + protected virtual ExternalTriggerResult OnApiTrigger(int currentSiteId, LoopTask task, Car car) + { + // 榛樿杩斿洖澶辫触锛屽瓙绫婚渶閲嶅啓浠ュ惎鐢 API 瑙﹀彂鍔熻兘 + return ExternalTriggerResult.Fail(); + } + + /// + /// PLC 淇″彿瑙﹀彂澶勭悊鎺ュ彛 + /// 褰撲换鍔$殑 StartType 涓 Plc 鏃讹紝杞﹁締鍒拌揪褰撳墠绔欑偣鍚庝細璋冪敤姝ゆ柟娉 + /// 瀛愮被閲嶅啓姝ゆ柟娉曞疄鐜 PLC 瑙﹀彂鐨勪笟鍔¢昏緫 + /// + /// 褰撳墠绔欑偣ID + /// 鍖归厤鐨勪换鍔¢厤缃 + /// 鍒拌揪鐨勮溅杈 + /// 澶勭悊缁撴灉 + protected virtual ExternalTriggerResult OnPlcTrigger(int currentSiteId, LoopTask task, Car car) + { + // 榛樿杩斿洖澶辫触锛屽瓙绫婚渶閲嶅啓浠ュ惎鐢 PLC 瑙﹀彂鍔熻兘 + return ExternalTriggerResult.Fail(); + } + + /// + /// 鍏呯數鏉′欢淇″彿瑙﹀彂澶勭悊鎺ュ彛 + /// 褰撲换鍔$殑 StartType 涓 Plc 鏃讹紝杞﹁締鍒拌揪褰撳墠绔欑偣鍚庝細璋冪敤姝ゆ柟娉 + /// 瀛愮被閲嶅啓姝ゆ柟娉曞疄鐜 PLC 瑙﹀彂鐨勪笟鍔¢昏緫 + /// + /// 褰撳墠绔欑偣ID + /// 鍖归厤鐨勪换鍔¢厤缃 + /// 鍒拌揪鐨勮溅杈 + /// 澶勭悊缁撴灉 + protected virtual ExternalTriggerResult OnChargeTrigger(int currentSiteId, LoopTask task, Car car) + { + // 榛樿杩斿洖澶辫触锛屽瓙绫婚渶閲嶅啓宸插惎鐢ㄥ幓鍏呯數瑙﹀彂鍔熻兘 + return ExternalTriggerResult.Fail(); + } + + /// + /// 鎸夐挳鐩掕Е鍙戝鐞嗘帴鍙 + /// 褰撲换鍔$殑 StartType 涓 ButtonBox 鏃讹紝杞﹁締鍒拌揪褰撳墠绔欑偣鍚庝細璋冪敤姝ゆ柟娉 + /// 瀛愮被閲嶅啓姝ゆ柟娉曞疄鐜版寜閽洅瑙﹀彂鐨勪笟鍔¢昏緫 + /// + /// 褰撳墠绔欑偣ID + /// 鍖归厤鐨勪换鍔¢厤缃 + /// 鍒拌揪鐨勮溅杈 + /// 澶勭悊缁撴灉 + protected virtual ExternalTriggerResult OnButtonTrigger(int currentSiteId, LoopTask task, Car car) + { + // 榛樿杩斿洖澶辫触锛屽瓙绫婚渶閲嶅啓浠ュ惎鐢ㄦ寜閽洅瑙﹀彂鍔熻兘 + return ExternalTriggerResult.Fail(); + } + + /// + /// 鏍规嵁鍚姩绫诲瀷璋冪敤瀵瑰簲鐨勫瓙绫昏Е鍙戞帴鍙 + /// + /// 鍚姩绫诲瀷 + /// 褰撳墠绔欑偣ID + /// 鍖归厤鐨勪换鍔¢厤缃 + /// 鍒拌揪鐨勮溅杈 + /// 瀛愮被杩斿洖鐨勫鐞嗙粨鏋 + private ExternalTriggerResult InvokeExternalTrigger(TaskStartType startType, int currentSiteId, LoopTask task, Car car) + { + return startType switch + { + TaskStartType.Api => OnApiTrigger(currentSiteId, task, car), + TaskStartType.Plc => OnPlcTrigger(currentSiteId, task, car), + TaskStartType.ButtonBox => OnButtonTrigger(currentSiteId, task, car), + TaskStartType.Charge => OnChargeTrigger(currentSiteId, task, car), + _ => ExternalTriggerResult.Fail() + }; + } + + + + #endregion + + #region 浠诲姟绛栫暐绠$悊 + + /// + /// 浠庝换鍔$瓥鐣ュ埛鏂板苟搴旂敤浠诲姟鍒楄〃 + /// 璇诲彇閰嶇疆鏂囦欢鍐呭骞舵浛鎹㈠綋鍓嶄换鍔″垪琛 + /// + public void ApplyTaskStrategy() + { + try + { + // 鍒锋柊绛栫暐鏁版嵁 + TaskStrategy?.Refresh(); + var sourceTasks = TaskStrategy?.GetTasks(); + + if (sourceTasks == null) + return; + + lock (_tasksLock) + { + // 娓呯┖骞堕噸鏂板姞杞戒换鍔 + Tasks.Clear(); + foreach (var task in sourceTasks) + { + Tasks.Add(task); + } + + // 瑙﹀彂浠诲姟鏇挎崲浜嬩欢 + TaskListChanged?.Invoke(this, new TaskListChangedEventArgs(TaskChangeType.Replaced, -1, null)); + + // 鏇存柊鐘舵佷腑鐨勪换鍔¤鏁 + ((AbstractLoopMissionStatus)status).TaskCount = Tasks.Count; + } + + System.Diagnostics.Debug.WriteLine($"[AbstractLoopMission] ApplyTaskStrategy: 鍔犺浇浜 {Tasks.Count} 涓换鍔"); + } + catch (Exception ex) + { + // 璁板綍閿欒淇℃伅 + ((AbstractLoopMissionStatus)status).LastError = ex.Message; + System.Diagnostics.Debug.WriteLine($"[AbstractLoopMission] ApplyTaskStrategy error: {ex}"); + } + } + + /// + /// 浠诲姟绛栫暐閰嶇疆鏂囦欢鍙樻洿澶勭悊 + /// + private void OnTaskStrategyChanged(object sender, EventArgs e) + { + try + { + ApplyTaskStrategy(); + System.Diagnostics.Debug.WriteLine("[AbstractLoopMission] OnTaskStrategyChanged: 閰嶇疆鏂囦欢宸叉洿鏂"); + } + catch (Exception ex) + { + System.Diagnostics.Debug.WriteLine($"[AbstractLoopMission] OnTaskStrategyChanged error: {ex}"); + } + } + + #endregion + + #region 鏍稿績涓氬姟閫昏緫 + + /// + /// 涓氬姟閫昏緫涓诲惊鐜 + /// 鍦 _logicThread 绾跨▼涓墽琛岋紝姣 500ms 澶勭悊涓娆′换鍔¤皟搴 + /// + protected virtual void ExecuteLogicLoop() + { + while (_logicRunning) + { + try + { + ProcessAutoLoopTasks(); + Thread.Sleep(LOGIC_LOOP_INTERVAL_MS); + } + catch (ThreadAbortException) + { + break; + } + catch (Exception ex) + { + System.Diagnostics.Debug.WriteLine($"[AbstractLoopMission] ExecuteLogicLoop error: {ex}"); + } + } + } + + /// + /// 澶勭悊鎵鏈変换鍔★紙涓昏皟搴﹀叆鍙o級 + /// 鎸夐『搴忓鐞嗭細鑷姩寰幆浠诲姟 鈫 澶栭儴瑙﹀彂浠诲姟 鈫 鍒嗘祦鐐逛换鍔 鈫 姹囧悎鐐逛换鍔 鈫 寰幆鍚姩鍔ㄤ綔 + /// + protected virtual void ProcessAutoLoopTasks() + { + // 鑾峰彇浠诲姟蹇収锛堥伩鍏嶉暱鏃堕棿鎸侀攣锛 + List snapshot; + lock (_tasksLock) + { + snapshot = Tasks.ToList(); + } + + // 1. 澶勭悊鑷姩寰幆浠诲姟锛圫tartType=AutoLoop, Kind=Loop锛 + ProcessAutoLoopTypeTasks(snapshot); + + // 2. 澶勭悊澶栭儴瑙﹀彂浠诲姟锛圫tartType=Api/Plc/ButtonBox/charge锛 + ProcessExternalTriggerTasks(snapshot); + + // 3. 澶勭悊鑷姩鍒嗘祦鐐逛换鍔★紙StartType=AutoLoop, Kind=BranchPoint锛 + ProcessBranchPointTasks(snapshot); + + // 4. 澶勭悊鑷姩姹囧悎鐐逛换鍔★紙StartType=AutoLoop, Kind=JoinPoint锛 + ProcessJoinPointTasks(snapshot); + + // 5. 鎵ц寰幆鍚姩鍔ㄤ綔锛堝鐞嗙珯鐐归粯璁ょ洰鏍囥佹墽琛屽鑸佸紓甯告仮澶嶇瓑锛 + LoopStartAction(); + } + + /// + /// 澶勭悊鑷姩寰幆绫诲瀷浠诲姟 + /// 绛涢 StartType=AutoLoop 涓 Kind=Loop 鐨勪换鍔★紝鎸変紭鍏堢骇澶勭悊 + /// + /// 浠诲姟蹇収 + private void ProcessAutoLoopTypeTasks(List snapshot) + { + // 绛涢夎嚜鍔ㄥ惊鐜换鍔″苟鎸変紭鍏堢骇鎺掑簭 + var autoTasks = snapshot + .Where(t => t.StartType == TaskStartType.AutoLoop && t.Kind == TaskKind.Loop) + .OrderByDescending(t => t.Priority) + .ToList(); + + foreach (var task in autoTasks) + { + try + { + // 鏌ユ壘鍒拌揪褰撳墠绔欑偣鐨勮溅杈 + var car = FindCarArrivedAtSite(task.CurrentStationId); + if (car == null) + continue; + + // 妫鏌ヨ溅杈嗘槸鍚﹀彲鐢紙SelectCar 杩斿洖 1 琛ㄧず鍙敤锛 + if (Commons.SelectCar(car) != 1) + continue; + + // 娴侀噺鎺у埗妫鏌 + if (!CheckTrafficControl(task.TargetStationId, task.TrafficControl)) + { + + Diagnosis.Post($"[AbstractLoopMission] AutoLoop: 鐩爣绔欑偣 {task.TargetStationId} 娴侀噺宸叉弧锛岃溅杈 {car.name} 绛夊緟", "Loop-AutoLoop", true); + continue; + } + + // 鍒嗛厤鐩爣绔欑偣 + AssignCarToTarget(car, task.TargetStationId); + Diagnosis.Post($"[AbstractLoopMission] AutoLoop: 杞﹁締 {car.name} 浠庣珯鐐 {task.CurrentStationId} 鑷姩鍒嗛厤鍒扮洰鏍囩珯鐐 {task.TargetStationId}", "Loop-AutoLoop", true); + + } + catch (Exception ex) + { + Diagnosis.Post($"[AbstractLoopMission] ProcessAutoLoopTypeTasks error: {ex}", "Loop-AutoLoop", true); + + } + } + } + + /// + /// 澶勭悊澶栭儴瑙﹀彂绫诲瀷浠诲姟锛圓PI/PLC/ButtonBox锛 + /// 渚濇澶勭悊鍚勭被鍨嬬殑澶栭儴瑙﹀彂浠诲姟 + /// + /// 浠诲姟蹇収 + private void ProcessExternalTriggerTasks(List snapshot) + { + // 渚濇澶勭悊 API銆丳LC銆丅uttonBox 绫诲瀷浠诲姟 + ProcessExternalTasksByType(snapshot, TaskStartType.Api); + ProcessExternalTasksByType(snapshot, TaskStartType.Plc); + ProcessExternalTasksByType(snapshot, TaskStartType.ButtonBox); + ProcessExternalTasksByType(snapshot, TaskStartType.Charge); + } + + /// + /// 鎸夊惎鍔ㄧ被鍨嬪鐞嗗閮ㄨЕ鍙戜换鍔 + /// 澶勭悊鏅氬惊鐜换鍔°佸垎娴佺偣浠诲姟銆佹眹鍚堢偣浠诲姟 + /// + /// 浠诲姟蹇収 + /// 鍚姩绫诲瀷 + private void ProcessExternalTasksByType(List snapshot, TaskStartType startType) + { + // 绛涢夋寚瀹氬惎鍔ㄧ被鍨嬬殑鏅氬惊鐜换鍔 + var externalTasks = snapshot + .Where(t => t.StartType == startType && t.Kind == TaskKind.Loop) + .OrderByDescending(t => t.Priority) + .ToList(); + + foreach (var task in externalTasks) + { + try + { + // 鏌ユ壘鍒拌揪褰撳墠绔欑偣鐨勮溅杈 + var car = FindCarArrivedAtSite(task.CurrentStationId); + if (car == null) + continue; + + // 妫鏌ヨ溅杈嗘槸鍚﹀彲鐢 + if (Commons.SelectCar(car) != 1) + continue; + + // 璋冪敤瀛愮被鎺ュ彛鑾峰彇澶勭悊缁撴灉 + ExternalTriggerResult result = InvokeExternalTrigger(startType, task.CurrentStationId, task, car); + + // 瀛愮被杩斿洖澶辫触鍒欒烦杩 + if (result?.Success != true) + continue; + + // 纭畾鐩爣绔欑偣锛氬瓙绫绘寚瀹 > 閰嶇疆鏂囦欢 + int targetSiteId = result.CustomTargetSiteId ?? task.TargetStationId; + + // 娴侀噺鎺у埗妫鏌 + if (!CheckTrafficControl(targetSiteId, task.TrafficControl)) + { + Diagnosis.Post($"[AbstractLoopMission] {startType}: 鐩爣绔欑偣 {targetSiteId} 娴侀噺宸叉弧锛岃溅杈 {car.name} 绛夊緟", $"Loop-{startType}", true); + + continue; + } + + // 鍒嗛厤鐩爣绔欑偣 + AssignCarToTarget(car, targetSiteId); + Diagnosis.Post($"[AbstractLoopMission] {startType}: 杞﹁締 {car.name} 浠庣珯鐐 {task.CurrentStationId} 鍒嗛厤鍒扮洰鏍囩珯鐐 {targetSiteId}", $"Loop-{startType}", true); + + } + catch (Exception ex) + { + Diagnosis.Post($"[AbstractLoopMission] ProcessExternalTasksByType({startType}) error: {ex}", $"Loop-{startType}", true); + + } + } + + // 澶勭悊鍒嗘祦鐐圭被鍨嬬殑澶栭儴瑙﹀彂浠诲姟 + ProcessExternalBranchTasks(snapshot, startType); + + // 澶勭悊姹囧悎鐐圭被鍨嬬殑澶栭儴瑙﹀彂浠诲姟 + ProcessExternalJoinTasks(snapshot, startType); + } + + /// + /// 澶勭悊澶栭儴瑙﹀彂鐨勫垎娴佺偣浠诲姟 + /// 鎸夊綋鍓嶇珯鐐瑰垎缁勶紝浼樺厛绾ф帓搴忥紝璋冪敤瀛愮被鎺ュ彛鍚庡垎閰嶇洰鏍 + /// + /// 浠诲姟蹇収 + /// 鍚姩绫诲瀷 + private void ProcessExternalBranchTasks(List snapshot, TaskStartType startType) + { + // 鎸夊綋鍓嶇珯鐐瑰垎缁 + var branchGroups = snapshot + .Where(t => t.Kind == TaskKind.BranchPoint && t.StartType == startType) + .GroupBy(t => t.CurrentStationId) + .ToList(); + + foreach (var group in branchGroups) + { + int currentSiteId = group.Key; + + // 鏌ユ壘鍒拌揪褰撳墠绔欑偣鐨勮溅杈 + var car = FindCarArrivedAtSite(currentSiteId); + if (car == null || Commons.SelectCar(car) != 1) + continue; + + // 鎸変紭鍏堢骇鎺掑簭 + var sortedTasks = group.OrderByDescending(t => t.Priority).ToList(); + + foreach (var task in sortedTasks) + { + // 璋冪敤瀛愮被鎺ュ彛 + ExternalTriggerResult result = InvokeExternalTrigger(startType, currentSiteId, task, car); + + if (result?.Success != true) + continue; + + // 纭畾鐩爣锛氬瓙绫绘寚瀹 > 鎸変紭鍏堢骇閫夋嫨娴侀噺鍏佽鐨勭洰鏍 + int targetSiteId = result.CustomTargetSiteId ?? SelectBranchTarget(task, snapshot); + + if (CheckTrafficControl(targetSiteId, task.TrafficControl)) + { + AssignCarToTarget(car, targetSiteId); + Diagnosis.Post($"[AbstractLoopMission] {startType} BranchPoint: 杞﹁締 {car.name} 浠庡垎娴佺偣 {currentSiteId} 鍒嗛厤鍒扮洰鏍 {targetSiteId}", $"BranchPoint-{startType}", true); + + break; // 鍒嗘祦鐐瑰彧鍒嗛厤涓涓洰鏍 + } + } + } + } + + + + + /// + /// 澶勭悊澶栭儴瑙﹀彂鐨勬眹鍚堢偣浠诲姟 + /// 鎸夌洰鏍囩珯鐐瑰垎缁勶紙姹囧悎鐐癸級锛屾瘡缁勪腑鐨勭瓥鐣ユ寜浼樺厛绾ф帓鍒楋紝鎺掑垪鍚庢瘡涓瓥鐣ユ牴鎹祦閲忓垽鏂紝鏈鍚庨噰鐢ㄦ帴鍙h繑鍥炵粨鏋 + /// + /// 浠诲姟蹇収 + /// 鍚姩绫诲瀷 + private void ProcessExternalJoinTasks(List snapshot, TaskStartType startType) + { + // 鎸夌洰鏍囩珯鐐瑰垎缁勶紙姹囧悎鐐癸級 + var joinGroups = snapshot + .Where(t => t.Kind == TaskKind.JoinPoint && t.StartType == startType) + .GroupBy(t => t.TargetStationId) + .ToList(); + + foreach (var group in joinGroups) + { + int targetSiteId = group.Key; + + // 鎸変紭鍏堢骇闄嶅簭鎺掑垪缁勫唴浠诲姟 + var sortedTasks = group.OrderByDescending(t => t.Priority).ToList(); + + // 閬嶅巻鎺掑簭鍚庣殑浠诲姟锛屾牴鎹祦閲忓拰鎺ュ彛杩斿洖缁撴灉澶勭悊 + foreach (var task in sortedTasks) + { + try + { + + // 鏌ユ壘鍒拌揪褰撳墠绔欑偣鐨勮溅杈 + var car = FindCarArrivedAtSite(task.CurrentStationId); + if (car == null) + continue; + + // 妫鏌ヨ溅杈嗘槸鍚﹀彲鐢 + if (Commons.SelectCar(car) != 1) + continue; + + + // 娴侀噺鎺у埗妫鏌ワ紙浼樺厛鍒ゆ柇锛岄伩鍏嶆棤鏁堢殑鎺ュ彛璋冪敤锛 + if (!CheckTrafficControl(targetSiteId, task.TrafficControl)) + { + Diagnosis.Post($"[AbstractLoopMission] {startType} JoinPoint: 鐩爣绔欑偣 {targetSiteId} 娴侀噺宸叉弧锛岃烦杩囦换鍔 {task.Id}", $"JoinPoint-{startType}", true); + continue; + } + + + // 璋冪敤瀛愮被鎺ュ彛鑾峰彇澶勭悊缁撴灉 + ExternalTriggerResult result = InvokeExternalTrigger(startType, task.CurrentStationId, task, car); + + // 瀛愮被杩斿洖澶辫触鍒欒烦杩囧綋鍓嶄换鍔 + if (result?.Success != true) + continue; + + // 纭畾鏈缁堢洰鏍囷細瀛愮被鎸囧畾 > 閰嶇疆鏂囦欢 + int finalTarget = result.CustomTargetSiteId ?? targetSiteId; + + // 濡傛灉瀛愮被鎸囧畾浜嗕笉鍚岀殑鐩爣锛岄渶瑕佸啀娆℃鏌ユ祦閲 + if (finalTarget != targetSiteId && !CheckTrafficControl(finalTarget, task.TrafficControl)) + { + Diagnosis.Post($"[AbstractLoopMission] {startType} JoinPoint: 瀛愮被鎸囧畾鐩爣绔欑偣 {finalTarget} 娴侀噺宸叉弧锛岃溅杈 {car.name} 绛夊緟", $"JoinPoint-{startType}", true); + continue; + } + + // 鍒嗛厤鐩爣绔欑偣 + AssignCarToTarget(car, finalTarget); + Diagnosis.Post($"[AbstractLoopMission] {startType} JoinPoint: 杞﹁締 {car.name} 浠庣珯鐐 {task.CurrentStationId} 姹囧叆鐩爣 {finalTarget}锛堜紭鍏堢骇 {task.Priority}锛", $"JoinPoint-{startType}", true); + } + catch (Exception ex) + { + Diagnosis.Post($"[AbstractLoopMission] ProcessExternalJoinTasks({startType}) task {task.Id} error: {ex.Message}", $"JoinPoint-{startType}", true); + } + } + } + } + /// + /// 寰幆鍚姩鍔ㄤ綔 + /// 澶勭悊绔欑偣閰嶇疆鐨勯粯璁ょ洰鏍囥佹墽琛岃矾寰勮鍒掑鑸佹娴嬪苟澶勭悊鑴氭湰寮傚父 + /// 娉ㄦ剰锛氭鏂规硶涓嶆媶鍒嗭紝淇濇寔鍘熸湁閫昏緫瀹屾暣鎬 + /// + private void LoopStartAction() + { + try + { + foreach (var car in SimpleLib.GetAllCars()) + { + // 濡傛灉杞﹁締鍙敤涓旀病鏈夌洰鏍囷紝灏濊瘯浠庣珯鐐归厤缃鍙栭粯璁ょ洰鏍 + if (Commons.SelectCar(car) == 1 && !car.tags.Contains("goalSite")) + { + var carSite = SimpleLib.GetSite(car.GetLastSite()); + if (carSite != null && carSite.fields.TryGetValue("goSite", out var dst)) + { + Commons.AddOrUpdateTag(car.tags, "goalSite", dst); + } + if (carSite != null && !car.tags.Contains("goalSite")) + { + var taskLine = FindBestTaskForCar((Car)car); + if (taskLine!=null && !taskLine.IsAtStartPoint && !taskLine.IsAtEndPoint) + { + Commons.AddOrUpdateTag(car.tags, "goalSite", taskLine.TargetSiteId.ToString()); + } + } + } + + // 濡傛灉杞﹁締鍙敤涓旀湁鐩爣锛屾墽琛屽鑸 + if (Commons.SelectCar(car) == 1 && car.tags.Contains("goalSite")) + { + var goalSite = SimpleLib.GetAllSites() + .FirstOrDefault(s => car.tags.IsEqual("goalSite", s.id.ToString())); + var holdingSite=car.status.holdingLocks.FirstOrDefault(); + if (goalSite != null && holdingSite!=goalSite.id ) + { + _=GoSite(car, goalSite); + + } + else if (goalSite != null && holdingSite == goalSite.id) + { + // 宸插埌杈剧洰鏍囷紝娓呯悊 goalSite 鏍囩 + car.tags.Remove("goalSite"); + car.tags.Remove("loopAssigned"); + } + + } + + // 浠ヤ笅澶勭悊澹版槑浜嗚剼鏈紓甯歌嚜鎭㈠鑳藉姏鐨勮溅杈嗭紙IScriptErrorRecoverable锛屽綋鍓嶄负 Kiva锛 + if (!(car is IScriptErrorRecoverable recoverable) || !(car is Car mcar)) + continue; + + // 妫娴嬭剼鏈紓甯哥姸鎬侊細杞﹁締琚崰鐢ㄤ絾鑴氭湰鐘舵佷负 Error 鎴 Bad + LadderLogic.TriggerOnce( + mcar.tags.Contains("occupied") && + (mcar.status.programs.printStatus().Contains("Error") || mcar.status.programs.printStatus().Contains("Bad")), + SCRIPT_ERROR_TRIGGER_DELAY_MS, + () => + { + // 鑾峰彇杞﹁締鏈鍚庢寔鏈夌殑绔欑偣閿 + var lastHoldingLocks = car.status.holdingLocks.First(); + var site = SimpleLib.GetSite(lastHoldingLocks); + + // 鏍囪绔欑偣涓嶅彲鐢 + site.MakeAllCarsUnavailable(); + + // 绂佹杞﹁締璋冨害 + mcar.NoSchedule(true); + Diagnosis.Post($"errStatus ", "NoSchedule", true); + + // 閲嶇疆杞﹁締绔欑偣ID + mcar.siteID = -1; + + // 鏇存柊杞﹁締浣跨敤鐘舵 + mcar.status.usage.AddUsage("base", new CarUsage.CarUsageInfo() + { + scheduling = false, + refreshing = false + }); + + // 璁剧疆杞﹁締鐘舵佷负杩斿巶妫淇 + mcar.lstatus = "杩斿巶妫淇"; + + // 娓呯悊杞﹁締鏍囩 + Commons.DeleteTag(mcar.tags, "occupied"); + // Commons.DeleteTag(mcar.tags, "goalSite"); + + // 鎵ц杞﹁締閲嶇疆 + ((Car)car).Intercept((_) => + { + recoverable.RecoverReset(lastHoldingLocks); + }); + }, + mcar.id + ); + } + } + catch (Exception e) + { + Diagnosis.Post($"loop mission error {ExceptionFormatter.FormatEx(e)}"); + } + } + + + /// + /// 瀵艰埅杞﹁締鍒扮洰鏍囩珯鐐 + /// 鍒涘缓璺緞瑙勫垝骞舵墽琛岀Щ鍔ㄤ换鍔 + /// + /// 杞﹁締 + /// 鐩爣绔欑偣 + /// 鍔ㄤ綔鍙傛暟锛岄粯璁や负 "/" + /// 鏄惁鍊掕溅锛岄粯璁や负 false + public async Task GoSite(AbstractCar car, Site targetSite, string action = "/", bool reverse = false) + { + + try + { + // 鍒涘缓璺緞瑙勫垝 + var plan = new SegmentPlan() { usingCar = car }; + plan.fields["reverse"] = reverse.ToString(); + plan.fields["action"] = action; + plan.fields["allow_destination_on_route"] = "true"; + + // 鏌ユ壘浠庡綋鍓嶄綅缃埌鐩爣绔欑偣鐨勮矾寰 + plan.FindRoute(SimpleLib.GetSite(car.GetLastSite()), targetSite); + + // 缂栬瘧骞舵墽琛岀Щ鍔ㄨ剼鏈 + var program = plan.Compile("move"); + + // 鏍囪杞﹁締涓哄崰鐢ㄧ姸鎬 + car.tags.Add("occupied", $"go{targetSite.id}"); + + Console.WriteLine($">>Script:{program.script}"); + + // 绛夊緟浠诲姟瀹屾垚 + var tsk = program.Queue(); + await tsk; + + // 浠诲姟瀹屾垚鍚庢竻鐞嗘爣绛 + car.tags.Remove("occupied"); + car.tags.Remove("goalSite"); + car.tags.Remove("loopAssigned"); + } + catch (Exception ex) + { + // 璁板綍寮傚父骞剁瓑寰呭悗閲嶈瘯 + Diagnosis.Post( + $"{car.name}寰幆浠诲姟鎶ラ敊{ExceptionFormatter.FormatEx(ex)}--鍘诲線寰幆鐐筰d:{targetSite.id},name:{targetSite.name}", + $"閲嶆柊鎵ц寰幆浠诲姟", + true + ); + Thread.Sleep(ERROR_RECOVERY_DELAY_MS); + } + } + + /// + /// 澶勭悊鑷姩鍒嗘祦鐐逛换鍔 + /// 绛涢 StartType=AutoLoop 涓 Kind=BranchPoint 鐨勪换鍔 + /// 鎸夊綋鍓嶇珯鐐瑰垎缁勶紝浼樺厛绾ф帓搴忥紝閫夋嫨娴侀噺鍏佽鐨勭洰鏍 + /// + /// 浠诲姟鍒楄〃 + protected virtual void ProcessBranchPointTasks(List allTasks) + { + // 鎸夊綋鍓嶇珯鐐瑰垎缁 + var branchGroups = allTasks + .Where(t => t.Kind == TaskKind.BranchPoint && t.StartType == TaskStartType.AutoLoop) + .GroupBy(t => t.CurrentStationId) + .ToList(); + + foreach (var group in branchGroups) + { + int currentSiteId = group.Key; + + // 鏌ユ壘鍒拌揪褰撳墠绔欑偣鐨勮溅杈 + var car = FindCarArrivedAtSite(currentSiteId); + if (car == null || Commons.SelectCar(car) != 1) + continue; + + // 鎸変紭鍏堢骇鎺掑簭锛岄夋嫨娴侀噺鍏佽鐨勭洰鏍 + var sortedTasks = group.OrderByDescending(t => t.Priority).ToList(); + + foreach (var task in sortedTasks) + { + if (CheckTrafficControl(task.TargetStationId, task.TrafficControl)) + { + AssignCarToTarget(car, task.TargetStationId); + Diagnosis.Post($"[AbstractLoopMission] BranchPoint: 杞﹁締 {car.name}-{car.id} 浠庡垎娴佺偣 {currentSiteId} 鍒嗛厤鍒扮洰鏍 {task.TargetStationId}锛堜紭鍏堢骇 {task.Priority}锛", $"BranchPoint-AutoLoop", true); + + break; // 鍒嗘祦鐐瑰彧鍒嗛厤涓涓洰鏍 + } + } + } + } + + /// + /// 澶勭悊鑷姩姹囧悎鐐逛换鍔 + /// 绛涢 StartType=AutoLoop 涓 Kind=JoinPoint 鐨勪换鍔 + /// 鎸夌洰鏍囩珯鐐瑰垎缁勶紝浼樺厛绾ф帓搴忥紝閫夋嫨鏈夎溅鍦ㄧ珯鐨勬渶楂樹紭鍏堢骇浠诲姟 + /// + /// 浠诲姟鍒楄〃 + protected virtual void ProcessJoinPointTasks(List allTasks) + { + // 鎸夌洰鏍囩珯鐐瑰垎缁勶紙姹囧悎鐐癸級 + var joinGroups = allTasks + .Where(t => t.Kind == TaskKind.JoinPoint && t.StartType == TaskStartType.AutoLoop) + .GroupBy(t => t.TargetStationId) + .ToList(); + + foreach (var group in joinGroups) + { + int targetSiteId = group.Key; + + // 妫鏌ユ眹鍚堢偣娴侀噺鎺у埗 + var firstTask = group.First(); + if (!CheckTrafficControl(targetSiteId, firstTask.TrafficControl)) + continue; + + // 鎸変紭鍏堢骇鎺掑簭锛岄夋嫨鏈夎溅鍦ㄧ珯鐨勬渶楂樹紭鍏堢骇浠诲姟 + var sortedTasks = group.OrderByDescending(t => t.Priority).ToList(); + + foreach (var task in sortedTasks) + { + var car = FindCarArrivedAtSite(task.CurrentStationId); + + if (car != null && Commons.SelectCar(car) == 1) + { + AssignCarToTarget(car, targetSiteId); + Diagnosis.Post($"[AbstractLoopMission] JoinPoint: 杞﹁締 {car.name} 浠庣珯鐐 {task.CurrentStationId} 姹囧叆鐩爣 {targetSiteId}锛堜紭鍏堢骇 {task.Priority}锛", $"JoinPoint-AutoLoop", true); + + //break; // 姹囧悎鐐逛竴娆″彧鏀捐涓杈嗚溅 + } + } + } + } + + /// + /// 鍒嗘祦鐐圭洰鏍囬夋嫨 + /// 鎸変紭鍏堢骇閫夋嫨娴侀噺鍏佽鐨勭洰鏍囩珯鐐 + /// + /// 褰撳墠鍒嗘祦浠诲姟 + /// 鎵鏈変换鍔″垪琛 + /// 閫変腑鐨勭洰鏍囩珯鐐笽D + protected virtual int SelectBranchTarget(LoopTask branchTask, List allTasks) + { + // 鏌ユ壘鍚屼竴褰撳墠绔欑偣鐨勬墍鏈夊垎娴佺洰鏍 + var branches = allTasks + .Where(t => t.Kind == TaskKind.BranchPoint && t.CurrentStationId == branchTask.CurrentStationId) + .OrderByDescending(t => t.Priority) + .ToList(); + + // 鎸変紭鍏堢骇閫夋嫨娴侀噺鍏佽鐨勭洰鏍 + foreach (var branch in branches) + { + if (CheckTrafficControl(branch.TargetStationId, branch.TrafficControl)) + { + return branch.TargetStationId; + } + } + + // 榛樿杩斿洖鍘熶换鍔$洰鏍 + return branchTask.TargetStationId; + } + + /// + /// 姹囧悎鐐归氳鍒ゆ柇 + /// 妫鏌ュ綋鍓嶄换鍔℃槸鍚︽湁鏈楂樹紭鍏堢骇閫氳鏉冿紙鏄惁鏈夋洿楂樹紭鍏堢骇鐨勮溅鍦ㄧ瓑寰咃級 + /// + /// 褰撳墠姹囧悎浠诲姟 + /// 鎵鏈変换鍔″垪琛 + /// true=鍙互閫氳锛宖alse=闇瑕佺瓑寰呮洿楂樹紭鍏堢骇鐨勮溅 + protected virtual bool CanJoinPass(LoopTask joinTask, List allTasks) + { + // 鏌ユ壘鍚屼竴鐩爣绔欑偣鐨勬墍鏈夋眹鍚堟潵婧 + var joinSources = allTasks + .Where(t => t.Kind == TaskKind.JoinPoint && t.TargetStationId == joinTask.TargetStationId) + .OrderByDescending(t => t.Priority) + .ToList(); + + // 妫鏌ユ槸鍚︽湁鏇撮珮浼樺厛绾х殑浠诲姟涓斿叾绔欑偣鏈夌┖闂茶溅绛夊緟 + foreach (var source in joinSources) + { + if (source.Priority > joinTask.Priority) + { + var higherPriorityCar = FindCarArrivedAtSite(source.CurrentStationId); + if (higherPriorityCar != null && IsCarIdle(higherPriorityCar)) + { + return false; // 鏈夋洿楂樹紭鍏堢骇鐨勮溅绛夊緟锛屽綋鍓嶈溅闇瑕佽琛 + } + } + } + + return true; + } + + /// + /// 涓鸿溅杈嗗垎閰嶇洰鏍囩珯鐐 + /// 璁剧疆杞﹁締鐨 goalSite 鏍囩鍜 loopAssigned 鏃堕棿鎴 + /// + /// 杞﹁締 + /// 鐩爣绔欑偣ID + protected virtual void AssignCarToTarget(Car car, int targetSiteId) + { + if (car == null) + return; + + try + { + string targetIdStr = targetSiteId.ToString(); + + // 璁剧疆鎴栨洿鏂扮洰鏍囩珯鐐规爣绛 + if (car.tags.ContainsKey("goalSite")) + car.tags["goalSite"] = targetIdStr; + else + car.tags.Add("goalSite", targetIdStr); + + // 璁板綍鍒嗛厤鏃堕棿锛堢敤浜庤皟璇曞拰鐩戞帶锛 + if (!car.tags.ContainsKey("loopAssigned")) + car.tags.Add("loopAssigned", DateTime.Now.ToString("HH:mm:ss")); + } + catch (Exception ex) + { + System.Diagnostics.Debug.WriteLine($"[AbstractLoopMission] AssignCarToTarget error: {ex}"); + } + } + + #endregion + + #region 绾跨▼鐢熷懡鍛ㄦ湡 + + /// + /// 鍚姩绛栫暐鍚屾绾跨▼ + /// 姣忕鍚屾涓娆¢厤缃枃浠讹紝鑷姩鏇存柊浠诲姟鍒楄〃 + /// + public virtual void StartLoop() + { + if (_strategyRunning) + return; + + _strategyRunning = true; + _strategyThread = new Thread(() => + { + while (_strategyRunning) + { + try + { + ApplyTaskStrategy(); + Thread.Sleep(STRATEGY_SYNC_INTERVAL_MS); + } + catch (ThreadAbortException) + { + break; + } + catch (Exception ex) + { + System.Diagnostics.Debug.WriteLine($"[AbstractLoopMission] Strategy loop error: {ex}"); + } + } + }) + { + Name = "AbstractLoopMission_Strategy", + IsBackground = true + }; + _strategyThread.Start(); + + status.status = "绛栫暐鍚屾宸插惎鍔"; + System.Diagnostics.Debug.WriteLine("[AbstractLoopMission] StartLoop: 绛栫暐鍚屾绾跨▼宸插惎鍔"); + } + + /// + /// 鍚姩涓氬姟閫昏緫绾跨▼ + /// 姣 500ms 澶勭悊涓娆′换鍔¤皟搴 + /// + public virtual void StartLogicLoop() + { + if (_logicRunning) + return; + + _logicRunning = true; + _logicThread = new Thread(ExecuteLogicLoop) + { + Name = "AbstractLoopMission_Logic", + IsBackground = true + }; + _logicThread.Start(); + + status.status = "涓氬姟閫昏緫宸插惎鍔"; + System.Diagnostics.Debug.WriteLine("[AbstractLoopMission] StartLogicLoop: 涓氬姟閫昏緫绾跨▼宸插惎鍔"); + } + + /// + /// 鍋滄绛栫暐鍚屾绾跨▼ + /// + public virtual void StopLoop() + { + try + { + _strategyRunning = false; + _strategyThread?.Join(2000); + _strategyThread = null; + System.Diagnostics.Debug.WriteLine("[AbstractLoopMission] StopLoop: 绛栫暐鍚屾绾跨▼宸插仠姝"); + } + catch { } + } + + /// + /// 鍋滄涓氬姟閫昏緫绾跨▼ + /// + public virtual void StopLogicLoop() + { + try + { + _logicRunning = false; + _logicThread?.Join(2000); + _logicThread = null; + System.Diagnostics.Debug.WriteLine("[AbstractLoopMission] StopLogicLoop: 涓氬姟閫昏緫绾跨▼宸插仠姝"); + } + catch { } + } + + /// + /// 鍚姩鎵鏈夌嚎绋嬶紙绛栫暐鍚屾 + 涓氬姟閫昏緫锛 + /// + public virtual void StartAll() + { + // ApplyTaskStrategy(); + MarkTaskStartSitesAsTerminal(); + StartLoop(); + StartLogicLoop(); + status.status = "宸插惎鍔"; + } + + /// + /// 灏嗕换鍔$瓥鐣ヤ腑鐨勮捣濮嬬珯鐐规爣璁颁负 terminal=true + /// + private void MarkTaskStartSitesAsTerminal() + { + List startSiteIds; + lock (_tasksLock) + { + startSiteIds = Tasks + .Select(t => t.CurrentStationId) + .Where(id => id > 0) + .Distinct() + .ToList(); + } + + foreach (var siteId in startSiteIds) + { + var site = SimpleLib.GetSite(siteId); + if (site == null) + continue; + + Commons.AddOrUpdateSiteField(site, "terminal", "true"); + } + } + + /// + /// 鍋滄鎵鏈夌嚎绋 + /// + public virtual void StopAll() + { + StopLoop(); + StopLogicLoop(); + status.status = "宸插仠姝"; + } + + /// + /// 閲婃斁璧勬簮 + /// 娓呯悊瑙﹀彂鍣ㄩ傞厤鍣ㄣ佸仠姝㈡墍鏈夌嚎绋嬨佸彇娑堢瓥鐣ヨ闃 + /// + public void Dispose() + { + // 娓呯悊瑙﹀彂鍣ㄩ傞厤鍣 + foreach (var adapter in _adapters.ToArray()) + { + try + { + adapter?.Dispose(); + } + catch { } + } + _adapters.Clear(); + + // 鍋滄鎵鏈夌嚎绋 + StopAll(); + + // 鍙栨秷绛栫暐璁㈤槄 + if (_taskStrategy is JsonFileTaskStrategy jsonStrategy) + { + jsonStrategy.Changed -= OnTaskStrategyChanged; + jsonStrategy.DisposeWatcher(); + } + + System.Diagnostics.Debug.WriteLine("[AbstractLoopMission] Dispose: 璧勬簮宸查噴鏀"); + } + + #endregion + + #region 杈呭姪绫诲瀷 + + /// + /// 浠诲姟鍒楄〃鍙樻洿浜嬩欢鍙傛暟 + /// + public class TaskListChangedEventArgs : EventArgs + { + /// 鍙樻洿绫诲瀷 + public TaskChangeType ChangeType { get; } + + /// 鍙樻洿鐨勪换鍔$储寮曪紙Replaced 鏃朵负 -1锛 + public int Index { get; } + + /// 鍙樻洿鐨勪换鍔★紙Replaced 鏃朵负 null锛 + public LoopTask Task { get; } + + /// + /// 鍒涘缓浠诲姟鍒楄〃鍙樻洿浜嬩欢鍙傛暟 + /// + /// 鍙樻洿绫诲瀷 + /// 浠诲姟绱㈠紩 + /// 浠诲姟瀵硅薄 + public TaskListChangedEventArgs(TaskChangeType changeType, int index, LoopTask task) + { + ChangeType = changeType; + Index = index; + Task = task; + } + } + + /// + /// 浠诲姟鍙樻洿绫诲瀷鏋氫妇 + /// + public enum TaskChangeType + { + /// 娣诲姞浠诲姟 + Added, + /// 鏇存柊浠诲姟 + Updated, + /// 绉婚櫎浠诲姟 + Removed, + /// 鏇挎崲鍏ㄩ儴浠诲姟 + Replaced + } + + /// + /// 寰幆鐐归厤缃被 + /// 鐢ㄤ簬瀹氫箟寰幆鐐圭殑瑙勫垯鍜屽睘鎬 + /// + public class LoopPoint + { + /// 鍞竴鏍囪瘑 + public string Id { get; set; } = Guid.NewGuid().ToString("N"); + + /// 鍚嶇О + public string Name { get; set; } = string.Empty; + + /// 鑷畾涔夋爣绛鹃泦鍚 + public Dictionary Tags { get; } = new Dictionary(); + + /// 杩涘叆瑙勫垯 + public IEnterRule EnterRule { get; set; } + + /// 閫鍑鸿鍒 + public IExitRule ExitRule { get; set; } + + /// 姹囧悎瑙勫垯 + public IJoinRule JoinRule { get; set; } + + /// 鍒嗘祦瑙勫垯 + public IBranchRule BranchRule { get; set; } + } + + /// + /// JSON 鏂囦欢浠诲姟绛栫暐绫 + /// 浠 tasklist.json 璇诲彇浠诲姟閰嶇疆锛屾敮鎸佹枃浠跺彉鏇磋嚜鍔ㄥ埛鏂 + /// + public class JsonFileTaskStrategy : ITaskStrategy, IDisposable + { + /// JSON 鏂囦欢璺緞 + public string JsonPath { get; set; } + + /// 浠诲姟缂撳瓨鍒楄〃 + private List _cache = new List(); + + /// 鏂囦欢绯荤粺鐩戝惉鍣 + private FileSystemWatcher _watcher; + + /// 閰嶇疆鍙樻洿浜嬩欢 + public event EventHandler Changed; + + /// + /// 鍒涘缓 JSON 鏂囦欢浠诲姟绛栫暐 + /// + /// JSON 鏂囦欢璺緞锛坣ull 鏃朵娇鐢ㄩ粯璁よ矾寰 tasklist.json锛 + public JsonFileTaskStrategy(string jsonPath = null) + { + JsonPath = string.IsNullOrWhiteSpace(jsonPath) + ? Path.Combine(Application.StartupPath, "tasklist.json") + : jsonPath; + + EnsureWatcher(); + Refresh(); + } + + /// + /// 鑾峰彇浠诲姟鍒楄〃 + /// + /// 浠诲姟鍒楄〃鍓湰 + public IEnumerable GetTasks() + { + return _cache.ToArray(); + } + + /// + /// 鍒锋柊浠诲姟鍒楄〃锛堜粠鏂囦欢閲嶆柊璇诲彇锛 + /// + public void Refresh() + { + try + { + if (!File.Exists(JsonPath)) + { + _cache = new List(); + return; + } + + string json = File.ReadAllText(JsonPath); + _cache = JsonConvert.DeserializeObject>(json) ?? new List(); + } + catch + { + _cache = new List(); + } + } + + /// + /// 纭繚鏂囦欢鐩戝惉鍣ㄥ凡鍚姩 + /// + public void EnsureWatcher() + { + try + { + if (_watcher != null) + return; + + string directory = Path.GetDirectoryName(JsonPath); + string fileName = Path.GetFileName(JsonPath); + + if (string.IsNullOrEmpty(directory)) + return; + + _watcher = new FileSystemWatcher(directory, fileName) + { + NotifyFilter = NotifyFilters.LastWrite | NotifyFilters.Size | NotifyFilters.FileName + }; + + _watcher.Changed += (s, e) => OnFileChanged(); + _watcher.Renamed += (s, e) => OnFileChanged(); + _watcher.EnableRaisingEvents = true; + } + catch { } + } + + private void OnFileChanged() + { + try + { + Thread.Sleep(50); + Refresh(); + Changed?.Invoke(this, EventArgs.Empty); + } + catch { } + } + + public void DisposeWatcher() + { + try + { + if (_watcher != null) + { + _watcher.EnableRaisingEvents = false; + _watcher.Dispose(); + _watcher = null; + } + } + catch { } + } + + public void Dispose() + { + DisposeWatcher(); + } + } + + #endregion + + #region 璺緞鏌ユ壘杈呭姪鏂规硶 + + /// + /// 鏌ユ壘浠庤捣鐐瑰埌缁堢偣涔嬮棿鐨勬墍鏈夌珯鐐癸紙鍩轰簬杞ㄩ亾杩炴帴鐨勫箍搴︿紭鍏堟悳绱級 + /// + /// 璧风偣绔欑偣ID + /// 缁堢偣绔欑偣ID + /// 浠庤捣鐐瑰埌缁堢偣鐨勭珯鐐笽D鍒楄〃锛堝寘鍚捣鐐瑰拰缁堢偣锛夛紝鏈壘鍒拌矾寰勮繑鍥炵┖鍒楄〃 + /// + /// + /// // 鏌ユ壘绔欑偣100鍒扮珯鐐200涔嬮棿鐨勬墍鏈夌珯鐐 + /// var sites = GetSitesBetween(100, 200); + /// // 杩斿洖: [100, 150, 180, 200] + /// + /// + protected List GetSitesBetween(int startSiteId, int endSiteId) + { + if (startSiteId == endSiteId) + return new List { startSiteId }; + + try + { + // 鏋勫缓绔欑偣閭绘帴琛 + var adjacency = BuildSiteAdjacency(); + + // 骞垮害浼樺厛鎼滅储 + var visited = new HashSet(); + var queue = new Queue>(); + queue.Enqueue(new List { startSiteId }); + visited.Add(startSiteId); + + while (queue.Count > 0) + { + var currentPath = queue.Dequeue(); + int currentSite = currentPath[currentPath.Count - 1]; + + // 鎵惧埌缁堢偣锛岃繑鍥炶矾寰 + if (currentSite == endSiteId) + return currentPath; + + // 鑾峰彇鐩搁偦绔欑偣 + if (!adjacency.TryGetValue(currentSite, out var neighbors)) + continue; + + foreach (var neighbor in neighbors) + { + if (visited.Contains(neighbor)) + continue; + + visited.Add(neighbor); + var newPath = new List(currentPath) { neighbor }; + queue.Enqueue(newPath); + } + } + + // 鏈壘鍒拌矾寰 + return new List(); + } + catch (Exception ex) + { + System.Diagnostics.Debug.WriteLine($"[AbstractLoopMission] GetSitesBetween({startSiteId}, {endSiteId}) error: {ex.Message}"); + return new List(); + } + } + + /// + /// 鏌ユ壘浠庤捣鐐瑰埌缁堢偣涔嬮棿鐨勬墍鏈夌珯鐐瑰璞 + /// + /// 璧风偣绔欑偣ID + /// 缁堢偣绔欑偣ID + /// 浠庤捣鐐瑰埌缁堢偣鐨勭珯鐐瑰璞″垪琛 + protected List GetSiteObjectsBetween(int startSiteId, int endSiteId) + { + var siteIds = GetSitesBetween(startSiteId, endSiteId); + var result = new List(); + + foreach (var siteId in siteIds) + { + var site = SimpleLib.GetSite(siteId); + if (site != null) + result.Add(site); + } + + return result; + } + + /// + /// 鏋勫缓绔欑偣閭绘帴琛紙鍩轰簬杞ㄩ亾杩炴帴锛岃冭檻杞ㄩ亾鏂瑰悜锛 + /// + /// 閭绘帴琛細绔欑偣ID -> 鍙揪鐨勭浉閭荤珯鐐笽D鍒楄〃 + private Dictionary> BuildSiteAdjacency() + { + var adjacency = new Dictionary>(); + + foreach (var track in SimpleLib.GetAllTracks()) + { + int siteA = track.siteA; + int siteB = track.siteB; + + // 鏍规嵁杞ㄩ亾鏂瑰悜娣诲姞閭绘帴鍏崇郴 + // direction: 0=鍙屽悜, 1=A->B, 2=B->A + switch (track.direction) + { + case 0: // 鍙屽悜 + AddAdjacency(adjacency, siteA, siteB); + AddAdjacency(adjacency, siteB, siteA); + break; + case 1: // A -> B + AddAdjacency(adjacency, siteA, siteB); + break; + case 2: // B -> A + AddAdjacency(adjacency, siteB, siteA); + break; + default: + AddAdjacency(adjacency, siteA, siteB); + AddAdjacency(adjacency, siteB, siteA); + break; + } + } + + return adjacency; + } + + /// + /// 鍚戦偦鎺ヨ〃娣诲姞杈 + /// + private void AddAdjacency(Dictionary> adjacency, int from, int to) + { + if (!adjacency.ContainsKey(from)) + adjacency[from] = new HashSet(); + + adjacency[from].Add(to); + } + + /// + /// 妫鏌ヨ矾寰勪笂鏄惁鏈夊叾浠栬溅杈嗭紙涓嶅寘鍚捣鐐瑰拰缁堢偣锛 + /// + /// 璧风偣绔欑偣ID + /// 缁堢偣绔欑偣ID + /// 鎺掗櫎鐨勮溅杈嗭紙閫氬父鏄綋鍓嶈溅杈嗭級 + /// true=璺緞涓婃湁杞﹁締锛宖alse=璺緞鐣呴 + protected bool HasCarOnPath(int startSiteId, int endSiteId, Car excludeCar = null) + { + var pathSites = GetSitesBetween(startSiteId, endSiteId); + + // 鎺掗櫎璧风偣鍜岀粓鐐 + var middleSites = pathSites + .Skip(1) + .Take(pathSites.Count - 2) + .ToList(); + + if (middleSites.Count == 0) + return false; + + foreach (var car in SimpleLib.GetAllCars().OfType()) + { + if (excludeCar != null && car == excludeCar) + continue; + + if (car?.status?.holdingLocks == null) + continue; + + // 妫鏌ヨ溅杈嗘槸鍚﹀湪璺緞涓棿绔欑偣 + foreach (var siteId in middleSites) + { + if (car.status.holdingLocks.Contains(siteId)) + return true; + } + } + + return false; + } + + /// + /// 鑾峰彇璺緞涓婄殑鎵鏈夎溅杈 + /// + /// 璧风偣绔欑偣ID + /// 缁堢偣绔欑偣ID + /// 鏄惁鍖呭惈璧风偣鍜岀粓鐐圭殑杞﹁締 + /// 璺緞涓婄殑杞﹁締鍒楄〃 + protected List GetCarsOnPath(int startSiteId, int endSiteId, bool includeEndpoints = false) + { + var pathSites = GetSitesBetween(startSiteId, endSiteId); + + if (!includeEndpoints && pathSites.Count > 2) + { + pathSites = pathSites + .Skip(1) + .Take(pathSites.Count - 2) + .ToList(); + } + + var carsOnPath = new List(); + + foreach (var car in SimpleLib.GetAllCars().OfType()) + { + if (car?.status?.holdingLocks == null) + continue; + + foreach (var siteId in pathSites) + { + if (car.status.holdingLocks.Contains(siteId)) + { + carsOnPath.Add(car); + break; + } + } + } + + return carsOnPath; + } + + /// + /// 璁$畻涓ょ珯鐐逛箣闂寸殑璺緞闀垮害锛堢粡杩囩殑绔欑偣鏁帮級 + /// + /// 璧风偣绔欑偣ID + /// 缁堢偣绔欑偣ID + /// 璺緞闀垮害锛堢珯鐐规暟锛夛紝鏈壘鍒拌矾寰勮繑鍥 -1 + protected int GetPathLength(int startSiteId, int endSiteId) + { + var path = GetSitesBetween(startSiteId, endSiteId); + return path.Count > 0 ? path.Count : -1; + } + + #endregion + + #region 浠诲姟绛栫暐鍖归厤 + + /// + /// 璺緞缂撳瓨锛堥伩鍏嶉噸澶嶈绠楋級 + /// + [JsonIgnore] + private readonly Dictionary<(int, int), List> _pathCache = new Dictionary<(int, int), List>(); + + /// + /// 璺緞缂撳瓨閿 + /// + [JsonIgnore] + private readonly object _pathCacheLock = new object(); + + /// + /// 鏍规嵁杞﹁締褰撳墠绔欑偣鍖归厤鏈浼樹换鍔$瓥鐣 + /// 鍖归厤閫昏緫锛 + /// + /// 閬嶅巻鎵鏈変换鍔$瓥鐣ワ紝鑾峰彇姣忎釜绛栫暐浠庡綋鍓嶇偣鍒扮洰鏍囩偣鐨勫畬鏁磋矾寰 + /// 妫鏌ヨ溅杈嗗綋鍓嶇珯鐐规槸鍚﹀湪璇ヨ矾寰勪笂 + /// 杩斿洖鍖呭惈褰撳墠绔欑偣涓斾紭鍏堢骇鏈楂樼殑绛栫暐 + /// + /// + /// 杞﹁締 + /// 鏈浼樺尮閰嶇粨鏋滐紝鏃犲尮閰嶈繑鍥 null + protected TaskPathMatchResult FindBestTaskForCar(Car car) + { + if (car?.status?.holdingLocks == null || car.status.holdingLocks.Length == 0) + return null; + + int currentSiteId = car.status.holdingLocks[0]; + return FindBestTaskForSite(currentSiteId); + } + + /// + /// 鏍规嵁绔欑偣ID鍖归厤鏈浼樹换鍔$瓥鐣 + /// 閬嶅巻鎵鏈変换鍔★紝鎵惧嚭鍖呭惈璇ョ珯鐐圭殑璺緞锛岃繑鍥炰紭鍏堢骇鏈楂樼殑浠诲姟 + /// + /// 褰撳墠绔欑偣ID + /// 鏈浼樺尮閰嶇粨鏋滐紝鏃犲尮閰嶈繑鍥 null + protected TaskPathMatchResult FindBestTaskForSite(int siteId) + { + List snapshot; + lock (_tasksLock) + { + snapshot = Tasks.Where(T=>T.IsViaPoint).ToList(); + } + + var matchResults = new List(); + + foreach (var task in snapshot) + { + // 鑾峰彇浠诲姟鐨勫畬鏁磋矾寰勶紙浠庡綋鍓嶇偣鍒扮洰鏍囩偣锛 + var fullPath = GetSitesBetweenCached(task.CurrentStationId, task.TargetStationId); + + if (fullPath.Count == 0) + continue; + + // 妫鏌ョ珯鐐规槸鍚﹀湪璺緞涓 + int indexInPath = fullPath.IndexOf(siteId); + if (indexInPath < 0) + continue; + + + // 鍒涘缓鍖归厤缁撴灉 + var result = new TaskPathMatchResult + { + Task = task, + CurrentSiteId = siteId, + FullPath = fullPath, + IndexInPath = indexInPath, + RemainingPath = fullPath.Skip(indexInPath).ToList(), + DistanceToTarget = fullPath.Count - indexInPath - 1, + IsAtStartPoint = (indexInPath == 0), + IsAtEndPoint = (indexInPath == fullPath.Count - 1), + IsOnMiddlePath = (indexInPath > 0 && indexInPath < fullPath.Count - 1) + }; + + matchResults.Add(result); + } + + if (matchResults.Count == 0) + return null; + + // 鎸夊尮閰嶄紭鍏堢骇鎺掑簭锛 + // 1. 璧风偣鍖归厤浼樺厛 + // 2. 浠诲姟浼樺厛绾ч珮鐨勪紭鍏 + // 3. 璺濈鐩爣杩戠殑浼樺厛 + var bestMatch = matchResults.Where(r=>!r.IsAtEndPoint && !r.IsAtStartPoint ) + .OrderByDescending(r => r.IsAtStartPoint ? 1 : 0) // 璧风偣鍖归厤浼樺厛 + .ThenByDescending(r => r.Task.Priority) // 浠诲姟浼樺厛绾ч珮鐨勪紭鍏 + .ThenBy(r => r.DistanceToTarget) // 璺濈鐩爣杩戠殑浼樺厛 + .FirstOrDefault(); + Diagnosis.Post($"[AbstractLoopMission] 灏忚溅鑾峰彇鍒扮殑浠诲姟绛栫暐 {bestMatch.ToString()} 锛", $"璺緞涓棿鐐瑰垵濮嬪寲", true); + return bestMatch; + } + + /// + /// 鏍规嵁绔欑偣ID鏌ユ壘鎵鏈夊尮閰嶇殑浠诲姟绛栫暐 + /// 杩斿洖鎵鏈夊寘鍚绔欑偣鐨勪换鍔★紝鎸変紭鍏堢骇鎺掑簭 + /// + /// 绔欑偣ID + /// 鍖归厤缁撴灉鍒楄〃锛堟寜浼樺厛绾ч檷搴忥級 + protected List FindAllTasksForSite(int siteId) + { + List snapshot; + lock (_tasksLock) + { + snapshot = Tasks.ToList(); + } + + var matchResults = new List(); + + foreach (var task in snapshot) + { + // 鑾峰彇浠诲姟鐨勫畬鏁磋矾寰 + var fullPath = GetSitesBetweenCached(task.CurrentStationId, task.TargetStationId); + + if (fullPath.Count == 0) + continue; + + // 妫鏌ョ珯鐐规槸鍚﹀湪璺緞涓 + int indexInPath = fullPath.IndexOf(siteId); + if (indexInPath < 0) + continue; + + var result = new TaskPathMatchResult + { + Task = task, + CurrentSiteId = siteId, + FullPath = fullPath, + IndexInPath = indexInPath, + RemainingPath = fullPath.Skip(indexInPath).ToList(), + DistanceToTarget = fullPath.Count - indexInPath - 1, + IsAtStartPoint = (indexInPath == 0), + IsAtEndPoint = (indexInPath == fullPath.Count - 1), + IsOnMiddlePath = (indexInPath > 0 && indexInPath < fullPath.Count - 1) + }; + + matchResults.Add(result); + } + + // 鎸変紭鍏堢骇鎺掑簭 + return matchResults + .OrderByDescending(r => r.IsAtStartPoint ? 1 : 0) + .ThenByDescending(r => r.Task.Priority) + .ThenBy(r => r.DistanceToTarget) + .ToList(); + } + + /// + /// 鑾峰彇杞﹁締搴旇鍓嶅線鐨勪笅涓涓洰鏍囩珯鐐 + /// 鏍规嵁鏈浼樺尮閰嶇瓥鐣ヨ繑鍥炵洰鏍囩珯鐐笽D + /// + /// 杞﹁締 + /// 鐩爣绔欑偣ID锛屾棤鍖归厤杩斿洖 -1 + protected int GetRecommendedTargetForCar(Car car) + { + var match = FindBestTaskForCar(car); + return match?.Task?.TargetStationId ?? -1; + } + + /// + /// 鑾峰彇杞﹁締鐨勪笅涓涓腑闂寸珯鐐 + /// 杩斿洖褰撳墠浣嶇疆鐨勪笅涓涓矾寰勮妭鐐 + /// + /// 杞﹁締 + /// 涓嬩竴绔欑偣ID锛屾棤鍖归厤杩斿洖 -1 + protected int GetNextSiteForCar(Car car) + { + var match = FindBestTaskForCar(car); + if (match?.RemainingPath == null || match.RemainingPath.Count < 2) + return -1; + + return match.RemainingPath[1]; + } + + /// + /// 鑾峰彇缂撳瓨鐨勮矾寰勶紙閬垮厤閲嶅璁$畻锛 + /// + private List GetSitesBetweenCached(int startSiteId, int endSiteId) + { + var key = (startSiteId, endSiteId); + + lock (_pathCacheLock) + { + if (_pathCache.TryGetValue(key, out var cached)) + return new List(cached); + + var path = GetSitesBetween(startSiteId, endSiteId); + _pathCache[key] = path; + return new List(path); + } + } + + /// + /// 娓呴櫎璺緞缂撳瓨 + /// 褰撲换鍔$瓥鐣ュ彉鏇存垨鍦板浘鍙樻洿鏃惰皟鐢 + /// + protected void ClearPathCache() + { + lock (_pathCacheLock) + { + _pathCache.Clear(); + } + } + + /// + /// 鏍规嵁浠诲姟ID鏌ユ壘浠诲姟 + /// + /// 浠诲姟ID + /// 浠诲姟瀵硅薄锛屾湭鎵惧埌杩斿洖 null + protected LoopTask FindTaskById(int taskId) + { + lock (_tasksLock) + { + return Tasks.FirstOrDefault(t => t.Id == taskId); + } + } + + /// + /// 鍒ゆ柇杞﹁締鏄惁鍦ㄦ寚瀹氫换鍔$殑璺緞涓 + /// + /// 杞﹁締 + /// 浠诲姟 + /// 鏄惁鍦ㄨ矾寰勪笂 + protected bool IsCarOnTaskPath(Car car, LoopTask task) + { + if (car?.status?.holdingLocks == null || car.status.holdingLocks.Length == 0) + return false; + + int currentSiteId = car.status.holdingLocks[0]; + var path = GetSitesBetweenCached(task.CurrentStationId, task.TargetStationId); + + return path.Contains(currentSiteId); + } + + #endregion + + #region 浠诲姟鍖归厤缁撴灉绫诲瀷 + + /// + /// 浠诲姟璺緞鍖归厤缁撴灉 + /// + public class TaskPathMatchResult + { + /// 鍖归厤鐨勪换鍔 + public LoopTask Task { get; set; } + + /// 杞﹁締褰撳墠绔欑偣ID + public int CurrentSiteId { get; set; } + + /// 瀹屾暣璺緞锛堜粠浠诲姟璧风偣鍒扮粓鐐癸級 + public List FullPath { get; set; } = new List(); + + /// 褰撳墠绔欑偣鍦ㄨ矾寰勪腑鐨勭储寮 + public int IndexInPath { get; set; } + + /// 鍓╀綑璺緞锛堜粠褰撳墠绔欑偣鍒扮粓鐐癸級 + public List RemainingPath { get; set; } = new List(); + + /// 鍒扮洰鏍囩殑璺濈锛堝墿浣欑珯鐐规暟锛 + public int DistanceToTarget { get; set; } + + /// 鏄惁鍦ㄤ换鍔¤捣鐐 + public bool IsAtStartPoint { get; set; } + + /// 鏄惁鍦ㄤ换鍔$粓鐐 + public bool IsAtEndPoint { get; set; } + + /// 鏄惁鍦ㄨ矾寰勪腑闂 + public bool IsOnMiddlePath { get; set; } + + /// 浠诲姟ID + public int TaskId => Task?.Id ?? 0; + + /// 浠诲姟浼樺厛绾 + public int TaskPriority => Task?.Priority ?? 0; + + /// 鐩爣绔欑偣ID + public int TargetSiteId => Task?.TargetStationId ?? -1; + + /// 涓嬩竴涓珯鐐笽D锛堝綋鍓嶇珯鐐圭殑涓嬩竴璺筹級 + public int NextSiteId => RemainingPath.Count > 1 ? RemainingPath[1] : -1; + + /// 璺緞杩涘害鐧惧垎姣 + public double ProgressPercent => FullPath.Count > 1 + ? (double)IndexInPath / (FullPath.Count - 1) * 100 + : 0; + + /// + /// 鑾峰彇鍖归厤淇℃伅鎻忚堪 + /// + public override string ToString() + { + string position = IsAtStartPoint ? "璧风偣" : (IsAtEndPoint ? "缁堢偣" : "涓棿"); + return $"浠诲姟[ID={TaskId}, {Task?.CurrentStationId}->{Task?.TargetStationId}] " + + $"浼樺厛绾={TaskPriority}, 褰撳墠={CurrentSiteId}({position}), " + + $"杩涘害={ProgressPercent:F1}%, 鍓╀綑={DistanceToTarget}绔, " + + $"涓嬩竴绔={NextSiteId}"; + } + } + + #endregion + + private LoopViewer lv; + + + [MethodMember(Name = "浠诲姟绠$悊", Description = "浠诲姟绠$悊")] + public void Print() + { + lv = new LoopViewer(); + lv.Show(); + } + + [MethodMember(Name = "鍚姩鐜嚎", Description = "鍚姩鐜嚎")] + public void Start() + { + StartAll(); + } + + [MethodMember(Name = "鍋滄鐜嚎", Description = "鍋滄鐜嚎")] + public void Stop() + { + StopAll(); + } + + } +} diff --git a/StandardScene.Core/Chained/ChainedDeliveryMission.cs b/StandardScene.Core/Chained/ChainedDeliveryMission.cs new file mode 100644 index 0000000..3ee03a6 --- /dev/null +++ b/StandardScene.Core/Chained/ChainedDeliveryMission.cs @@ -0,0 +1,1509 @@ +using IoTClient.Common.Helpers; +using Newtonsoft.Json; +using SimpleLite; +using SimpleLite.RCS; +using SimpleLite.RCS.CarTypes; +using SimpleLite.CADTools; +using SimpleLite.Props; +using SimpleLite.UI; +using SimpleCore; +using SimpleCore.Compiler; +using SimpleCore.Library; +using SimpleCore.PropType; +using SimpleCore.Traffic; +using StandardScene.CarTypes; +using StandardScene.CommonTools; +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Globalization; +using System.IO; +using System.Linq; +using System.Numerics; +using System.Threading; +using System.Threading.Tasks; + +namespace StandardScene.Chained +{ + /// + /// 鎶借薄閾惧紡鎼繍浠诲姟绫 + /// 瀹炵幇AGV鎼繍浠诲姟鐨勬牳蹇冭皟搴﹂昏緫锛屾敮鎸佷换鍔¢摼寮忔墽琛屻佹櫤鑳借皟搴︺佽矾寰勮鍒掋佸啿绐侀伩璁╃瓑鍔熻兘 + /// 鍙互鍋氬彇婊℃斁绌烘搷浣 + /// + public abstract class ChainedDeliveryMission:Mission + { + #region Parameters / Status + /// + /// 閾惧紡鎼繍浠诲姟鍙傛暟閰嶇疆绫 + /// + private class ChainedDeliveryParams + { + /// + /// 鏄惁鍚敤鎵撴柇姝e湪鍘婚伩璁╃偣鐨勫皬杞﹀姛鑳 + /// + public bool EnableBlockStandby { get; } = true; + + /// + /// 鏄惁鍚敤绱у瘑浠诲姟閾炬ā寮 + /// true: 閾惧紡鎼繍浠诲姟鍙互鍚屾椂鍚姩锛堝墠搴忎换鍔″紑濮嬫椂鍚庣画浠诲姟鍗冲彲寮濮嬶級锛屾彁楂樻晥鐜囦絾鍙兘閫犳垚姝婚攣 + /// false: 鍓嶅簭浠诲姟蹇呴』瀹屾垚鍚庯紝鍚庣画浠诲姟鎵嶈兘寮濮嬶紝鏇村畨鍏ㄤ絾鏁堢巼杈冧綆 + /// + public bool TightDeliveryChain { get; } = true; + } + + /// + /// 鎼繍浠诲姟鐘舵佺粺璁$被 + /// 璁板綍浠诲姟鎵ц杩囩▼涓殑鍚勭缁熻淇℃伅 + /// + public class DeliveryMissionStatus : MissionStatus + { + /// 鎺掗槦绛夊緟鎵ц鐨勪换鍔℃暟閲 + public int Enqueued; + /// 宸插畬鎴愮殑浠诲姟鏁伴噺 + public int Performed; + /// 姣忓皬鏃朵綔涓氭暟锛圝obs Per Hour锛夛紝鐢ㄤ簬琛¢噺绯荤粺鍚炲悙閲 + public double Jph; + /// 娣ょН浠诲姟淇℃伅瀛楃涓诧紝鏍煎紡锛氫换鍔D:娣ょН娆℃暟 + public string StuckInfo = "/"; + /// 褰撳墠娣ょН鐨勪换鍔℃暟閲忥紙鏃犳硶鎵ц鐨勪换鍔★級 + public int StuckNum; + + /// + /// 鍒涘缓鍚庯紝绛夊緟鏃堕棿瓒呰繃璇ラ槇鍊肩殑浠诲姟锛屽繀椤昏浼樺厛瀹夋帓鎵ц銆傚崟浣嶄负绉掋 + /// + public double MustArrangeThreshold = 300; + } + + /// + /// 缁ф壙鑷熀绫荤殑浠诲姟鐘舵佸睘鎬э紝鐢ㄤ簬璁板綍鎼繍浠诲姟鐨勬墽琛岀姸鎬佸苟鍦ㄧ晫闈㈡樉绀恒 + /// + public override MissionStatus status { get; set; } = new DeliveryMissionStatus(); + + /// + /// 鍏佽浠诲姟鎵撴柇姝e湪鍘婚伩璁╃偣鐨勫皬杞︼紝浣垮叾鎵挎帴璇ヤ换鍔° + /// + public bool EnableBlockStandby; + #endregion + + #region Delivery model + + /// + /// 鎼繍浠诲姟鐘舵佹灇涓 + /// + public enum DeliveryStatus + { + /// 绛夊緟鎵ц + Waiting = 0, + /// 鍙栬揣涓 + Fetching = 1, + /// 鏀捐揣涓 + Putting = 2, + /// 宸插畬鎴 + Finished = 3, + /// 宸插彇娑 + Canceled = 4, + /// 宸茬粓姝紙鎵ц杩囩▼涓涓柇锛 + Terminated = 5, + /// 閿欒 + Error = 6, + /// 鎸傝捣 + Suspended = 7, + } + + /// + /// 閲嶆瀯鐗 Delivery锛氬瓧娈典笌鍘 AbstractDelivery 淇濇寔鎺ヨ繎锛屼究浜庤縼绉伙紱 + /// 鐘舵佽鍐欑粺涓璧 syncStatus 閿侊紝閬垮厤绔炴併 + /// + public abstract class Delivery + { + /// 闆姳绠楁硶 ID 鐢熸垚鍣紙鎵鏈 Delivery 鍏变韩涓浠斤級 + private static readonly SnowflakeIdGenerator IdGenerator = + new SnowflakeIdGenerator(workerId: 1, datacenterId: 1); + + /// 浠诲姟鍞竴鏍囪瘑ID锛堥洩鑺辩畻娉曪紝Base62缂栫爜锛 + public string Id { get; set; } = IdGenerator.NextIdBase62(); + /// 澶栭儴绯荤粺浼犲叆鐨勪换鍔D + public string TaskId = string.Empty; + + /// 鐗╂枡闀垮害锛堟绫筹級锛岀敤浜庡寘缁滅害鏉 + public float MaterialLength = -1; + /// 鐗╂枡瀹藉害锛堟绫筹級锛岀敤浜庡寘缁滅害鏉 + public float MaterialWidth = -1; + + /// + /// 浠诲姟鍚姩鏉′欢鍑芥暟锛岃繑鍥瀟rue鏃朵换鍔℃墠鑳藉紑濮嬫墽琛 + /// 鍙敤浜庡疄鐜板鏉傜殑鍓嶇疆鏉′欢鍒ゆ柇 + /// + public Func StartCondition = null; + + // ========== 浠诲姟鍥炶皟鍑芥暟 ========== + /// 浠诲姟澶辫触鏃剁殑鍥炶皟鍑芥暟 + [JsonIgnore] public Action Failed; + /// 鏀捐揣瀹屾垚鏃剁殑鍥炶皟鍑芥暟 + [JsonIgnore] public Action DonePut; + /// 鍙栬揣瀹屾垚鏃剁殑鍥炶皟鍑芥暟 + [JsonIgnore] public Action DoneFetch; + /// 鏁翠釜浠诲姟瀹屾垚鏃剁殑鍥炶皟鍑芥暟 + [JsonIgnore] public Action DoneMission; + /// 浠诲姟寮濮嬫墽琛屾椂鐨勫洖璋冨嚱鏁 + [JsonIgnore] public Action OnStart; + /// 浠诲姟缁堟鏃剁殑鍥炶皟鍑芥暟 + [JsonIgnore] public Func> OnTerminated; + + // ========== 浠诲姟鍥炶皟閰嶇疆锛堝彲鎸佷箙鍖栵級 ========== + /// 鏄惁鍦ㄤ换鍔″紑濮嬫椂涓婃姤鍥炶皟 + public bool ReportOnStarted; + /// 鏄惁鍦ㄥ彇璐у畬鎴愭椂涓婃姤鍥炶皟 + public bool ReportOnFetched; + /// 鏄惁鍦ㄦ斁璐у畬鎴愭椂涓婃姤鍥炶皟 + public bool ReportOnPut; + /// 鏄惁鍦ㄤ换鍔℃渶缁堝畬鎴愭椂涓婃姤鍥炶皟 + public bool ReportOnFinished; + /// 鏄惁鍦ㄤ换鍔″け璐ユ椂涓婃姤鍥炶皟 + public bool ReportOnFailed; + /// 鏄惁鍦ㄤ换鍔¤缁堟/鏆傚仠鏃朵笂鎶ュ洖璋 + public bool ReportOnTerminated; + + /// OnStart 浜嬩欢缁戝畾鐨勫洖璋 key 鍒楄〃 + public List OnStartCallbackKeys { get; set; } = new(); + /// DoneFetch 浜嬩欢缁戝畾鐨勫洖璋 key 鍒楄〃 + public List DoneFetchCallbackKeys { get; set; } = new(); + /// DonePut 浜嬩欢缁戝畾鐨勫洖璋 key 鍒楄〃 + public List DonePutCallbackKeys { get; set; } = new(); + /// DoneMission 浜嬩欢缁戝畾鐨勫洖璋 key 鍒楄〃 + public List DoneMissionCallbackKeys { get; set; } = new(); + /// Failed 浜嬩欢缁戝畾鐨勫洖璋 key 鍒楄〃 + public List FailedCallbackKeys { get; set; } = new(); + /// OnTerminated 浜嬩欢缁戝畾鐨勫洖璋 key 鍒楄〃 + public List OnTerminatedCallbackKeys { get; set; } = new(); + + // ========== 浠诲姟璺緞淇℃伅 ========== + /// 鍙栬揣鐐圭珯鐐笽D + public int Src; + /// 鏀捐揣鐐圭珯鐐笽D + public int Dst; + /// 鏄惁璺宠繃鍙栬揣姝ラ锛堝鏋滃皬杞﹀凡鍦ㄥ彇璐х偣鎴栧凡杞借揣锛屽彲璁剧疆涓簍rue锛 + public bool SkipFetch; + /// 鏄惁璺宠繃鏀捐揣姝ラ锛堝鏋滃彧闇瑕佺Щ鍔ㄥ埌鐩爣鐐逛絾涓嶆斁璐э級 + public bool SkipPut; + /// 鏀捐揣鐐规槸鍚﹀厑璁告煡鎵惧洖璺矾寰 + public bool DstFindLoop; + /// 鍙栬揣鐐规槸鍚﹀厑璁告煡鎵惧洖璺矾寰 + public bool SrcFindLoop; + + /// 鍙栬揣璺緞瑙勫垝鍙傛暟瀛楀吀锛堜紶閫掔粰璺緞瑙勫垝鍣級 + [JsonIgnore] public Dictionary FetchPlanInfo = new() { { "action", "fetch" } }; + /// 鏀捐揣璺緞瑙勫垝鍙傛暟瀛楀吀锛堜紶閫掔粰璺緞瑙勫垝鍣級 + [JsonIgnore] public Dictionary PutPlanInfo = new() { { "action", "put" } }; + + /// 鍓嶅簭浠诲姟锛屽綋鍓嶄换鍔¢渶瑕佺瓑寰呭墠搴忎换鍔℃墽琛屽畬姣曟墠鑳藉紑濮嬶紙浠诲姟閾句緷璧栧叧绯伙級 + [JsonIgnore] public Delivery Former; + /// 鎵ц褰撳墠浠诲姟鐨勫皬杞﹀璞 + [JsonIgnore] public Car UsingCar; + /// 灏忚溅鍒嗙粍绛涢夋潯浠讹紙鐢ㄤ簬绛涢夌壒瀹氱粍鐨勫皬杞︼級 + [JsonIgnore] public string Group = string.Empty; + /// 灏忚溅绛涢夐敭鍒楄〃锛堢敤浜庡鏉′欢绛涢夛級 + public List SelectorKeys = new(); + /// 鍏佽鎵ц浠诲姟鐨勫皬杞︾被鍨嬶紙濡"Forklift"銆"Kiva"绛夛級 + [JsonIgnore] public string CarType = string.Empty; + /// + /// 浠诲姟浼樺厛绾э紝鏁板艰秺澶э紝浼樺厛绾ц秺楂 + /// 浠诲姟璋冨害鏃舵寜浼樺厛绾ч檷搴忔墽琛岋紝鍚屼紭鍏堢骇鎸夊垱寤烘椂闂存帓搴 + /// + public int Priority = 0; + + // ========== 鏃堕棿璁板綍 ========== + /// 浠诲姟鍒涘缓鏃堕棿 + public DateTime CreateTime = DateTime.Now; + /// 浠诲姟寮濮嬫墽琛屾椂闂 + public DateTime StartTime; + /// 浠诲姟瀹屾垚鏃堕棿 + public DateTime FinishTime; + + /// 浠诲姟鐘舵佸悓姝ラ攣瀵硅薄锛岀敤浜庡绾跨▼鐜涓嬩繚璇佺姸鎬佷慨鏀圭殑绾跨▼瀹夊叏 + [JsonIgnore] public readonly object SyncStatus = new object(); + + // ========== 浠诲姟鍐呴儴鐘舵佹爣蹇 ========== + /// 浠诲姟鏄惁姝e湪鎵ц涓 + internal bool Active; + /// 浠诲姟鏄惁澶勪簬鏀捐揣闃舵锛坱rue=鏀捐揣涓紝false=鍙栬揣涓級 + internal bool Putting; + /// 浠诲姟鏄惁宸插畬鎴 + internal bool Finished; + /// 浠诲姟鏄惁鍑洪敊 + internal bool Error; + /// 浠诲姟鏄惁宸插彇娑 + internal bool Canceled; + /// 浠诲姟鏄惁宸茬粓姝紙鎵ц杩囩▼涓涓柇锛 + internal bool Terminated; + /// 浠诲姟琚寕璧 + internal bool Suspended; + + /// 杩藉姞鐨勯冮稿姩浣滐紙浠诲姟瀹屾垚鍚庨渶瑕佹墽琛岀殑棰濆鍔ㄤ綔锛屽鍘婚伩璁╃偣锛 + [JsonIgnore] public Action AppendedEscape; + + /// 浠诲姟娣ょН娆℃暟锛堣繛缁墽琛屽け璐ョ殑娆℃暟锛岀敤浜庣洃鎺т换鍔¢樆濉炴儏鍐碉級 + public int StuckTime; + /// 浠诲姟娣ょН鍘熷洜璇存槑锛堣褰曚负浠涔堟棤娉曟墽琛岋級 + public string StuckReason = "/"; + + /// + /// 鑾峰彇浠诲姟褰撳墠鐘舵 + /// 鐘舵佷紭鍏堢骇锛欶inished > Canceled > Terminated > Error > Fetching/Putting > Waiting + /// + /// 浠诲姟褰撳墠鐘舵佹灇涓惧 + public DeliveryStatus GetStatus() + { + lock (SyncStatus) + { + if (Finished) return DeliveryStatus.Finished; + if (Canceled) return DeliveryStatus.Canceled; + if (Terminated) return DeliveryStatus.Terminated; + if (Suspended) return DeliveryStatus.Suspended; + if (Error) return DeliveryStatus.Error; + if (Active) return Putting ? DeliveryStatus.Putting : DeliveryStatus.Fetching; + return DeliveryStatus.Waiting; + } + } + + public bool IsActive() { lock (SyncStatus) return Active; } + public bool IsFinished() { lock (SyncStatus) return Finished; } + public bool IsError() { lock (SyncStatus) return Error; } + public bool IsCanceled() { lock (SyncStatus) return Canceled; } + public bool IsTerminated() { lock (SyncStatus) return Terminated; } + + /// + /// 鍙栨秷浠诲姟锛屽綋鍓嶅疄鐜颁粎鍗犱綅杩斿洖 true锛屽叿浣撳彇娑堥昏緫鍙敱瀛愮被閲嶅啓 + /// + public bool Cancel() + { + return true; + } + } + + /// + /// 鐢ㄤ簬鎸佷箙鍖栫殑浠诲姟鐘舵佸揩鐓 + /// 鍙寘鍚彲搴忓垪鍖栥佷笌涓氬姟鎭㈠鐩稿叧鐨勫叧閿俊鎭 + /// + protected class DeliveryStateSnapshot + { + /// 浠诲姟鍞竴鏍囪瘑ID + public string Id { get; set; } + /// 澶栭儴绯荤粺浼犲叆鐨勪换鍔D + public string TaskId { get; set; } + /// 鍙栬揣鐐圭珯鐐笽D + public int Src { get; set; } + /// 鏀捐揣鐐圭珯鐐笽D + public int Dst { get; set; } + /// 鏄惁璺宠繃鍙栬揣姝ラ + public bool SkipFetch { get; set; } + /// 鏄惁璺宠繃鏀捐揣姝ラ + public bool SkipPut { get; set; } + /// 褰撳墠鏄惁澶勪簬鏀捐揣闃舵锛坱rue=鏀捐揣涓級 + public bool Putting { get; set; } + /// 浠诲姟鐘舵佸瓧绗︿覆锛圵aiting/Fetching/Putting/Finished绛夛級 + public string Status { get; set; } + /// 浠诲姟鍒涘缓鏃堕棿 + public DateTime CreateTime { get; set; } + /// 浠诲姟寮濮嬫墽琛屾椂闂 + public DateTime StartTime { get; set; } + /// 浠诲姟瀹屾垚鏃堕棿 + public DateTime FinishTime { get; set; } + /// 鎵ц浠诲姟鐨勫皬杞D锛-1琛ㄧず鏈垎閰嶏級 + public int UsingCarId { get; set; } + /// 鍏佽鎵ц浠诲姟鐨勫皬杞︾被鍨 + public string CarType { get; set; } + /// 鏄惁鍦ㄤ换鍔″紑濮嬫椂涓婃姤鍥炶皟 + public bool ReportOnStarted { get; set; } + /// 鏄惁鍦ㄥ彇璐у畬鎴愭椂涓婃姤鍥炶皟 + public bool ReportOnFetched { get; set; } + /// 鏄惁鍦ㄦ斁璐у畬鎴愭椂涓婃姤鍥炶皟 + public bool ReportOnPut { get; set; } + /// 鏄惁鍦ㄤ换鍔℃渶缁堝畬鎴愭椂涓婃姤鍥炶皟 + public bool ReportOnFinished { get; set; } + /// 鏄惁鍦ㄤ换鍔″け璐ユ椂涓婃姤鍥炶皟 + public bool ReportOnFailed { get; set; } + /// 鏄惁鍦ㄤ换鍔¤缁堟/鏆傚仠鏃朵笂鎶ュ洖璋 + public bool ReportOnTerminated { get; set; } + /// OnStart 浜嬩欢缁戝畾鐨勫洖璋 key 鍒楄〃 + public List OnStartCallbackKeys { get; set; } = new(); + /// DoneFetch 浜嬩欢缁戝畾鐨勫洖璋 key 鍒楄〃 + public List DoneFetchCallbackKeys { get; set; } = new(); + /// DonePut 浜嬩欢缁戝畾鐨勫洖璋 key 鍒楄〃 + public List DonePutCallbackKeys { get; set; } = new(); + /// DoneMission 浜嬩欢缁戝畾鐨勫洖璋 key 鍒楄〃 + public List DoneMissionCallbackKeys { get; set; } = new(); + /// Failed 浜嬩欢缁戝畾鐨勫洖璋 key 鍒楄〃 + public List FailedCallbackKeys { get; set; } = new(); + /// OnTerminated 浜嬩欢缁戝畾鐨勫洖璋 key 鍒楄〃 + public List OnTerminatedCallbackKeys { get; set; } = new(); + } + + #endregion + + #region Extensibility hooks + + /// + /// 灏忚溅绂诲紑鍏呯數绔欐椂鐨勬墿灞曞姩浣滈挬瀛愶紝瀛愮被鍙噸鍐欎互鎵ц鑷畾涔夐昏緫锛堝閫氱煡銆佺粺璁$瓑锛 + /// + public virtual void LeaveChargeAction(Car car, Site site) { } + /// + /// 鍔ㄦ佽皟鏁翠换鍔′紭鍏堢骇鐨勯挬瀛愶紝鍦ㄨ皟搴﹀惊鐜腑姣忔杩唬鍓嶈皟鐢紝瀛愮被鍙噸鍐欏疄鐜拌嚜瀹氫箟浼樺厛绾х瓥鐣 + /// + public virtual void ChangePriority() { } + /// + /// 瀹氫箟閬胯鐐圭被鍨嬪瓧绗︿覆锛岀敤浜庤矾寰勮鍒掓椂璇嗗埆鍙伩璁╃殑绔欑偣锛屽瓙绫诲彲閲嶅啓浠ュ尯鍒嗕笉鍚岄伩璁╃被鍨 + /// + public virtual string DefineGiveWayType(AbstractCar car) => "giveWay"; + + #endregion + + #region Queue / persistence (thread-safe) + + /// 闃熷垪涓庢寔涔呭寲鎿嶄綔鐨勫悓姝ラ攣锛屼繚璇佸绾跨▼璁块棶 _allDeliveries 鏃剁殑绾跨▼瀹夊叏 + [JsonIgnore] private readonly object _sync = new(); + /// 鎵鏈夋惉杩愪换鍔$殑闆嗗悎锛堝惈绛夊緟涓笌鎵ц涓殑浠诲姟锛 + [JsonIgnore] private readonly HashSet _allDeliveries = new(); + + /// + /// 灏嗕换鍔″姞鍏ラ槦鍒楀苟鎸佷箙鍖栧埌鏂囦欢锛岀嚎绋嬪畨鍏 + /// + /// 寰呭叆闃熺殑鎼繍浠诲姟 + public void Enqueue(Delivery d) + { + lock (_sync) _allDeliveries.Add(d); + PersistSingleDelivery(d); + } + + /// + /// 鑾峰彇褰撳墠浠诲姟鍒楄〃锛屾敮鎸佹寜鐘舵佽繃婊わ紙宸插畬鎴/宸茬粓姝/閿欒/宸插彇娑堬級锛岀嚎绋嬪畨鍏 + /// + /// 鏄惁鍖呭惈宸插畬鎴愮殑浠诲姟 + /// 鏄惁鍖呭惈宸茬粓姝㈢殑浠诲姟 + /// 鏄惁鍖呭惈閿欒鐘舵佺殑浠诲姟 + /// 鏄惁鍖呭惈宸插彇娑堢殑浠诲姟 + /// 鍘婚噸鍚庢寜鐘舵佹帓搴忕殑浠诲姟鍒楄〃 + public List GetDeliveries(bool includeFinished = false, bool includeAborted = false,bool includeError = false,bool includeCanceled=false) + { + List ds; + lock (_sync) ds = _allDeliveries.ToList(); + + if (!includeFinished) + ds = ds.Where(p => p.GetStatus() != DeliveryStatus.Finished).ToList(); + if (!includeAborted) + ds = ds.Where(p => p.GetStatus() != DeliveryStatus.Terminated).ToList(); + if (!includeError) + ds = ds.Where(p => p.GetStatus() != DeliveryStatus.Error).ToList(); + if (!includeCanceled) + ds = ds.Where(p => p.GetStatus() != DeliveryStatus.Canceled).ToList(); + + return System.Linq.Enumerable.DistinctBy(ds, e => e.Id).OrderBy(e => e.GetStatus()).ToList(); + } + + /// + /// 鑾峰彇鎸佷箙鍖栨枃浠惰矾寰勶紝榛樿瀛樺偍鍦 BaseDirectory/Deliveries/ 涓嬶紝瀛愮被鍙噸鍐欒嚜瀹氫箟璺緞 + /// + protected virtual string GetDeliveryPersistFilePath() + { + var baseDir = AppDomain.CurrentDomain.BaseDirectory; + var persistDir = Path.Combine(baseDir, "Deliveries"); + Directory.CreateDirectory(persistDir); + + var fileName = $"{GetType().Name}_Deliveries.json"; + return Path.Combine(persistDir, fileName); + } + + /// + /// 浠庡崟涓 Delivery 鐢熸垚鎸佷箙鍖栧揩鐓э紙浠呭綋鐘舵佷负鏈粨鏉熸椂杩斿洖闈 null锛夈 + /// 鍦ㄥ洖璋冧腑鐢ㄤ簬鍗曚换鍔℃寔涔呭寲锛屼笉淇敼鍏朵粬浠诲姟璁板綍銆 + /// + private static DeliveryStateSnapshot SnapshotFromDelivery(Delivery d) + { + lock (d.SyncStatus) + { + var curStatus = d.GetStatus(); + if (curStatus is DeliveryStatus.Finished or DeliveryStatus.Canceled or DeliveryStatus.Error or DeliveryStatus.Terminated) + return null; + return new DeliveryStateSnapshot + { + Id = d.Id, + TaskId = d.TaskId, + Src = d.Src, + Dst = d.Dst, + SkipFetch = d.SkipFetch, + SkipPut = d.SkipPut, + Putting = d.Putting, + Status = curStatus.ToString(), + CreateTime = d.CreateTime, + StartTime = d.StartTime, + FinishTime = d.FinishTime, + UsingCarId = d.UsingCar?.id ?? -1, + CarType = d.CarType ?? string.Empty, + ReportOnStarted = d.ReportOnStarted, + ReportOnFetched = d.ReportOnFetched, + ReportOnPut = d.ReportOnPut, + ReportOnFinished = d.ReportOnFinished, + ReportOnFailed = d.ReportOnFailed, + ReportOnTerminated = d.ReportOnTerminated, + OnStartCallbackKeys = d.OnStartCallbackKeys?.ToList() ?? new List(), + DoneFetchCallbackKeys = d.DoneFetchCallbackKeys?.ToList() ?? new List(), + DonePutCallbackKeys = d.DonePutCallbackKeys?.ToList() ?? new List(), + DoneMissionCallbackKeys = d.DoneMissionCallbackKeys?.ToList() ?? new List(), + FailedCallbackKeys = d.FailedCallbackKeys?.ToList() ?? new List(), + OnTerminatedCallbackKeys = d.OnTerminatedCallbackKeys?.ToList() ?? new List() + }; + } + } + + /// + /// 浠呮寔涔呭寲褰撳墠浠诲姟锛屼笉鏇存敼鍏朵粬浠诲姟璁板綍锛涢珮骞跺彂涓嬬敱 CommonTools 鍘熷瓙鏇存柊淇濊瘉鏁版嵁瀹夊叏銆 + /// + private void PersistSingleDelivery(Delivery d) + { + try + { + var snap = SnapshotFromDelivery(d); + if (snap == null) + { + RemoveSingleDeliveryFromPersist(d.Id); + return; + } + var path = GetDeliveryPersistFilePath(); + AtomicFileUpdateHelper.ExecuteAtomicUpdate(path, current => + { + var list = string.IsNullOrEmpty(current) + ? new List() + : JsonConvert.DeserializeObject>(current) ?? new List(); + var idx = list.FindIndex(x => x.Id == d.Id); + if (idx >= 0) + list[idx] = snap; + else + list.Add(snap); + return JsonConvert.SerializeObject(list, Formatting.Indented); + }); + } + catch (Exception ex) + { + Diagnosis.Log($"PersistSingleDelivery error: {ExceptionFormatter.FormatEx(ex)}", "ChainedDeliveryPersist", true); + } + } + + /// + /// 瀵瑰鍏紑鐨勫崟浠诲姟鎸佷箙鍖栨柟娉曪紝渚涚晫闈㈡垨澶栭儴閫昏緫鍦ㄤ慨鏀 Delivery 鐘舵佸悗璋冪敤銆 + /// + /// 闇瑕佹寔涔呭寲鐨勬惉杩愪换鍔 + public void PersistDelivery(Delivery d) + { + if (d == null) return; + PersistSingleDelivery(d); + } + + /// + /// 浠庢寔涔呭寲鏂囦欢涓Щ闄ゆ寚瀹氫换鍔¤褰曪紝涓嶄慨鏀瑰叾浠栦换鍔¤褰曘 + /// + private void RemoveSingleDeliveryFromPersist(string deliveryId) + { + try + { + var path = GetDeliveryPersistFilePath(); + AtomicFileUpdateHelper.ExecuteAtomicUpdate(path, current => + { + if (string.IsNullOrEmpty(current)) return null; + var list = JsonConvert.DeserializeObject>(current) ?? new List(); + list.RemoveAll(x => x.Id == deliveryId); + return JsonConvert.SerializeObject(list, Formatting.Indented); + }); + } + catch (Exception ex) + { + Diagnosis.Log($"RemoveSingleDeliveryFromPersist error: {ExceptionFormatter.FormatEx(ex)}", "ChainedDeliveryPersist", true); + } + } + + /// + /// 宸ュ巶鏂规硶锛氬綋鍐呭瓨涓皻涓嶅瓨鍦ㄦ煇涓揩鐓у搴旂殑 Delivery 鏃讹紝 + /// 鍏佽瀛愮被鏍规嵁蹇収淇℃伅鍒涘缓涓涓柊鐨 Delivery 瀹炰緥骞跺姞鍏ュ埌 _allDeliveries銆 + /// 榛樿杩斿洖 null锛岃〃绀哄熀绫讳笉涓诲姩鍒涘缓锛屽叿浣撲笟鍔$敱瀛愮被鍐冲畾銆 + /// + /// 鎸佷箙鍖栧揩鐓 + /// 鏂板缓鐨 Delivery锛涜繑鍥 null 鍒欒烦杩囪蹇収 + protected virtual Delivery CreateDeliveryFromSnapshot(DeliveryStateSnapshot snap) + { + // 鍩虹被涓嶇煡閬撳叿浣撲笟鍔$被鍨嬶紝浜ょ敱瀛愮被閲嶅啓銆 + return null; + } + + /// + /// 妫鏌ュ皬杞︽槸鍚﹀凡杞借揣锛屽瓙绫诲彲閲嶅啓浠ラ傞厤涓嶅悓杞﹀瀷鐨勮浇璐у垽鏂昏緫 + /// + /// 寰呮鏌ョ殑灏忚溅 + /// true 琛ㄧず宸茶浇璐 + protected virtual bool CheckCarLoaded(AbstractCar car) + { + return false; + } + + /// + /// 浠庢寔涔呭寲鏂囦欢鎭㈠鏈畬鎴愮殑浠诲姟鍒板唴瀛橀槦鍒楋紝鍦ㄨ繘绋嬪惎鍔ㄦ椂璋冪敤 + /// + private void RecoverDeliveriesFromPersistedFile() + { + try + { + var path = GetDeliveryPersistFilePath(); + if (!File.Exists(path)) return; + + var json = File.ReadAllText(path); + if (string.IsNullOrWhiteSpace(json)) return; + + var snapshots = JsonConvert.DeserializeObject>(json) ?? new List(); + if (!snapshots.Any()) return; + + List ds; + lock (_sync) ds = _allDeliveries.ToList(); + + foreach (var snap in snapshots) + { + // 1. 鍏堝皾璇曞湪鐜版湁鍐呭瓨浠诲姟涓尮閰嶏紙浼樺厛 TaskId锛屽叾娆″唴閮 Id锛 + var d = ds.FirstOrDefault(x => + (!string.IsNullOrEmpty(snap.TaskId) && x.TaskId == snap.TaskId) || x.Id == snap.Id); + + // 2. 濡傛灉鍐呭瓨涓病鏈夊搴斾换鍔★紝灏濊瘯閫氳繃宸ュ巶鏂规硶鍒涘缓涓涓柊鐨 Delivery + if (d == null) + { + var created = CreateDeliveryFromSnapshot(snap); + if (created != null) + { + lock (_sync) + { + _allDeliveries.Add(created); + ds.Add(created); // 浠ヤ究鍚庣画蹇収浠嶇劧鍙互鍦ㄦ湰娆″惊鐜腑鍖归厤鍒 + } + d = created; + } + else + { + // 瀛愮被鏈彁渚涘垱寤洪昏緫锛屽垯璺宠繃璇ュ揩鐓 + continue; + } + } + + lock (d.SyncStatus) + { + d.Src = snap.Src; + d.Dst = snap.Dst; + d.SkipFetch = snap.SkipFetch; + d.SkipPut = snap.SkipPut; + d.Putting = snap.Putting; + d.CreateTime = snap.CreateTime; + d.StartTime = snap.StartTime; + d.FinishTime = snap.FinishTime; + d.ReportOnStarted = snap.ReportOnStarted; + d.ReportOnFetched = snap.ReportOnFetched; + d.ReportOnPut = snap.ReportOnPut; + d.ReportOnFinished = snap.ReportOnFinished; + d.ReportOnFailed = snap.ReportOnFailed; + d.ReportOnTerminated = snap.ReportOnTerminated; + d.OnStartCallbackKeys = snap.OnStartCallbackKeys ?? new List(); + d.DoneFetchCallbackKeys = snap.DoneFetchCallbackKeys ?? new List(); + d.DonePutCallbackKeys = snap.DonePutCallbackKeys ?? new List(); + d.DoneMissionCallbackKeys = snap.DoneMissionCallbackKeys ?? new List(); + d.FailedCallbackKeys = snap.FailedCallbackKeys ?? new List(); + d.OnTerminatedCallbackKeys = snap.OnTerminatedCallbackKeys ?? new List(); + + d.Active = false; + d.Finished = false; + d.Error = false; + d.Canceled = false; + d.Terminated = false; + + if (Enum.TryParse(snap.Status, out var s)) + { + switch (s) + { + case DeliveryStatus.Fetching: + d.Putting = false; + break; + case DeliveryStatus.Putting: + d.Putting = true; + d.SkipFetch = true; + break; + } + } + + if (snap.UsingCarId >= 0) + d.UsingCar = SimpleLib.GetCar(snap.UsingCarId) as Car; + } + } + } + catch (Exception ex) + { + Diagnosis.Log($"RecoverDeliveriesFromPersistedFile error: {ExceptionFormatter.FormatEx(ex)}", "ChainedDeliveryPersist", true); + } + } + + #endregion + + #region Lifecycle (background loop with cancellation) + + /// 鐢ㄤ簬鍙栨秷鍚庡彴璋冨害寰幆鐨勫彇娑堜护鐗屾簮 + [JsonIgnore] private CancellationTokenSource _cts; + /// 鍚庡彴璋冨害寰幆浠诲姟 + [JsonIgnore] private Task _loopTask; + /// 杩涚▼鏄惁宸插惎鍔 + [JsonIgnore] private volatile bool _started; + /// 鐢ㄤ簬鍦ㄧ晫闈㈢粯鍒惰皟搴︾姸鎬佺殑鐢诲埛 + [JsonIgnore] private readonly SimpleMonitor.Painter _painter = SimpleMonitor.getPainter("cdmPainter_refactored"); + + /// + /// 鍚姩鎼繍浠诲姟璋冨害杩涚▼锛氭仮澶嶆寔涔呭寲浠诲姟銆佸惎鍔ㄥ悗鍙板惊鐜 + /// + [MethodMember(Name = "鍚姩杩涚▼", Description = "寮濮嬪鐞嗘惉杩愰槦鍒")] + public override void Execute() + { + if (_started) return; + _started = true; + status.status = "鍚姩涓"; + + RecoverDeliveriesFromPersistedFile(); + + _cts = new CancellationTokenSource(); + _loopTask = Task.Run(() => LoopAsync(_cts.Token), _cts.Token); + status.status = "宸插惎鍔"; + } + + /// + /// 鍏抽棴璋冨害杩涚▼锛屽彇娑堝悗鍙板惊鐜 + /// + [MethodMember(Name = "鍏抽棴杩涚▼", Description = "鍏抽棴浠诲姟缁存姢杩涚▼")] + public void Stop() + { + status.status = "鍏抽棴涓"; + _started = false; + try + { + _cts?.Cancel(); + } + catch { /* ignore */ } + status.status = "宸插叧闂"; + } + + /// + /// 鍚庡彴璋冨害寰幆锛氬懆鏈熸у湴妫鏌ョ瓑寰呬腑鐨勪换鍔★紝灏濊瘯涓哄叾鍒嗛厤灏忚溅骞舵墽琛 + /// 姣忔杩唬锛氳鍙栭厤缃佽皟鏁翠紭鍏堢骇銆佸绛夊緟浠诲姟鎺掑簭銆侀愪釜灏濊瘯鎵ц骞舵洿鏂版筏绉粺璁 + /// + /// 鍙栨秷浠ょ墝锛岀敤浜庡搷搴 Stop 鍏抽棴璇锋眰 + private async Task LoopAsync(CancellationToken token) + { + var stat = (DeliveryMissionStatus)status; + var iteration = 0; + + while (!token.IsCancellationRequested) + { + var checkingTime = new Dictionary(); + try + { + // 璇诲彇浠诲姟鍙傛暟 + var missionField = StringDictConvert.Convert(fields) ?? new ChainedDeliveryParams(); + var mustArrangeThreshold = ReadMustArrangeThreshold(); + stat.MustArrangeThreshold = mustArrangeThreshold; + EnableBlockStandby = missionField.EnableBlockStandby; + var arrangeThresholdTime = TimeSpan.FromSeconds(mustArrangeThreshold); + + ChangePriority(); + + // 鑾峰彇鎵鏈夌瓑寰呬腑鐨勪换鍔★紝鎸変紭鍏堢骇鍜岃秴鏃舵椂闂存帓搴忥紙瓒呮椂蹇呴』浼樺厛瀹夋帓锛 + Delivery[] currentChecking; + lock (_sync) + { + var pendingDs = _allDeliveries.Where(p => p.GetStatus() == DeliveryStatus.Waiting).ToList(); + currentChecking = pendingDs.ToArray(); + currentChecking = currentChecking.OrderByDescending(p => + p.Priority + ((DateTime.Now - p.CreateTime) > arrangeThresholdTime ? 1 : 0)).ToArray(); + } + + stat.Enqueued = currentChecking.Length; + + var stuckStr = ""; + var stuckedNum = 0; + + foreach (var d in currentChecking) + { + + checkingTime[d.Id] = "澶勭悊涓"; + UpdateDisplay(iteration, checkingTime); + var startAt = DateTime.Now; + + if (!TryExecuteSingle(d, missionField, out var reason)) + { + d.StuckTime += 1; + d.StuckReason = reason ?? d.StuckReason; + stuckedNum += 1; + stuckStr += $"{d.Id}:{d.StuckTime} "; + } + + checkingTime[d.Id] = $"{(DateTime.Now - startAt).TotalSeconds:0.0}s"; + UpdateDisplay(iteration, checkingTime); + } + + stat.StuckNum = stuckedNum; + stat.StuckInfo = stuckStr; + + status.status = $"宸插惎鍔-寰幆{iteration++}"; + } + catch (Exception ex) + { + Diagnosis.Log($"CDM(refactored) error: {ExceptionFormatter.FormatEx(ex)}", "error", true); + } + + UpdateDisplay(iteration, checkingTime); + try { await Task.Delay(1000, token); } catch { /* cancelled */ } // 姣 1 绉掑惊鐜竴娆 + } + } + + /// + /// 浠庨厤缃腑璇诲彇鈥滃繀椤诲畨鎺掆濋槇鍊硷紙绉掞級锛岃秴杩囪绛夊緟鏃堕棿鐨勪换鍔″皢琚紭鍏堣皟搴 + /// + private double ReadMustArrangeThreshold() + { + if (!fields.TryGetValue("mustArrangeThreshold", out var str) || !double.TryParse(str, out var v)) + return 300; + return v; + } + + /// + /// 鏇存柊鐣岄潰涓婄殑璋冨害鐘舵佹樉绀猴細寰幆娆℃暟鍙婂悇浠诲姟澶勭悊鑰楁椂 + /// + private void UpdateDisplay(int iteration, Dictionary checkingTime) + { + try + { + _painter.clear(); + _painter.drawTextFixed( + $"CDM寰幆鏁帮細{iteration}\n{string.Join("\n", checkingTime.Select(kvp => $"{kvp.Key}\t{kvp.Value}"))}", + new SolidBrush(Color.Black), + VirtualPainter.DrawPosition.RightBottom, + Color.AliceBlue); + } + catch + { + // UI缁樺埗澶辫触涓嶅奖鍝嶈皟搴 + } + } + + #endregion + + #region Core execution pipeline (refactored) + + /// + /// 灏濊瘯鎵ц鍗曚釜鎼繍浠诲姟锛氭牎楠屽墠缃潯浠躲侀夎溅瑙勫垝鍙栬揣銆佽鍒掓斁璐с佺紪璇戜笅鍙戙佸惎鍔ㄥ紓姝ユ墽琛 + /// + /// 寰呮墽琛岀殑鎼繍浠诲姟 + /// 浠诲姟鍙傛暟閰嶇疆 + /// 鑻ュけ璐ワ紝杩斿洖娣ょН鍘熷洜 + /// true 琛ㄧず浠诲姟宸叉垚鍔熷惎鍔ㄦ墽琛 + private bool TryExecuteSingle(Delivery d, ChainedDeliveryParams missionField, out string stuckReason) + { + stuckReason = null; + missionField ??= new ChainedDeliveryParams(); + + // 宸插湪鎵ц涓 + if (d.IsActive()) return true; + + // 渚濊禆鍏崇郴 + if (d.Former != null && !d.Former.IsActive()) + { + stuckReason = $"depends on {d.Former.Id} not started"; + return false; + } + + if (d.Former != null && !d.Former.IsFinished() && !missionField.TightDeliveryChain) + { + stuckReason = $"depends on {d.Former.Id} not finished"; + return false; + } + + if (d.StartCondition != null && !d.StartCondition()) + { + stuckReason = $"{d.Id} does not meet startCondition"; + return false; + } + + // 寤剁画浠诲姟锛氬凡鏈 fetchPlan/fetchCode,寤剁画浠诲姟鍙栨秷 + //if (d.FetchPlan != null) + // return TryHandleContinuationFetch(d, out stuckReason); + + // 闈炲欢缁細閫夎溅骞惰鍒掑彇璐 + SegmentPlan fetchPlan = null; + Car usingCar; + if (!d.SkipFetch) + { + if (!TrySelectCarAndPlanFetch(d, out usingCar, out fetchPlan, out stuckReason)) + return false; + } + else + { + usingCar = d.UsingCar; + //fetchPlan = d.FetchPlan; + } + + if (usingCar == null) + { + stuckReason = d.SkipFetch + ? $"{d.Id} skip fetch, but no using car assigned for put" + : $"ucar null, {d.Src}->{d.Dst}"; + return false; + } + + // 瑙勫垝鏀捐揣 + if (!TryPlanPut(d, usingCar, fetchPlan != null, out var putPlan, out var escPlan, out stuckReason)) + return false; + + // 缂栬瘧涓庝笅鍙戯紙鍚爣绛惧崰鐢級 + if (!TryCompileAndDispatch(d, usingCar, fetchPlan, putPlan, escPlan, out var allCode, out stuckReason)) + return false; + + // 鍚姩 Runner 寮傛杩愯骞跺洖鏀剁姸鎬 + _ = RunDeliveryAsync(d, usingCar, fetchPlan, escPlan, allCode); + + return true; + } + + #region 寤剁画浠诲姟锛屽姛鑳藉睆钄 + //private bool TryHandleContinuationFetch(Delivery d, out string stuckReason) + //{ + // stuckReason = null; + // var usingCar = (Car)d.FetchPlan.UsingCar; + + // // redirect_fetched: 0 鏈紑濮嬶紱1 鍙栬揣涓紱3 redirect锛333 鍙栬揣瀹屾垚绛夊緟鏀捐揣锛666 鍘婚伩璁 + // if (d.RedirectFetched == 0) + // { + // lock (Commons.PlanSession) + // { + // if (usingCar.tags.Contains("occupied")) + // { + // stuckReason = $"preassigned using car {usingCar.id} is still occupied"; + // return false; + // } + + // d.RedirectFetched = 1; + // usingCar.tags.Add("occupied", $"DeliverFetch{d.Id}"); + // usingCar.tags.Remove("blocking"); + // usingCar.tags.Remove("idle"); + // usingCar.tags.Remove("charging"); + // } + + // try + // { + // Commons.DeleteTag(tags, "FailCDMToStandby"); + // Commons.DeleteTag(tags, "FailCDMToStandbyTime"); + // var task = d.FetchCode.Queue(); + + // async Task go() + // { + // try + // { + // await task; + // d.doneFetch?.Invoke(d); + // lock (d.SyncStatus) d.Putting = true; + // } + // catch (Exception ex) + // { + // Diagnosis.Log($"Send Prefetch Script to {usingCar.id} exception:{ExceptionFormatter.FormatEx(ex)}", "error", true); + // lock (d.SyncStatus) { d.Error = true; d.Active = false; } + // lock (Commons.PlanSession) + // { + // usingCar.tags.Remove("redirect"); + // usingCar.tags.Remove("occupied"); + // usingCar.tags.Remove("dest"); + // usingCar.tags.Add("idle", DateTime.Now.ToString()); + // } + // d.failed?.Invoke(d); + // return; + // } + + // usingCar.siteID = d.Src; + // lock (Commons.PlanSession) + // { + // usingCar.tags.Remove("occupied"); + // usingCar.tags.Remove("redirect"); + // usingCar.tags.Add("pendingput", d.Id.ToString()); + // } + // d.RedirectFetched = 333; + // } + + // _ = go(); + // } + // catch (Exception ex) + // { + // Diagnosis.Log($"pre-fetching of {d.Id} by {usingCar.id} failed: {ExceptionFormatter.FormatEx(ex)}", "error", true); + // lock (d.SyncStatus) { d.Error = true; d.Active = false; } + // lock (Commons.PlanSession) + // { + // usingCar.tags.Remove("occupied"); + // usingCar.tags.Remove("redirect"); + // usingCar.tags.Remove("dest"); + // usingCar.tags.Add("idle", DateTime.Now.ToString()); + // } + // d.failed?.Invoke(d); + // stuckReason = "prefetch failed"; + // return false; + // } + + // stuckReason = "prefetching"; + // return false; + // } + + // if (d.RedirectFetched == 1 || d.RedirectFetched == 3) + // { + // stuckReason = "redirect fetching or redirect giveWay"; + // return false; + // } + + // // 333/666锛氱瓑寰呮斁璐ч樁娈碉紝鐢变富娴佺▼缁х画澶勭悊锛坒etchPlan != null 鍒嗘敮浼氳繘鍏 TryPlanPut锛 + // stuckReason = "waiting for put after prefetch"; + // return false; + //} + #endregion + + /// + /// 浠庡彲鐢ㄥ皬杞︿腑閫夎溅骞惰鍒掑彇璐ц矾寰勶細绛涢夊彲鐢ㄨ溅銆佽绠楀埌鍙栬揣鐐圭殑璺緞鏉冮噸銆侀夋嫨鏈浼樿溅涓庡彇璐ф + /// + /// 寰呮墽琛岀殑鎼繍浠诲姟 + /// 杈撳嚭锛氶変腑鐨勫皬杞 + /// 杈撳嚭锛氬彇璐ц矾寰勮鍒 + /// 杈撳嚭锛氳嫢澶辫触锛屾筏绉師鍥 + /// true 琛ㄧず鎴愬姛閫夎溅骞跺畬鎴愬彇璐ц矾寰勮鍒 + private bool TrySelectCarAndPlanFetch(Delivery d, out Car usingCar, out SegmentPlan fetchPlan, out string stuckReason) + { + usingCar = null; + fetchPlan = null; + stuckReason = null; + + // 缁熻鍙敤杞︼細鎺掗櫎涓嶅彲璋冨害銆佺被鍨嬩笉鍖归厤銆佽 hold 鍒板叾浠栫珯鐐圭殑杞 + var okCars = new List(); + if (d.UsingCar == null) + { + foreach (var car in SimpleLib.GetAllCars().OfType()) + { + if (Commons.SelectCar(car, EnableBlockStandby) < 0) continue; + if (d.UsingCar != null && d.UsingCar != car) continue; + if (!MeetSelectorKey(car, d.CarType)) continue; + if (car.tags.Contains("taskIdsString") && car.tags.TryGetValue("taskIdsString", out var ids) && !ids.Contains(d.TaskId)) continue; + if (car.tags.TryGetValue("holdCar", out var holdSite) && holdSite != d.Src.ToString()) continue; + okCars.Add(car); + } + } + else + { + usingCar = d.UsingCar; + if (Commons.SelectCar(usingCar, EnableBlockStandby) < 0) + { + stuckReason = $"preassigned using car {usingCar.id} is not available"; + return false; + } + fetchPlan = new SegmentPlan { usingCar = usingCar }; + if(!d.SkipPut) fetchPlan.fields["allow_destination_on_route"] = "true"; + fetchPlan.fields["CarLength"] = d.MaterialLength.ToString(CultureInfo.InvariantCulture); + fetchPlan.fields["CarWidth"] = d.MaterialWidth.ToString(CultureInfo.InvariantCulture); + fetchPlan.fields["action"] = "fetch"; + foreach (var kv in d.FetchPlanInfo) fetchPlan.fields[kv.Key] = kv.Value; + fetchPlan.findLoop = d.SrcFindLoop; + var carSiteId = usingCar.GetLastSite(); + fetchPlan.FindRoute(SimpleLib.GetSite(carSiteId), SimpleLib.GetSite(d.Src)); + return true; + } + + if (okCars.Count == 0 && d.UsingCar == null) + { + stuckReason = $"no available car, {d.Src}->{d.Dst}"; + return false; + } + + + // 閬嶅巻鍙敤杞︼紝璁$畻姣忚締杞﹀埌鍙栬揣鐐圭殑璺緞鏉冮噸锛岄夋嫨璺緞鏈鐭殑杞 + var currentWeight = float.MaxValue; + foreach (var car in okCars) + { + var mPlan = new SegmentPlan { usingCar = car }; + if (!d.SkipPut) mPlan.fields["allow_destination_on_route"] = "true"; + mPlan.fields["CarLength"] = d.MaterialLength.ToString(CultureInfo.InvariantCulture); + mPlan.fields["CarWidth"] = d.MaterialWidth.ToString(CultureInfo.InvariantCulture); + mPlan.fields["action"] = "fetch"; + foreach (var kv in d.FetchPlanInfo) mPlan.fields[kv.Key] = kv.Value; + + try + { + var fs = car.GetLastSite(); + if (fs == -1) continue; + mPlan.findLoop = d.SrcFindLoop; + var w = mPlan.FindRoute(SimpleLib.GetSite(fs), SimpleLib.GetSite(d.Src)); + if (w < currentWeight) + { + currentWeight = w; + usingCar = car; + fetchPlan = mPlan; + } + } + catch (BasicHeuristics.PlanningConflictException sde) + { + Diagnosis.Post($"D{d.Id} {car.name}({car.id}) conflict: {ExceptionFormatter.FormatEx(sde)}"); + } + catch (Exception e) + { + Diagnosis.Post($"D{d.Id} {car.name}({car.id}) exception: {ExceptionFormatter.FormatEx(e)}"); + } + } + + if (usingCar == null) + { + stuckReason = $"no car route to fetch src {d.Src}"; + return false; + } + + return true; + } + + /// + /// 瑙勫垝鏀捐揣璺緞锛氫粠鍙栬揣鐐/褰撳墠杞借揣鐐瑰埌鏀捐揣鐐圭殑璺緞锛屾敮鎸 DestinationOnRouteException 閲嶈瘯 + /// + /// 鎼繍浠诲姟 + /// 鎵ц浠诲姟鐨勫皬杞 + /// 鏄惁宸插畬鎴愬彇璐э紙true 琛ㄧず鍙栬揣娈靛凡瑙勫垝鎴栧凡鎵ц锛 + /// 杈撳嚭锛氭斁璐ц矾寰勮鍒 + /// 杈撳嚭锛氶冮歌矾寰勶紙褰撳墠瀹炵幇涓负 null锛 + /// 杈撳嚭锛氳嫢澶辫触锛屾筏绉師鍥 + /// true 琛ㄧず鎴愬姛瑙勫垝鏀捐揣璺緞 + private bool TryPlanPut(Delivery d, Car usingCar, bool prefetch, out SegmentPlan putPlan, out SegmentPlan escPlan, out string stuckReason) + { + putPlan = null; + escPlan = null; + stuckReason = null; + + putPlan = new SegmentPlan { usingCar = usingCar }; + putPlan.fields["action"] = "put"; + foreach (var kv in d.PutPlanInfo) putPlan.fields[kv.Key] = kv.Value; + putPlan.fields["useMustCanGo"] = prefetch ? "true" : "false"; + putPlan.findLoop = !prefetch || d.DstFindLoop; + + // 纭畾鏀捐揣璺緞璧风偣锛歋kipFetch 鏃朵粠杞借揣绔欑偣鍙栵紝鍚﹀垯浠庡彇璐х偣 d.Src + var srcSiteId = d.Src; + if (d.SkipFetch) + { + srcSiteId = usingCar.status.holdingLocks.FirstOrDefault(); + if (srcSiteId == -1) + { + stuckReason = "selected using car is not reset"; + return false; + } + } + + if (d.SkipPut) + return true; + + // 瑙勫垝鏀捐揣璺緞锛屾渶澶氶噸璇 2 娆′互澶勭悊 DestinationOnRouteException + for (var attempt = 0; attempt < 2; attempt++) + { + try + { + putPlan.fields["allow_destination_on_route"] = "true"; + putPlan.FindRoute(SimpleLib.GetSite(srcSiteId), SimpleLib.GetSite(d.Dst)); + return true; + } + catch (BasicHeuristics.DestinationOnRouteException) + { + //allowDestinationOnRoute = true; + } + catch (BasicHeuristics.PlanningConflictException dce) + { + // 杩欓噷淇濇寔涓庡師閫昏緫涓鑷达細閬囧埌缁堢偣鍐茬獊鍏堣繑鍥 false 璁╀笅涓杞啀璇曪紙鎺ㄦ尋/閬胯鍙湪瀛愮被鎵╁睍锛 + stuckReason = $"dest {d.Dst} conflict with other cars"; + Diagnosis.Post($"D{d.Id} dest {d.Dst} conflict: {ExceptionFormatter.FormatEx(dce)}"); + return false; + } + catch (Exception ex) + { + stuckReason = $"no route {srcSiteId}->{d.Dst}: {ex.Message}"; + return false; + } + } + + stuckReason = $"destination {d.Dst} on route, retry failed"; + return false; + } + + /// + /// 缂栬瘧鍙栬揣/鏀捐揣/閫冮告涓哄皬杞︾▼搴忋佽缃皬杞︽爣绛撅紙occupied/deliver/dest锛夈佷笅鍙戞墽琛 + /// + /// 鎼繍浠诲姟 + /// 鎵ц浠诲姟鐨勫皬杞 + /// 鍙栬揣璺緞瑙勫垝锛堝彲涓 null锛 + /// 鏀捐揣璺緞瑙勫垝 + /// 閫冮歌矾寰勮鍒掞紙鍙负 null锛 + /// 杈撳嚭锛氱紪璇戝悗鐨勫皬杞︾▼搴 + /// 杈撳嚭锛氳嫢澶辫触锛屾筏绉師鍥 + /// true 琛ㄧず缂栬瘧骞朵笅鍙戞垚鍔 + private bool TryCompileAndDispatch( + Delivery d, + Car usingCar, + SegmentPlan fetchPlan, + SegmentPlan putPlan, + SegmentPlan escPlan, + out CarProgram allCode, + out string stuckReason) + { + allCode = null; + stuckReason = null; + + // 杞﹀彲鐢ㄦф鏌ワ細鑻ヨ溅宸茶鍗犵敤鍒欎笉鑳戒笅鍙 + lock (Commons.PlanSession) + { + if (usingCar.tags.Contains("occupied")) + { + stuckReason = $"{d.Id} selected {usingCar.id} but car not available"; + return false; + } + } + + try + { + void AfterFetch(CarProgram.LetGo doLetGo) + { + d.DoneFetch?.Invoke(d); + lock (d.SyncStatus) d.Putting = true; + PersistSingleDelivery(d); + doLetGo(); + } + + void AfterPut(CarProgram.LetGo doLetGo) + { + d.FinishTime = DateTime.Now; + d.DonePut?.Invoke(d); + PersistSingleDelivery(d); + doLetGo(); + } + + var forecastPredicate = new Func(site => site.fields.ContainsKey(DefineGiveWayType(usingCar))); + + // 鏍规嵁鏄惁鏈夊彇璐/鏀捐揣/閫冮告锛屾嫾鎺ョ紪璇戜负瀹屾暣绋嬪簭骞惰拷鍔犻伩璁╅娴 + if (escPlan == null) + { + if (fetchPlan != null && !d.SkipPut) + { + allCode = fetchPlan.Compile($"D{d.Id}", false) + .Append(putPlan, beforeCall: AfterFetch) + .Forecast(Commons.GenerateEscapePlan(putPlan, forecastPredicate)); + } + else + { + allCode = putPlan.Compile($"D{d.Id}",false) + .Forecast(Commons.GenerateEscapePlan(putPlan, forecastPredicate)); + } + } + else + { + if (fetchPlan != null) + { + allCode = fetchPlan.Compile($"D{d.Id}Esc", false) + .Append(putPlan, beforeCall: AfterFetch) + .Append(escPlan, beforeCall: AfterPut) + .Forecast(Commons.GenerateEscapePlan(putPlan, forecastPredicate)); + } + else + { + allCode = putPlan.Compile($"D{d.Id}Esc", false) + .Append(escPlan, beforeCall: AfterPut) + .Forecast(Commons.GenerateEscapePlan(putPlan, forecastPredicate)); + } + } + + // 鏇存柊灏忚溅鏍囩锛氭爣璁颁负 occupied/deliver锛岃缃 dest 涓烘斁璐х偣鎴栭冮哥粓鐐 + lock (Commons.PlanSession) + { + usingCar.tags.Remove("pendingput"); + usingCar.tags.Add("occupied", $"CDM_{d.Id}"); + usingCar.tags.Remove("blocking"); + usingCar.tags.Remove("idle"); + usingCar.tags.Remove("charging"); + usingCar.tags.Add("deliver", d.Id); + usingCar.tags.Remove("redirect"); + if (escPlan == null) + usingCar.tags.Add("dest", d.Dst.ToString()); + else + usingCar.tags.Add("dest", escPlan.segments.Last().id.ToString()); + } + + d.UsingCar = usingCar; + stuckReason = $"{usingCar.id} running"; + return true; + } + catch (Exception ex) + { + // 缂栬瘧澶辫触锛氬洖婊氭爣绛撅紝骞惰褰 + Diagnosis.Log($"compile/dispatch failed D{d.Id} car {usingCar.id}: {ExceptionFormatter.FormatEx(ex)}", "error", true); + lock (Commons.PlanSession) + { + usingCar.tags.Remove("deliver"); + usingCar.tags.Remove("occupied"); + usingCar.tags.Remove("dest"); + usingCar.tags.Add("idle", DateTime.Now.ToString(CultureInfo.CurrentCulture)); + } + stuckReason = $"compile failed: {ex.Message}"; + return false; + } + } + + /// + /// 寮傛鎵ц鎼繍浠诲姟锛氭爣璁颁换鍔℃縺娲汇佷笅鍙戠▼搴忋佺瓑寰呭畬鎴愩佹洿鏂 JPH銆佸洖璋冦佹竻鐞嗘爣绛句笌鎸佷箙鍖 + /// + /// 鎼繍浠诲姟 + /// 鎵ц浠诲姟鐨勫皬杞 + /// 鍙栬揣璺緞瑙勫垝锛堝彲涓 null锛 + /// 閫冮歌矾寰勮鍒掞紙鍙负 null锛 + /// 宸茬紪璇戠殑灏忚溅绋嬪簭 + private async Task RunDeliveryAsync( + Delivery d, + Car usingCar, + SegmentPlan fetchPlan, + SegmentPlan escPlan, + CarProgram allCode) + { + try + { + lock (d.SyncStatus) d.Active = true; + d.StartTime = DateTime.Now; + d.OnStart?.Invoke(d); + PersistSingleDelivery(d); + Commons.DeleteTag(tags, "FailCDMToStandby"); + Commons.DeleteTag(tags, "FailCDMToStandbyTime"); + if (fetchPlan != null) LeaveChargeAction(usingCar, fetchPlan.Source); + + try + { + await allCode.Queue(); + } + catch (Exception ex) + { + // 鈥滆 Block() 瑙﹀彂鐨 not allowed to lock鈥 灞炰簬鍙鏈熷垎鏀細娓呯悊灏忚溅鏍囩骞惰繑鍥烇紝涓嶈涓洪敊璇 + var msg = ex is AggregateException ae ? ae.InnerExceptions.FirstOrDefault()?.Message ?? ex.Message : ex.Message; + Diagnosis.Post($"runner ex: {msg}", "block", true); + if (msg.Contains("not allowed to lock")) + { + lock (d.SyncStatus) d.Active = false; + lock (Commons.PlanSession) + { + usingCar.tags.Remove("deliver"); + usingCar.tags.Remove("occupied"); + usingCar.tags.Remove("redirect"); + usingCar.tags.Remove("dest"); + usingCar.tags.Add("idle", DateTime.Now.ToString(CultureInfo.CurrentCulture)); + } + return; + } + + Diagnosis.Log($"Send Script to {usingCar.id} exception:{ExceptionFormatter.FormatEx(ex)}", "error", true); + lock (d.SyncStatus) + { + d.Active = false; + if(d.Putting) + d.Error = true; + else + { + d.Suspended = true; + } + } + lock (Commons.PlanSession) + { + usingCar.tags.Remove("deliver"); + usingCar.tags.Remove("occupied"); + usingCar.tags.Remove("redirect"); + usingCar.tags.Remove("dest"); + usingCar.tags.Add("idle", DateTime.Now.ToString(CultureInfo.CurrentCulture)); + } + d.Failed?.Invoke(d); + PersistSingleDelivery(d); + return; + } + + //杞︿綋瀹屾垚鎵鏈夊姩浣滃悗缁鐞 + UpdateJphOnSuccess(); // 鏇存柊姣忓皬鏃朵綔涓氭暟缁熻 + + if (d.AppendedEscape != null) + { + d.FinishTime = DateTime.Now; + d.DonePut?.Invoke(d); + PersistSingleDelivery(d); + } + d.AppendedEscape?.Invoke(); + + lock (d.SyncStatus) d.Finished = true; + + lock (Commons.PlanSession) + { + usingCar.tags.Remove("occupied"); + usingCar.tags.Remove("deliver"); + usingCar.tags.Remove("dest"); + usingCar.tags.Add("idle", DateTime.Now.ToString(CultureInfo.CurrentCulture)); + } + + if (d.AppendedEscape == null) + { + d.FinishTime = DateTime.Now; + d.DonePut?.Invoke(d); + } + d.DoneMission?.Invoke(d); + RemoveSingleDeliveryFromPersist(d.Id); + } + catch (Exception ex) + { + Diagnosis.Post($"Delivery(refactored) {d.Id} {d.Src}->{d.Dst} fault:{ExceptionFormatter.FormatEx(ex)}", "ChainedDelivery"); + // 澶栧眰寮傚父锛堝 OnStart/Persist 鎶涢敊锛夐渶閲嶇疆浠诲姟鐘舵佸苟娓呯悊灏忚溅鏍囩锛岄伩鍏嶅兊灏镐换鍔′笌鍗犺溅 + lock (d.SyncStatus) { d.Active = false; d.Error = true; } + lock (Commons.PlanSession) + { + usingCar.tags.Remove("deliver"); + usingCar.tags.Remove("occupied"); + usingCar.tags.Remove("redirect"); + usingCar.tags.Remove("dest"); + usingCar.tags.Add("idle", DateTime.Now.ToString(CultureInfo.CurrentCulture)); + } + d.Failed?.Invoke(d); + RemoveSingleDeliveryFromPersist(d.Id); + } + } + + private DateTime _stTime = DateTime.Now; + private int _stPerformed; + private double _prevJph; + + /// + /// 浠诲姟鎴愬姛鍚庢洿鏂 JPH锛堟瘡灏忔椂浣滀笟鏁帮級缁熻锛岄噰鐢ㄦ粦鍔ㄧ獥鍙f寚鏁板钩婊 + /// + private void UpdateJphOnSuccess() + { + var stat = (DeliveryMissionStatus)status; + stat.Performed += 1; + var passTime = (DateTime.Now - _stTime).TotalHours; + stat.Jph = _prevJph * (1 - passTime) + (stat.Performed - _stPerformed) * passTime; + if (passTime > 1) + { + _stTime = DateTime.Now; + _stPerformed = stat.Performed; + _prevJph = stat.Jph; + } + } + + #endregion + + #region Car selection helpers / blocking (kept as compatible utilities) + + /// + /// 鍒ゆ柇灏忚溅鏄惁婊¤冻浠诲姟鎸囧畾鐨勮溅鍨嬬瓫閫夋潯浠讹紙CarType/SelectorKey锛 + /// + /// 寰呮鏌ョ殑灏忚溅 + /// 浠诲姟瑕佹眰鐨勫皬杞︾被鍨嬶紙绌烘垨 "Car" 琛ㄧず涓嶉檺鍒讹級 + /// true 琛ㄧず婊¤冻绛涢夋潯浠 + private bool MeetSelectorKey(Car car, string carType) + { + if (car is DummyCar) return true; + if (carType == "Car") return true; + if (car.fields.ContainsKey("CarType") && + ((car.fields["CarType"] == carType) || car.fields["CarType"] == "Car")) + return true; + return false; + } + + /// + /// 鍛戒护鎸囧畾灏忚溅杩涘叆闃诲鐘舵侊細鎷︽埅褰撳墠鎵ц銆佸幓閬胯鐐广侀噸缃皟搴︼紝淇濇寔涓庢棫瀹炵幇涓鑷寸殑閿侀『搴忎互闄嶄綆姝婚攣椋庨櫓銆 + /// + /// 灏忚溅ID + /// 鍏宠仈浠诲姟ID锛堜粎鐢ㄤ簬鏃ュ織锛 + /// true 琛ㄧず鍛戒护涓嬪彂鎴愬姛 + public static bool CommandToBlock(int carId, int taskId = -1) + { + var car = (Car)SimpleLib.GetCar(carId); + int siteId; + + lock (TrafficControl.syncTrafficSequence) + { + if (car.status.pendingLocks.Length == 0) + { + Diagnosis.Log($"cannot block {taskId}, destination already locked", "CommandToBlock", true); + return false; + } + if (car.status.holdingLocks == null || car.status.holdingLocks.Length == 0) + { + Diagnosis.Log($"cannot block {taskId}, car {car.id} has no holdingLocks", "CommandToBlock", true); + return false; + } + siteId = car.status.holdingLocks.Last(); + } + + car.Intercept(success => + { + if (!success) return; + lock (Commons.PlanSession) + { + car.NoSchedule(true); + car.tags.Add("occupied", "blocking"); + } + UglyBlock(car); + car.TrafficReset(SimpleLib.GetSite(siteId), true, false); + lock (Commons.PlanSession) + { + car.tags.Clear(); + car.tags.Add("idle", DateTime.Now.ToString(CultureInfo.CurrentCulture)); + } + }); + + return true; + } + + /// + /// 灏嗗皬杞︾疆涓衡滃惞鏁b濈姸鎬侊細鍋滄璋冨害銆佹竻绌虹珯鐐广佹竻绌烘爣绛俱佽涓轰笉鍙皟搴 + /// + private static void BlownCar(Car car) + { + car.NoSchedule(); + car.siteID = -1; + car.tags.Clear(); + car.status.usage.AddUsage("base", new CarUsage.CarUsageInfo { scheduling = false, refreshing = false }); + } + + /// + /// 鎵ц闃诲閫昏緫锛氳皟鐢ㄥ皬杞 reset銆佺疆涓洪樆濉炰腑銆佸惞鏁c佽疆璇㈢瓑寰呮仮澶嶏紙鏈闀跨害 60 绉掞級 + /// + private static void UglyBlock(Car usingCar) + { + try + { + ((ClumsyCar)usingCar).Get("reset"); + ((ClumsyCar)usingCar).lstatus = "闃诲涓"; + BlownCar(usingCar); + const int maxWaitCount = 120; + var cnt = 0; + while (cnt < maxWaitCount) + { + cnt++; + Thread.Sleep(500); + if (usingCar.lstatus.Contains("姝e父") && cnt > 2) break; + } + + if (!usingCar.lstatus.Contains("姝e父")) + Diagnosis.Log($"block timeout, ucar:{usingCar.id}, lstatus:{usingCar.lstatus}", "block", true); + } + catch (Exception e) + { + Diagnosis.Log($"block fail, ucar:{usingCar?.id}, status:{usingCar?.status}, " + + $"programs:{usingCar?.status?.programs}, task:{usingCar?.status?.programs?.task}: {ExceptionFormatter.FormatEx(e)}", "block", true); + try { Thread.Sleep(500); } + catch + { + // ignored + } + } + } + + #endregion + } +} diff --git a/StandardScene.Core/Chained/DeliveryCallbackAttacher.cs b/StandardScene.Core/Chained/DeliveryCallbackAttacher.cs new file mode 100644 index 0000000..eb1183c --- /dev/null +++ b/StandardScene.Core/Chained/DeliveryCallbackAttacher.cs @@ -0,0 +1,66 @@ +using System; +using System.Linq; +using SimpleCore.Library; + +namespace StandardScene.Chained +{ + /// + /// 璐熻矗鏍规嵁 Delivery 涓婅褰曠殑鍥炶皟 key 鍒楄〃锛岄氳繃鍥炶皟娉ㄥ唽琛ㄧ粺涓鎸傝浇鎵鏈変簨浠跺洖璋冦 + /// + public static class DeliveryCallbackAttacher + { + public static void AttachAll(ChainedDeliveryMission.Delivery d) + { + if (d == null) return; + + // OnStart + foreach (var key in d.OnStartCallbackKeys.Distinct()) + { + var cb = DeliveryCallbackRegistry.ResolveOnStart(key); + if (cb != null) d.OnStart += cb; + else Diagnosis.Log($"Unknown OnStart callback key: {key}", "DeliveryCallbackAttacher"); + } + + // DoneFetch + foreach (var key in d.DoneFetchCallbackKeys.Distinct()) + { + var cb = DeliveryCallbackRegistry.ResolveDoneFetch(key); + if (cb != null) d.DoneFetch += cb; + else Diagnosis.Log($"Unknown DoneFetch callback key: {key}", "DeliveryCallbackAttacher"); + } + + // DonePut + foreach (var key in d.DonePutCallbackKeys.Distinct()) + { + var cb = DeliveryCallbackRegistry.ResolveDonePut(key); + if (cb != null) d.DonePut += cb; + else Diagnosis.Log($"Unknown DonePut callback key: {key}", "DeliveryCallbackAttacher"); + } + + // DoneMission + foreach (var key in d.DoneMissionCallbackKeys.Distinct()) + { + var cb = DeliveryCallbackRegistry.ResolveDoneMission(key); + if (cb != null) d.DoneMission += cb; + else Diagnosis.Log($"Unknown DoneMission callback key: {key}", "DeliveryCallbackAttacher"); + } + + // Failed + foreach (var key in d.FailedCallbackKeys.Distinct()) + { + var cb = DeliveryCallbackRegistry.ResolveFailed(key); + if (cb != null) d.Failed += cb; + else Diagnosis.Log($"Unknown Failed callback key: {key}", "DeliveryCallbackAttacher"); + } + + // OnTerminated + foreach (var key in d.OnTerminatedCallbackKeys.Distinct()) + { + var cb = DeliveryCallbackRegistry.ResolveOnTerminated(key); + if (cb != null) d.OnTerminated += cb; + else Diagnosis.Log($"Unknown OnTerminated callback key: {key}", "DeliveryCallbackAttacher"); + } + } + } +} + diff --git a/StandardScene.Core/Chained/DeliveryCallbackRegistry.cs b/StandardScene.Core/Chained/DeliveryCallbackRegistry.cs new file mode 100644 index 0000000..150708c --- /dev/null +++ b/StandardScene.Core/Chained/DeliveryCallbackRegistry.cs @@ -0,0 +1,76 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace StandardScene.Chained +{ + /// + /// 鍙敤浜庡尯鍒 Delivery 鍚勭敓鍛藉懆鏈熶簨浠剁殑鏋氫妇銆 + /// + public enum DeliveryEventType + { + OnStart, + DoneFetch, + DonePut, + DoneMission, + Failed, + OnTerminated + } + + /// + /// 鍏ㄥ眬浠诲姟鍥炶皟娉ㄥ唽琛細閫氳繃 (浜嬩欢绫诲瀷, key) 娉ㄥ唽/瑙f瀽鍚勯樁娈靛洖璋冿紝渚夸簬鎸佷箙鍖栧拰鎭㈠銆 + /// + public static class DeliveryCallbackRegistry + { + private static readonly Dictionary<(DeliveryEventType, string), Delegate> _callbacks = new(); + + private static void RegisterInternal(DeliveryEventType type, string key, Delegate callback) + { + if (string.IsNullOrWhiteSpace(key) || callback == null) return; + _callbacks[(type, key)] = callback; + } + + private static T ResolveInternal(DeliveryEventType type, string key) where T : class + { + if (key == null) return null; + return _callbacks.TryGetValue((type, key), out var d) ? d as T : null; + } + + public static void RegisterOnStart(string key, Action callback) => + RegisterInternal(DeliveryEventType.OnStart, key, callback); + + public static void RegisterDoneFetch(string key, Action callback) => + RegisterInternal(DeliveryEventType.DoneFetch, key, callback); + + public static void RegisterDonePut(string key, Action callback) => + RegisterInternal(DeliveryEventType.DonePut, key, callback); + + public static void RegisterDoneMission(string key, Action callback) => + RegisterInternal(DeliveryEventType.DoneMission, key, callback); + + public static void RegisterFailed(string key, Action callback) => + RegisterInternal(DeliveryEventType.Failed, key, callback); + + public static void RegisterOnTerminated(string key, Func> callback) => + RegisterInternal(DeliveryEventType.OnTerminated, key, callback); + + public static Action ResolveOnStart(string key) => + ResolveInternal>(DeliveryEventType.OnStart, key); + + public static Action ResolveDoneFetch(string key) => + ResolveInternal>(DeliveryEventType.DoneFetch, key); + + public static Action ResolveDonePut(string key) => + ResolveInternal>(DeliveryEventType.DonePut, key); + + public static Action ResolveDoneMission(string key) => + ResolveInternal>(DeliveryEventType.DoneMission, key); + + public static Action ResolveFailed(string key) => + ResolveInternal>(DeliveryEventType.Failed, key); + + public static Func> ResolveOnTerminated(string key) => + ResolveInternal>>(DeliveryEventType.OnTerminated, key); + } +} + diff --git a/StandardScene.Core/Chained/DeliveryViewer.cs b/StandardScene.Core/Chained/DeliveryViewer.cs new file mode 100644 index 0000000..baa34dd --- /dev/null +++ b/StandardScene.Core/Chained/DeliveryViewer.cs @@ -0,0 +1,316 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Data; +using System.Drawing; +using System.Linq; +using System.Net.Http; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Forms; +using StandardScene.Model; +using SimpleLite; +using SimpleCore; +using SimpleCore.Library; +using StandardScene.Utils; +using static StandardScene.Chained.ChainedDeliveryMission; + +namespace StandardScene.Chained +{ + public partial class DeliveryViewer : Form + { + private const int OverdueMinutesThreshold = 10000; // 绾7澶╄涓鸿秴鏃 + private const int DisplayColumnIndexOverdueFlag = 9; + private static readonly HttpClient SharedHttpClient = new HttpClient(); + /// 閫変腑琛岀殑鑳屾櫙鑹 + private static readonly Color SelectedRowBackColor = Color.FromArgb(220, 230, 250); + /// 缂撳瓨閫変腑琛岀储寮曪紝閬垮厤鍦 RetrieveVirtualItem 涓闂 SelectedIndices 寮曞彂閫掑綊 + private readonly HashSet _selectedIndicesCache = new HashSet(); + + private ListViewItem _item = null; + + public DeliveryViewer() + { + InitializeComponent(); + } + + private readonly ContextMenuStrip strip = new ContextMenuStrip(); + + private void DeliveryViewer_Load(object sender, EventArgs e) + { + strip.Items.Clear(); + strip.Items.Add("鍙栨秷浠诲姟", null, CancelClick); + strip.Items.Add("閲嶅彂浠诲姟", null, ResendClick); + strip.Items.Add("鎹㈣溅閲嶅彂浠诲姟", null, ChangeCarResendClick); + currentTaskList.ContextMenuStrip = strip; + } + + private List _listDeliveries = new List(); + + /// 灏嗕换鍔℃爣璁颁负宸插彇娑堬紙Canceled锛夈 + private static void MarkDeliveryCanceled(Delivery d) + { + if (d == null) return; + lock (d.SyncStatus) + { + d.Canceled = true; + d.Active = false; + } + } + + /// + /// 灏嗕换鍔$姸鎬侀噸缃负 Waiting銆 + /// 褰 clearCarForChange=true 鏃讹紝浠呭綋鐘舵佷负 Suspended 鎴 Waiting 涓旀湭澶勪簬鏀捐揣闃舵鏃讹紝 + /// 鎵嶄細娓呯┖ UsingCar 骞惰繑鍥 true锛涘惁鍒欒繑鍥 false銆 + /// + private static bool MarkDeliveryWaiting(Delivery d, bool clearCarForChange) + { + if (d == null) return false; + lock (d.SyncStatus) + { + var status = d.GetStatus(); + if (clearCarForChange) + { + if ((status is not DeliveryStatus.Suspended and not DeliveryStatus.Waiting) || d.Putting) + { + return false; + } + + d.UsingCar = null; + } + else + { + d.SkipFetch = d.Putting; + } + + d.Active = false; + d.Finished = false; + d.Error = false; + d.Canceled = false; + d.Terminated = false; + d.Suspended = false; + return true; + } + } + + protected virtual string[] GetDisplayContent(Delivery dd) + { + var srcName =SimpleLib.GetSite(dd.Src).name; + var dstName =SimpleLib.GetSite(dd.Dst).name; + var now = DateTime.Now; + var usingCar = dd.UsingCar == null ? string.Empty : dd.UsingCar.name; + return + [ + $"{dd.Id}", + $"{usingCar}", + $"{dd.Src}-{srcName}", + $"{dd.Dst}-{dstName}", + $"{dd.GetStatus()}", + $"{dd.CreateTime:yyyy-mm-dd HH:mm:ss:fff}", + $"{dd.StartTime:yyyy-mm-dd HH:mm:ss:fff}", + $"{dd.FinishTime:yyyy-mm-dd HH:mm:ss:fff}", + + $"{dd.Priority}", + $"{((now - dd.CreateTime).TotalMinutes > OverdueMinutesThreshold ? 1 : 0)}", + $"{dd.Id}" + ]; + } + + private void TaskFlush() + { + _listDeliveries.Clear(); + try + { + foreach (var cdm in SimpleProject.proj.Missions.OfType()) + foreach (var dd in cdm.GetDeliveries(checkBox1.Checked, checkBox2.Checked,checkBox2.Checked,checkBox2.Checked)) + _listDeliveries.Add(GetDisplayContent(dd)); + + if (_listDeliveries.Count > 0) + { + var len = _listDeliveries[0].Length; + if (len > 0) _listDeliveries = _listDeliveries.OrderByDescending(p => int.Parse(p[len - 2])).ToList(); + } + } + catch (Exception ex) + { + Diagnosis.Post($"TaskFlush 寮傚父: {ExceptionFormatter.FormatEx(ex)}"); + } + + } + + private void timer1_Tick(object sender, EventArgs e) + { + try + { + TaskFlush(); + currentTaskList.VirtualListSize = _listDeliveries.Count; + currentTaskList.Invalidate(); + } + catch (Exception ex) + { + Diagnosis.Post($"timer1_Tick 寮傚父: {ExceptionFormatter.FormatEx(ex)}"); + } + + } + + private void currentTaskList_RetrieveVirtualItem(object sender, RetrieveVirtualItemEventArgs e) + { + try + { + var n = e.ItemIndex; + e.Item = new ListViewItem(_listDeliveries[n]); + if (_listDeliveries[n].Length > DisplayColumnIndexOverdueFlag && _listDeliveries[n][DisplayColumnIndexOverdueFlag] == "1") + e.Item.ForeColor = Color.Red; + if (_selectedIndicesCache.Contains(n)) + e.Item.BackColor = SelectedRowBackColor; + } + catch (Exception) + { + e.Item = new ListViewItem(["", "", "", "", "", "", "", "", ""]); + } + } + + private void currentTaskList_MouseClick(object sender, MouseEventArgs e) + { + if (e.Button != MouseButtons.Right) return; + _item = currentTaskList.GetItemAt(e.X, e.Y); + } + + private void ResendClick(object sender, EventArgs e) + { + if (_item == null) return; + string taskCode = _item.Text; + try + { + var cdm = SimpleProject.proj.Missions.OfType().FirstOrDefault(); + if (cdm == null) return; + var d = cdm.GetDeliveries(true, true, true, true) + .OfType() + .FirstOrDefault(s => s.Id == taskCode); + if (d == null) + { + MessageBox.Show("鍒楄〃涓笉瀛樺湪鐩爣浠诲姟", "鎻愮ず", MessageBoxButtons.OK, MessageBoxIcon.Warning); + return; + } + var ms = MessageBox.Show($"鏄惁閲嶅彂浠诲姟--{taskCode}", "鎻愮ず", MessageBoxButtons.OKCancel, MessageBoxIcon.Question); + if (ms != System.Windows.Forms.DialogResult.OK) return; + if (!MarkDeliveryWaiting(d, clearCarForChange: false)) + { + MessageBox.Show("閲嶅彂浠诲姟澶辫触锛氬綋鍓嶇姸鎬佷笉鍏佽閲嶅彂", "鎻愮ず", MessageBoxButtons.OK, MessageBoxIcon.Warning); + return; + } + + // 鐘舵佸凡鏀逛负 Waiting锛屾寔涔呭寲 + cdm.PersistDelivery(d); + } + catch (Exception) + { + MessageBox.Show("鍒楄〃涓笉瀛樺湪鐩爣浠诲姟", "鎻愮ず", MessageBoxButtons.OK, MessageBoxIcon.Warning); + } + } + + private void CancelClick(object sender, EventArgs e) + { + if (_item == null) return; + string str = _item.Text; + try + { + var cdm = SimpleProject.proj.Missions.OfType().FirstOrDefault(); + if (cdm == null) return; + var d = cdm.GetDeliveries(true, true, true, true) + .OfType() + .FirstOrDefault(s => s.Id == str); + if (d == null) + { + MessageBox.Show("鍒楄〃涓笉瀛樺湪鐩爣浠诲姟", "鎻愮ず", MessageBoxButtons.OK, MessageBoxIcon.Warning); + return; + } + var ms = MessageBox.Show($"鏄惁缁撴潫浠诲姟--{str}", "鎻愮ず", MessageBoxButtons.OKCancel, MessageBoxIcon.Question); + if (ms != System.Windows.Forms.DialogResult.OK || d.IsFinished()) return; + + // 1) 鐘舵佷笂灏嗕换鍔℃爣璁颁负宸插彇娑 + MarkDeliveryCanceled(d); + + // 2) 鑻ュ皬杞﹀綋鍓嶆鍦ㄦ墽琛岃浠诲姟锛屽垯涓嬪彂 reset 鎸囦护 + bool excutingTask = d.UsingCar != null && d.UsingCar.tags.IsEqual("taskCode", d.TaskId); + if (excutingTask && d.UsingCar != null + && (d.UsingCar.tags?.Contains("occupied") == true || (d.UsingCar.status?.pendingLocks?.Length ?? 0) != 0)) + { + _ = SharedHttpClient.GetStringAsync($"http://{d.UsingCar.address}:8008/reset"); + Diagnosis.Log($"鎵嬪姩缁撴潫浠诲姟;{d.TaskId}", "task", true); + } + + // 3) 鎸佷箙鍖栧凡鍙栨秷鐘舵 + cdm.PersistDelivery(d); + } + catch (Exception ex) + { + Diagnosis.Post($"缁撴潫浠诲姟 {str} 寮傚父: {ExceptionFormatter.FormatEx(ex)}"); + } + } + + private void ChangeCarResendClick(object sender, EventArgs e) + { + if (_item == null) return; + string taskCode = _item.Text; + try + { + var cdm = SimpleProject.proj.Missions.OfType().FirstOrDefault(); + if (cdm == null) return; + var d = cdm.GetDeliveries(true, true, true, true) + .OfType() + .FirstOrDefault(s => s.Id == taskCode); + if (d == null) + { + MessageBox.Show("鍒楄〃涓笉瀛樺湪鐩爣浠诲姟", "鎻愮ず", MessageBoxButtons.OK, MessageBoxIcon.Warning); + return; + } + + var ms = MessageBox.Show($"鏄惁鎹㈣溅閲嶅彂浠诲姟--{taskCode}", "鎻愮ず", MessageBoxButtons.OKCancel, MessageBoxIcon.Question); + if (ms != System.Windows.Forms.DialogResult.OK) return; + + if (!MarkDeliveryWaiting(d, clearCarForChange: true)) + { + MessageBox.Show("鎹㈣溅閲嶅彂澶辫触锛氫粎褰撲换鍔$姸鎬佷负 Suspended 鎴 Waiting 涓旀湭澶勪簬鏀捐揣闃舵鏃舵墠鍏佽鎹㈣溅閲嶅彂", "鎻愮ず", MessageBoxButtons.OK, MessageBoxIcon.Warning); + return; + } + + // 鐘舵佸凡鏀逛负 Waiting 涓 UsingCar 宸叉竻绌猴紝鎸佷箙鍖 + cdm.PersistDelivery(d); + } + catch (Exception ex) + { + Diagnosis.Post($"鎹㈣溅閲嶅彂浠诲姟 {taskCode} 寮傚父: {ExceptionFormatter.FormatEx(ex)}"); + MessageBox.Show("鎹㈣溅閲嶅彂浠诲姟寮傚父锛岃鏌ョ湅鏃ュ織", "鎻愮ず", MessageBoxButtons.OK, MessageBoxIcon.Warning); + } + } + + private void DeliveryViewer_FormClosing(object sender, FormClosingEventArgs e) + { + if (e.CloseReason == CloseReason.UserClosing) + { + e.Cancel = true; + this.Visible = false; + timer1.Stop(); + } + } + + protected override void SetVisibleCore(bool value) + { + if (!IsHandleCreated && value) + CreateHandle(); + bool wasVisible = Visible; + base.SetVisibleCore(value); + if (value && !wasVisible) + timer1.Start(); + } + + private void currentTaskList_SelectedIndexChanged(object sender, EventArgs e) + { + _selectedIndicesCache.Clear(); + foreach (int i in currentTaskList.SelectedIndices) + _selectedIndicesCache.Add(i); + this.BeginInvoke(() => currentTaskList.Invalidate()); + } + } +} diff --git a/StandardScene.Core/Chained/DeliveryViewer.designer.cs b/StandardScene.Core/Chained/DeliveryViewer.designer.cs new file mode 100644 index 0000000..d41121f --- /dev/null +++ b/StandardScene.Core/Chained/DeliveryViewer.designer.cs @@ -0,0 +1,206 @@ + +using System.Windows.Forms; + +namespace StandardScene.Chained +{ + partial class DeliveryViewer + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + if (disposing) + { + strip?.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.components = new System.ComponentModel.Container(); + this.currentTaskList = new System.Windows.Forms.ListView(); + this.columnHeader8 = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.columnHeader1 = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.columnHeader4 = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.columnHeader5 = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.columnHeader9 = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.columnHeader6 = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.columnHeader2 = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.columnHeader7 = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.columnHeader3 = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.label2 = new System.Windows.Forms.Label(); + this.timer1 = new System.Windows.Forms.Timer(this.components); + this.checkBox1 = new System.Windows.Forms.CheckBox(); + this.checkBox2 = new System.Windows.Forms.CheckBox(); + this.SuspendLayout(); + // + // currentTaskList + // + this.currentTaskList.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.currentTaskList.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] { + this.columnHeader8, + this.columnHeader1, + this.columnHeader4, + this.columnHeader5, + this.columnHeader9, + this.columnHeader6, + this.columnHeader2, + this.columnHeader7, + this.columnHeader3}); + this.currentTaskList.Font = new System.Drawing.Font("寰蒋闆呴粦", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + this.currentTaskList.FullRowSelect = true; + this.currentTaskList.GridLines = true; + this.currentTaskList.HideSelection = false; + this.currentTaskList.Location = new System.Drawing.Point(38, 62); + this.currentTaskList.Name = "currentTaskList"; + this.currentTaskList.Size = new System.Drawing.Size(1146, 429); + this.currentTaskList.TabIndex = 2; + this.currentTaskList.UseCompatibleStateImageBehavior = false; + this.currentTaskList.View = System.Windows.Forms.View.Details; + this.currentTaskList.VirtualMode = true; + this.currentTaskList.RetrieveVirtualItem += new System.Windows.Forms.RetrieveVirtualItemEventHandler(this.currentTaskList_RetrieveVirtualItem); + this.currentTaskList.SelectedIndexChanged += new System.EventHandler(this.currentTaskList_SelectedIndexChanged); + this.currentTaskList.MouseClick += new System.Windows.Forms.MouseEventHandler(this.currentTaskList_MouseClick); + // + // columnHeader8 + // + this.columnHeader8.Text = "浠诲姟鍙"; + this.columnHeader8.Width = 130; + // + // columnHeader1 + // + this.columnHeader1.Text = "灏忚溅"; + this.columnHeader1.Width = 100; + // + // columnHeader4 + // + this.columnHeader4.Text = "鍙栬揣鐐"; + this.columnHeader4.Width = 130; + // + // columnHeader5 + // + this.columnHeader5.Text = "鏀捐揣鐐"; + this.columnHeader5.Width = 130; + // + // columnHeader9 + // + this.columnHeader9.Text = "浠诲姟鐘舵"; + this.columnHeader9.Width = 100; + // + // columnHeader6 + // + this.columnHeader6.Text = "涓嬪彂鏃堕棿"; + this.columnHeader6.Width = 130; + // + // columnHeader2 + // + this.columnHeader2.Text = "鎵ц鏃堕棿"; + this.columnHeader2.Width = 130; + // + // columnHeader7 + // + this.columnHeader7.Text = "缁撴潫鏃堕棿"; + this.columnHeader7.Width = 130; + // + // columnHeader3 + // + this.columnHeader3.Text = "浼樺厛绾"; + this.columnHeader3.Width = 83; + // + // label2 + // + this.label2.AutoSize = true; + this.label2.Font = new System.Drawing.Font("寰蒋闆呴粦", 14.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + this.label2.Location = new System.Drawing.Point(33, 7); + this.label2.Name = "label2"; + this.label2.Size = new System.Drawing.Size(88, 26); + this.label2.TabIndex = 3; + this.label2.Text = "浠诲姟鍒楄〃"; + // + // timer1 + // + this.timer1.Enabled = true; + this.timer1.Interval = 1000; + this.timer1.Tick += new System.EventHandler(this.timer1_Tick); + // + // checkBox1 + // + this.checkBox1.AutoSize = true; + this.checkBox1.Checked = true; + this.checkBox1.CheckState = System.Windows.Forms.CheckState.Checked; + this.checkBox1.Location = new System.Drawing.Point(127, 15); + this.checkBox1.Margin = new System.Windows.Forms.Padding(2, 2, 2, 2); + this.checkBox1.Name = "checkBox1"; + this.checkBox1.Size = new System.Drawing.Size(108, 16); + this.checkBox1.TabIndex = 4; + this.checkBox1.Text = "鏄剧ず宸插畬鎴愪换鍔"; + this.checkBox1.UseVisualStyleBackColor = true; + // + // checkBox2 + // + this.checkBox2.AutoSize = true; + this.checkBox2.Checked = true; + this.checkBox2.CheckState = System.Windows.Forms.CheckState.Checked; + this.checkBox2.Location = new System.Drawing.Point(239, 14); + this.checkBox2.Margin = new System.Windows.Forms.Padding(2, 2, 2, 2); + this.checkBox2.Name = "checkBox2"; + this.checkBox2.Size = new System.Drawing.Size(318, 16); + this.checkBox2.TabIndex = 5; + this.checkBox2.Text = "鏄剧ず搴熸鐨勪换鍔★紙鍖呮嫭Error銆丆anceled銆乀erminated锛"; + this.checkBox2.UseVisualStyleBackColor = true; + // + // DeliveryViewer + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(1199, 551); + this.Controls.Add(this.checkBox2); + this.Controls.Add(this.checkBox1); + this.Controls.Add(this.label2); + this.Controls.Add(this.currentTaskList); + this.Name = "DeliveryViewer"; + this.Text = "DeliveryViewer"; + this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.DeliveryViewer_FormClosing); + this.Load += new System.EventHandler(this.DeliveryViewer_Load); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + private System.Windows.Forms.Label label2; + private System.Windows.Forms.ColumnHeader columnHeader4; + private System.Windows.Forms.ColumnHeader columnHeader5; + private System.Windows.Forms.ColumnHeader columnHeader6; + private System.Windows.Forms.ColumnHeader columnHeader7; + private System.Windows.Forms.ColumnHeader columnHeader8; + private System.Windows.Forms.Timer timer1; + private System.Windows.Forms.ColumnHeader columnHeader1; + private System.Windows.Forms.ColumnHeader columnHeader2; + private System.Windows.Forms.ColumnHeader columnHeader9; + private System.Windows.Forms.CheckBox checkBox1; + private System.Windows.Forms.CheckBox checkBox2; + public System.Windows.Forms.ListView currentTaskList; + private System.Windows.Forms.ColumnHeader columnHeader3; + } +} \ No newline at end of file diff --git a/StandardScene.Core/Chained/DeliveryViewer.resx b/StandardScene.Core/Chained/DeliveryViewer.resx new file mode 100644 index 0000000..1f666f2 --- /dev/null +++ b/StandardScene.Core/Chained/DeliveryViewer.resx @@ -0,0 +1,123 @@ +锘 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 17, 17 + + \ No newline at end of file diff --git a/StandardScene.Core/Chained/Loop/ILoopRules.cs b/StandardScene.Core/Chained/Loop/ILoopRules.cs new file mode 100644 index 0000000..4759d2e --- /dev/null +++ b/StandardScene.Core/Chained/Loop/ILoopRules.cs @@ -0,0 +1,58 @@ +锘縰sing StandardScene.Model; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using static StandardScene.Chained.AbstractLoopMission; + +namespace StandardScene.Chained.Loop +{ + /// + /// 杩涘叆鐐硅鍒欙細杩斿洖鏄惁鍏佽杩涘叆銆 + /// + public interface IEnterRule + { + bool CanEnter(LoopPoint point, LoopTask task, object context = null); + } + + /// + /// 绂诲紑鐐硅鍒欙細杩斿洖鏄惁鍏佽绂诲紑銆 + /// + public interface IExitRule + { + bool CanExit(LoopPoint point, LoopTask task, object context = null); + } + + /// + /// 鍚堟祦瑙勫垯锛氫粠鍊欓変换鍔′腑閫夋嫨涓涓 + /// + public interface IJoinRule + { + LoopTask SelectJoin(IEnumerable candidates, LoopPoint point); + } + + /// + /// 鍒嗘祦瑙勫垯锛氫负浠诲姟閫夊彇涓嬩竴鍒嗘敮鏍囪瘑 + /// + public interface IBranchRule + { + string SelectBranch(LoopPoint point, LoopTask task, object context = null); + } + + /// + /// 浠诲姟绛栫暐鎺ュ彛锛氭彁渚涗换鍔℃暟鎹潵婧愪笌鍩虹绛栫暐鍐崇瓥锛堢敱 LoopViewer 鎴栧叾瀹冪粍浠跺疄鐜/鏇挎崲锛夈 + /// + public interface ITaskStrategy + { + /// + /// 杩斿洖褰撳墠绛栫暐涓嬬殑浠诲姟闆嗗悎锛堢瓥鐣ヨ礋璐f暟鎹潵婧愶紝渚嬪璇诲彇 LoopViewer 鐨 JSON锛夈 + /// + IEnumerable GetTasks(); + + /// + /// 鍒锋柊绛栫暐鏁版嵁锛堜緥濡傞噸鏂板姞杞 JSON锛夈 + /// + void Refresh(); + } +} diff --git a/StandardScene.Core/Chained/Loop/ITriggerAdapter.cs b/StandardScene.Core/Chained/Loop/ITriggerAdapter.cs new file mode 100644 index 0000000..cea8c15 --- /dev/null +++ b/StandardScene.Core/Chained/Loop/ITriggerAdapter.cs @@ -0,0 +1,36 @@ +锘縰sing System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace StandardScene.Chained.Loop +{ + /// + /// 瑙﹀彂鍣ㄩ傞厤鍣ㄧ粺涓鎺ュ彛锛氬閮ㄨ澶囷紙PLC銆佹寜閽洅銆丄PI 缃戝叧绛夛級瀹炵幇姝ゆ帴鍙e苟瑙﹀彂浜嬩欢銆 + /// + public interface ITriggerAdapter : IDisposable + { + /// + /// 澶栭儴瑙﹀彂浜嬩欢锛歋ource 鐢ㄤ簬鍖哄垎鏉ユ簮锛"PLC","ButtonBox","API"绛夛級锛孠ey/Value 涓轰笟鍔¤嚜瀹氫箟璐熻浇銆 + /// + event EventHandler TriggerRaised; + + /// + /// 鍙夛細鍚姩閫傞厤鍣紙寮鍚疆璇€佸缓绔嬭繛鎺ョ瓑锛夈 + /// + void Start(); + + /// + /// 鍙夛細鍋滄閫傞厤鍣ㄣ + /// + void Stop(); + } + + public class TriggerEventArgs : EventArgs + { + public string Source { get; set; } = string.Empty; + public string Key { get; set; } = string.Empty; + public object Value { get; set; } + } +} diff --git a/StandardScene.Core/Chained/LoopMission.cs b/StandardScene.Core/Chained/LoopMission.cs new file mode 100644 index 0000000..3ad8559 --- /dev/null +++ b/StandardScene.Core/Chained/LoopMission.cs @@ -0,0 +1,163 @@ +using IoTClient.Clients.PLC; +using IoTClient.Common.Enums; +using LessokajiWeaverUtilities.Utilities; +using Newtonsoft.Json; +using SimpleLite.RCS; +using SimpleLite.RCS.CarTypes; +using SimpleCore.Library; +using StandardScene.Chained; +using StandardScene.Model; +using System.Collections.Concurrent; +using System.Threading; +using static StandardScene.Chained.AbstractLoopMission; + +namespace StandardScene +{ + + + public class LoopMissionStatus : AbstractLoopMissionStatus + { + + } + + [MissionType(Name = "鐜嚎杩涚▼", editor = typeof(LoopMission))] + [I18N.DocumentTranslation(Name = "Loop Mission", locale = "en")] + public class LoopMission : AbstractLoopMission + { + [JsonIgnore] public override MissionStatus status { get; set; } = new LoopMissionStatus(); + [JsonIgnore] public Thread myThread; + [JsonIgnore] public bool started = false; + [JsonIgnore] private readonly ConcurrentDictionary _buttonTriggeredSiteIds = new ConcurrentDictionary(); + public float ChargesocStandard = 30; + + public float NoChargesocStandard = 80; + + + + /// + /// API 瑙﹀彂锛氭鏌ョ珯鐐圭墿鏂欑姸鎬 + /// + protected override ExternalTriggerResult OnApiTrigger(int currentSiteId, LoopTask task, Car car) + { + /* var site = SimpleLib.GetSite(currentSiteId); + if (site != null && site.fields.TryGetValue("hasMaterial", out var val) && val == "true") + { + // 鐗╂枡瀛樺湪锛屼娇鐢ㄩ厤缃洰鏍 + return ExternalTriggerResult.UseConfigTarget(); + } + return ExternalTriggerResult.Fail();*/ + if (car.status.enums.TryGetValue("Soc", out var soc)) + { + if (float.Parse(soc) <= ChargesocStandard) + { + //浣跨敤閰嶇疆鐩爣 + return ExternalTriggerResult.UseConfigTarget(); + } + + } + + /* var plcTarget = 17; + if (plcTarget > 0) + { + // PLC 鎸囧畾浜嗙洰鏍囩珯鐐 + return ExternalTriggerResult.UseTarget(plcTarget); + }*/ + return ExternalTriggerResult.Fail(); + } + + /// + /// PLC 瑙﹀彂锛氭牴鎹 PLC 淇″彿鍐冲畾鐩爣 + /// + protected override ExternalTriggerResult OnPlcTrigger(int currentSiteId, LoopTask task, Car car) + { + //var plcTarget = ReadPlcTargetSite(currentSiteId); + SiemensClient client = new SiemensClient(SiemensVersion.S7_200Smart, "127.0.0.1", 103); + var M100 = client.ReadBoolean("M100").Value; + if (M100) + { + //浣跨敤閰嶇疆鐩爣 + return ExternalTriggerResult.UseConfigTarget(); + + } + + /* var plcTarget = 17; + if (plcTarget > 0) + { + // PLC 鎸囧畾浜嗙洰鏍囩珯鐐 + return ExternalTriggerResult.UseTarget(plcTarget); + }*/ + return ExternalTriggerResult.Fail(); + } + protected override ExternalTriggerResult OnChargeTrigger(int currentSiteId, LoopTask task, Car car) + { + + //浣跨敤閰嶇疆鐩爣 + return ExternalTriggerResult.UseConfigTarget(); + if (car.status.enums.TryGetValue("Soc", out var soc)) + { + if (float.Parse(soc) >= NoChargesocStandard) + { + //浣跨敤閰嶇疆鐩爣 + return ExternalTriggerResult.UseConfigTarget(); + } + + } + } + + /// + /// 渚 ButtonMission 鍙嶅皠璋冪敤锛氱櫥璁颁竴涓渶瑕佹寜閽斁琛岀殑绔欑偣銆 + /// 濡傛灉绔欑偣宸茬粡瀛樺湪浜庡瓧鍏镐腑鍒欏拷鐣ワ紝閬垮厤閲嶅鏀捐淇″彿鍫嗙Н銆 + /// + /// 闇瑕佹斁琛岀殑绔欑偣 ID锛岄氬父瀵瑰簲 LoopTask.CurrentStationId + /// 濮嬬粓杩斿洖 true锛岃〃绀烘湰娆℃寜閽Е鍙戝凡琚帴鍙 + public bool EnqueueButtonTriggerSite(int siteId) + { + if (siteId <= 0) + { + Diagnosis.Log($"鎸夐挳鏀捐鐧昏澶辫触锛氭棤鏁堢珯鐐 {siteId}", "LoopMission", true); + return false; + } + + var car = FindCarArrivedAtSite(siteId); + if (car == null) + { + Diagnosis.Post($"鎸夐挳鏀捐鐧昏蹇界暐锛氱珯鐐 {siteId} 褰撳墠娌℃湁鍒扮珯杞﹁締", "LoopMission", false); + return false; + } + + if (_buttonTriggeredSiteIds.TryAdd(siteId, 0)) + { + Diagnosis.Post($"鎸夐挳鏀捐鐧昏鎴愬姛锛氱珯鐐 {siteId}锛岃溅杈 {car.name}", "LoopMission", false); + } + else + { + Diagnosis.Post($"鎸夐挳鏀捐宸插瓨鍦紝蹇界暐閲嶅鐧昏锛氱珯鐐 {siteId}", "LoopMission", false); + } + + return true; + } + + /// + /// 鎸夐挳鐩掕Е鍙戯細鍙湁褰撳墠绔欑偣宸茶鎸夐挳鐧昏杩囷紝鎵嶅厑璁镐娇鐢ㄩ厤缃洰鏍囨斁琛屻 + /// 鍛戒腑鍚庣珛鍗虫秷璐瑰苟鍒犻櫎锛屼繚璇佷竴娆℃寜閽彧鏀捐涓娆° + /// + protected override ExternalTriggerResult OnButtonTrigger(int currentSiteId, LoopTask task, Car car) + { + if (_buttonTriggeredSiteIds.TryRemove(currentSiteId, out _)) + { + Diagnosis.Post($"鎸夐挳鏀捐娑堣垂鎴愬姛锛氱珯鐐 {currentSiteId}锛岃溅杈 {car?.name}", "LoopMission", false); + return ExternalTriggerResult.UseConfigTarget(); + } + + return ExternalTriggerResult.Fail(); + } + + /* private int ReadPlcTargetSite(int siteId) + { + // 浠 PLC 璇诲彇鐩爣绔欑偣鐨勪笟鍔¢昏緫 + // 杩斿洖 0 琛ㄧず鏈幏鍙栧埌鏈夋晥鐩爣 + return 0; + } + */ + } +} diff --git a/StandardScene.Core/Chained/LoopViewer.Designer.cs b/StandardScene.Core/Chained/LoopViewer.Designer.cs new file mode 100644 index 0000000..c9c8888 --- /dev/null +++ b/StandardScene.Core/Chained/LoopViewer.Designer.cs @@ -0,0 +1,570 @@ +锘縰sing System; +using System.Drawing; +using System.Windows.Forms; + +namespace LoopViewerApp +{ + partial class LoopViewer + { + private System.ComponentModel.IContainer components = null; + + private ComboBox cmbTaskKind; + private NumericUpDown numCurrent; + private NumericUpDown numTarget; + private NumericUpDown numTraffic; + private CheckBox chkViaPoint; + private ComboBox cmbStartType; + private NumericUpDown numPriority; + private Button btnEdit; // 淇濈暀瀛楁浠ヤ緵浠g爜閫昏緫/鏍峰紡浣跨敤锛堝湪鐣岄潰涓婇殣钘忥級 + private Button btnDelete; // 淇濈暀瀛楁浠ヤ緵浠g爜閫昏緫/鏍峰紡浣跨敤锛堝湪鐣岄潰涓婇殣钘忥級 + private Button btnSave; + private Button btnCancel; + private ListView lstTasks; + private GroupBox grpEdit; + + // 甯冨眬鎺т欢 + private SplitContainer splitContainer; + private TableLayoutPanel tlpEdit; + private FlowLayoutPanel flpButtons; + + // 涓棿绔栧悜鎸夐挳锛堝垪琛ㄤ笌缂栬緫鍖轰箣闂达級 + private Panel pnlMiddle; + private FlowLayoutPanel flpMiddle; + private Button btnMiddleEdit; + private Button btnMiddleDelete; + + // 鍒楀ご + private ColumnHeader colId; + private ColumnHeader colTaskType; + private ColumnHeader colCurrent; + private ColumnHeader colTarget; + private ColumnHeader colTraffic; + private ColumnHeader colPriority; + private ColumnHeader colViaPoint; + private ColumnHeader colStartType; + + // 鏍囩瀛楁锛堢紪杈戝尯锛 + private Label lblKind; + private Label lblCurrent; + private Label lblTarget; + private Label lblTraffic; + private Label lblPriority; + private Label lblVia; + private Label lblStartType; + private Label lblEditingId; // 鏄剧ず褰撳墠缂栬緫鐨勪换鍔D + + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + private void InitializeComponent() + { + this.splitContainer = new System.Windows.Forms.SplitContainer(); + this.pnlMiddle = new System.Windows.Forms.Panel(); + this.flpMiddle = new System.Windows.Forms.FlowLayoutPanel(); + this.btnMiddleEdit = new System.Windows.Forms.Button(); + this.btnMiddleDelete = new System.Windows.Forms.Button(); + this.lstTasks = new System.Windows.Forms.ListView(); + this.colId = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.colTaskType = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.colCurrent = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.colTarget = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.colTraffic = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.colPriority = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.colViaPoint = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.colStartType = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.grpEdit = new System.Windows.Forms.GroupBox(); + this.tlpEdit = new System.Windows.Forms.TableLayoutPanel(); + this.lblEditingId = new System.Windows.Forms.Label(); + this.lblKind = new System.Windows.Forms.Label(); + this.cmbTaskKind = new System.Windows.Forms.ComboBox(); + this.lblCurrent = new System.Windows.Forms.Label(); + this.numCurrent = new System.Windows.Forms.NumericUpDown(); + this.lblTarget = new System.Windows.Forms.Label(); + this.numTarget = new System.Windows.Forms.NumericUpDown(); + this.lblTraffic = new System.Windows.Forms.Label(); + this.numTraffic = new System.Windows.Forms.NumericUpDown(); + this.lblPriority = new System.Windows.Forms.Label(); + this.numPriority = new System.Windows.Forms.NumericUpDown(); + this.lblVia = new System.Windows.Forms.Label(); + this.chkViaPoint = new System.Windows.Forms.CheckBox(); + this.lblStartType = new System.Windows.Forms.Label(); + this.cmbStartType = new System.Windows.Forms.ComboBox(); + this.flpButtons = new System.Windows.Forms.FlowLayoutPanel(); + this.btnSave = new System.Windows.Forms.Button(); + this.btnCancel = new System.Windows.Forms.Button(); + this.btnEdit = new System.Windows.Forms.Button(); + this.btnDelete = new System.Windows.Forms.Button(); + ((System.ComponentModel.ISupportInitialize)(this.splitContainer)).BeginInit(); + this.splitContainer.Panel1.SuspendLayout(); + this.splitContainer.Panel2.SuspendLayout(); + this.splitContainer.SuspendLayout(); + this.pnlMiddle.SuspendLayout(); + this.flpMiddle.SuspendLayout(); + this.grpEdit.SuspendLayout(); + this.tlpEdit.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.numCurrent)).BeginInit(); + ((System.ComponentModel.ISupportInitialize)(this.numTarget)).BeginInit(); + ((System.ComponentModel.ISupportInitialize)(this.numTraffic)).BeginInit(); + ((System.ComponentModel.ISupportInitialize)(this.numPriority)).BeginInit(); + this.flpButtons.SuspendLayout(); + this.SuspendLayout(); + // + // splitContainer + // + this.splitContainer.Dock = System.Windows.Forms.DockStyle.Fill; + this.splitContainer.Location = new System.Drawing.Point(0, 0); + this.splitContainer.Name = "splitContainer"; + // + // splitContainer.Panel1 + // + this.splitContainer.Panel1.Controls.Add(this.pnlMiddle); + this.splitContainer.Panel1.Controls.Add(this.lstTasks); + // + // splitContainer.Panel2 + // + this.splitContainer.Panel2.Controls.Add(this.grpEdit); + this.splitContainer.Size = new System.Drawing.Size(1200, 600); + this.splitContainer.SplitterDistance = 680; + this.splitContainer.SplitterWidth = 6; + this.splitContainer.TabIndex = 0; + // + // pnlMiddle + // + this.pnlMiddle.Controls.Add(this.flpMiddle); + this.pnlMiddle.Dock = System.Windows.Forms.DockStyle.Right; + this.pnlMiddle.Location = new System.Drawing.Point(614, 0); + this.pnlMiddle.Name = "pnlMiddle"; + this.pnlMiddle.Padding = new System.Windows.Forms.Padding(6); + this.pnlMiddle.Size = new System.Drawing.Size(66, 600); + this.pnlMiddle.TabIndex = 0; + // + // flpMiddle + // + this.flpMiddle.Anchor = System.Windows.Forms.AnchorStyles.None; + this.flpMiddle.Controls.Add(this.btnMiddleEdit); + this.flpMiddle.Controls.Add(this.btnMiddleDelete); + this.flpMiddle.FlowDirection = System.Windows.Forms.FlowDirection.TopDown; + this.flpMiddle.Location = new System.Drawing.Point(0, 220); + this.flpMiddle.Name = "flpMiddle"; + this.flpMiddle.Padding = new System.Windows.Forms.Padding(2); + this.flpMiddle.Size = new System.Drawing.Size(63, 160); + this.flpMiddle.TabIndex = 0; + this.flpMiddle.WrapContents = false; + // + // btnMiddleEdit + // + this.btnMiddleEdit.BackColor = System.Drawing.SystemColors.Control; + this.btnMiddleEdit.FlatStyle = System.Windows.Forms.FlatStyle.Flat; + this.btnMiddleEdit.Font = new System.Drawing.Font("寰蒋闆呴粦", 9F); + this.btnMiddleEdit.Location = new System.Drawing.Point(6, 12); + this.btnMiddleEdit.Margin = new System.Windows.Forms.Padding(4, 10, 4, 4); + this.btnMiddleEdit.Name = "btnMiddleEdit"; + this.btnMiddleEdit.Size = new System.Drawing.Size(50, 40); + this.btnMiddleEdit.TabIndex = 0; + this.btnMiddleEdit.Text = "缂栬緫"; + this.btnMiddleEdit.UseVisualStyleBackColor = false; + this.btnMiddleEdit.Click += new System.EventHandler(this.btnEdit_Click); + // + // btnMiddleDelete + // + this.btnMiddleDelete.BackColor = System.Drawing.Color.LightCoral; + this.btnMiddleDelete.FlatStyle = System.Windows.Forms.FlatStyle.Flat; + this.btnMiddleDelete.Font = new System.Drawing.Font("寰蒋闆呴粦", 9F); + this.btnMiddleDelete.Location = new System.Drawing.Point(6, 62); + this.btnMiddleDelete.Margin = new System.Windows.Forms.Padding(4, 6, 4, 4); + this.btnMiddleDelete.Name = "btnMiddleDelete"; + this.btnMiddleDelete.Size = new System.Drawing.Size(50, 40); + this.btnMiddleDelete.TabIndex = 1; + this.btnMiddleDelete.Text = "鍒犻櫎"; + this.btnMiddleDelete.UseVisualStyleBackColor = false; + this.btnMiddleDelete.Click += new System.EventHandler(this.btnDelete_Click); + // + // lstTasks + // + this.lstTasks.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] { + this.colId, + this.colTaskType, + this.colCurrent, + this.colTarget, + this.colTraffic, + this.colPriority, + this.colViaPoint, + this.colStartType}); + this.lstTasks.Dock = System.Windows.Forms.DockStyle.Fill; + this.lstTasks.FullRowSelect = true; + this.lstTasks.HideSelection = false; + this.lstTasks.Location = new System.Drawing.Point(0, 0); + this.lstTasks.Name = "lstTasks"; + this.lstTasks.OwnerDraw = true; + this.lstTasks.Size = new System.Drawing.Size(680, 600); + this.lstTasks.TabIndex = 0; + this.lstTasks.UseCompatibleStateImageBehavior = false; + this.lstTasks.View = System.Windows.Forms.View.Details; + this.lstTasks.DrawColumnHeader += new System.Windows.Forms.DrawListViewColumnHeaderEventHandler(this.lstTasks_DrawColumnHeader); + this.lstTasks.DrawItem += new System.Windows.Forms.DrawListViewItemEventHandler(this.lstTasks_DrawItem); + this.lstTasks.DrawSubItem += new System.Windows.Forms.DrawListViewSubItemEventHandler(this.lstTasks_DrawSubItem); + this.lstTasks.MouseDoubleClick += new System.Windows.Forms.MouseEventHandler(this.lstTasks_MouseDoubleClick); + // + // colId + // + this.colId.Text = "ID"; + this.colId.Width = 40; + // + // colTaskType + // + this.colTaskType.Text = "浠诲姟绫诲埆"; + this.colTaskType.Width = 110; + // + // colCurrent + // + this.colCurrent.Text = "褰撳墠绔欑偣"; + this.colCurrent.Width = 90; + // + // colTarget + // + this.colTarget.Text = "鐩爣绔欑偣"; + this.colTarget.Width = 90; + // + // colTraffic + // + this.colTraffic.Text = "娴侀噺鎺у埗"; + this.colTraffic.Width = 90; + // + // colPriority + // + this.colPriority.Text = "浼樺厛绾"; + this.colPriority.Width = 80; + // + // colViaPoint + // + this.colViaPoint.Text = "閫斿緞鐐"; + this.colViaPoint.Width = 70; + // + // colStartType + // + this.colStartType.Text = "鍚姩绫诲瀷"; + this.colStartType.Width = 100; + // + // grpEdit + // + this.grpEdit.Controls.Add(this.tlpEdit); + this.grpEdit.Dock = System.Windows.Forms.DockStyle.Fill; + this.grpEdit.Font = new System.Drawing.Font("寰蒋闆呴粦", 10F, System.Drawing.FontStyle.Bold); + this.grpEdit.Location = new System.Drawing.Point(0, 0); + this.grpEdit.Name = "grpEdit"; + this.grpEdit.Size = new System.Drawing.Size(514, 600); + this.grpEdit.TabIndex = 1; + this.grpEdit.TabStop = false; + this.grpEdit.Text = "浠诲姟淇℃伅锛堥変腑鍒楄〃椤瑰悗鍙紪杈戯級"; + // + // tlpEdit + // + this.tlpEdit.ColumnCount = 2; + this.tlpEdit.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 120F)); + this.tlpEdit.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); + this.tlpEdit.Controls.Add(this.lblEditingId, 0, 0); + this.tlpEdit.Controls.Add(this.lblKind, 0, 1); + this.tlpEdit.Controls.Add(this.cmbTaskKind, 1, 1); + this.tlpEdit.Controls.Add(this.lblCurrent, 0, 2); + this.tlpEdit.Controls.Add(this.numCurrent, 1, 2); + this.tlpEdit.Controls.Add(this.lblTarget, 0, 3); + this.tlpEdit.Controls.Add(this.numTarget, 1, 3); + this.tlpEdit.Controls.Add(this.lblTraffic, 0, 4); + this.tlpEdit.Controls.Add(this.numTraffic, 1, 4); + this.tlpEdit.Controls.Add(this.lblPriority, 0, 5); + this.tlpEdit.Controls.Add(this.numPriority, 1, 5); + this.tlpEdit.Controls.Add(this.lblVia, 0, 6); + this.tlpEdit.Controls.Add(this.chkViaPoint, 1, 6); + this.tlpEdit.Controls.Add(this.lblStartType, 0, 7); + this.tlpEdit.Controls.Add(this.cmbStartType, 1, 7); + this.tlpEdit.Controls.Add(this.flpButtons, 1, 8); + this.tlpEdit.Dock = System.Windows.Forms.DockStyle.Fill; + this.tlpEdit.Location = new System.Drawing.Point(3, 25); + this.tlpEdit.Name = "tlpEdit"; + this.tlpEdit.Padding = new System.Windows.Forms.Padding(8); + this.tlpEdit.RowCount = 9; + this.tlpEdit.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 36F)); + this.tlpEdit.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 36F)); + this.tlpEdit.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 36F)); + this.tlpEdit.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 36F)); + this.tlpEdit.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 36F)); + this.tlpEdit.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 36F)); + this.tlpEdit.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 36F)); + this.tlpEdit.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 36F)); + this.tlpEdit.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); + this.tlpEdit.Size = new System.Drawing.Size(508, 572); + this.tlpEdit.TabIndex = 0; + // + // lblEditingId + // + this.lblEditingId.Anchor = System.Windows.Forms.AnchorStyles.Left; + this.lblEditingId.AutoSize = true; + this.tlpEdit.SetColumnSpan(this.lblEditingId, 2); + this.lblEditingId.Font = new System.Drawing.Font("寰蒋闆呴粦", 10F, System.Drawing.FontStyle.Bold); + this.lblEditingId.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(0)))), ((int)(((byte)(120)))), ((int)(((byte)(215))))); + this.lblEditingId.Location = new System.Drawing.Point(11, 14); + this.lblEditingId.Name = "lblEditingId"; + this.lblEditingId.Size = new System.Drawing.Size(78, 24); + this.lblEditingId.TabIndex = 0; + this.lblEditingId.Text = "鏂板浠诲姟"; + // + // lblKind + // + this.lblKind.Dock = System.Windows.Forms.DockStyle.Fill; + this.lblKind.Font = new System.Drawing.Font("寰蒋闆呴粦", 10F); + this.lblKind.Location = new System.Drawing.Point(11, 44); + this.lblKind.Name = "lblKind"; + this.lblKind.Size = new System.Drawing.Size(114, 36); + this.lblKind.TabIndex = 1; + this.lblKind.Text = "浠诲姟绫诲埆锛"; + this.lblKind.TextAlign = System.Drawing.ContentAlignment.MiddleRight; + // + // cmbTaskKind + // + this.cmbTaskKind.Dock = System.Windows.Forms.DockStyle.Fill; + this.cmbTaskKind.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.cmbTaskKind.Font = new System.Drawing.Font("寰蒋闆呴粦", 10F); + this.cmbTaskKind.Items.AddRange(new object[] { + "Loop", + "BranchPoint", + "JoinPoint"}); + this.cmbTaskKind.Location = new System.Drawing.Point(131, 47); + this.cmbTaskKind.Name = "cmbTaskKind"; + this.cmbTaskKind.Size = new System.Drawing.Size(366, 31); + this.cmbTaskKind.TabIndex = 2; + // + // lblCurrent + // + this.lblCurrent.Dock = System.Windows.Forms.DockStyle.Fill; + this.lblCurrent.Font = new System.Drawing.Font("寰蒋闆呴粦", 10F); + this.lblCurrent.Location = new System.Drawing.Point(11, 80); + this.lblCurrent.Name = "lblCurrent"; + this.lblCurrent.Size = new System.Drawing.Size(114, 36); + this.lblCurrent.TabIndex = 3; + this.lblCurrent.Text = "褰撳墠绔欑偣锛"; + this.lblCurrent.TextAlign = System.Drawing.ContentAlignment.MiddleRight; + // + // numCurrent + // + this.numCurrent.Dock = System.Windows.Forms.DockStyle.Left; + this.numCurrent.Font = new System.Drawing.Font("寰蒋闆呴粦", 10F); + this.numCurrent.Location = new System.Drawing.Point(131, 83); + this.numCurrent.Maximum = new decimal(new int[] { + 1000000, + 0, + 0, + 0}); + this.numCurrent.Name = "numCurrent"; + this.numCurrent.Size = new System.Drawing.Size(120, 29); + this.numCurrent.TabIndex = 4; + // + // lblTarget + // + this.lblTarget.Dock = System.Windows.Forms.DockStyle.Fill; + this.lblTarget.Font = new System.Drawing.Font("寰蒋闆呴粦", 10F); + this.lblTarget.Location = new System.Drawing.Point(11, 116); + this.lblTarget.Name = "lblTarget"; + this.lblTarget.Size = new System.Drawing.Size(114, 36); + this.lblTarget.TabIndex = 5; + this.lblTarget.Text = "鐩爣绔欑偣锛"; + this.lblTarget.TextAlign = System.Drawing.ContentAlignment.MiddleRight; + // + // numTarget + // + this.numTarget.Dock = System.Windows.Forms.DockStyle.Left; + this.numTarget.Font = new System.Drawing.Font("寰蒋闆呴粦", 10F); + this.numTarget.Location = new System.Drawing.Point(131, 119); + this.numTarget.Maximum = new decimal(new int[] { + 1000000, + 0, + 0, + 0}); + this.numTarget.Name = "numTarget"; + this.numTarget.Size = new System.Drawing.Size(120, 29); + this.numTarget.TabIndex = 6; + // + // lblTraffic + // + this.lblTraffic.Dock = System.Windows.Forms.DockStyle.Fill; + this.lblTraffic.Font = new System.Drawing.Font("寰蒋闆呴粦", 10F); + this.lblTraffic.Location = new System.Drawing.Point(11, 152); + this.lblTraffic.Name = "lblTraffic"; + this.lblTraffic.Size = new System.Drawing.Size(114, 36); + this.lblTraffic.TabIndex = 7; + this.lblTraffic.Text = "娴侀噺鎺у埗锛"; + this.lblTraffic.TextAlign = System.Drawing.ContentAlignment.MiddleRight; + // + // numTraffic + // + this.numTraffic.Dock = System.Windows.Forms.DockStyle.Left; + this.numTraffic.Font = new System.Drawing.Font("寰蒋闆呴粦", 10F); + this.numTraffic.Location = new System.Drawing.Point(131, 155); + this.numTraffic.Maximum = new decimal(new int[] { + 1000, + 0, + 0, + 0}); + this.numTraffic.Name = "numTraffic"; + this.numTraffic.Size = new System.Drawing.Size(120, 29); + this.numTraffic.TabIndex = 8; + // + // lblPriority + // + this.lblPriority.Dock = System.Windows.Forms.DockStyle.Fill; + this.lblPriority.Font = new System.Drawing.Font("寰蒋闆呴粦", 10F); + this.lblPriority.Location = new System.Drawing.Point(11, 188); + this.lblPriority.Name = "lblPriority"; + this.lblPriority.Size = new System.Drawing.Size(114, 36); + this.lblPriority.TabIndex = 9; + this.lblPriority.Text = "浼樺厛绾э細"; + this.lblPriority.TextAlign = System.Drawing.ContentAlignment.MiddleRight; + // + // numPriority + // + this.numPriority.Dock = System.Windows.Forms.DockStyle.Left; + this.numPriority.Font = new System.Drawing.Font("寰蒋闆呴粦", 10F); + this.numPriority.Location = new System.Drawing.Point(131, 191); + this.numPriority.Name = "numPriority"; + this.numPriority.Size = new System.Drawing.Size(120, 29); + this.numPriority.TabIndex = 10; + this.numPriority.Value = new decimal(new int[] { + 1, + 0, + 0, + 0}); + // + // lblVia + // + this.lblVia.Dock = System.Windows.Forms.DockStyle.Fill; + this.lblVia.Font = new System.Drawing.Font("寰蒋闆呴粦", 10F); + this.lblVia.Location = new System.Drawing.Point(11, 224); + this.lblVia.Name = "lblVia"; + this.lblVia.Size = new System.Drawing.Size(114, 36); + this.lblVia.TabIndex = 11; + this.lblVia.Text = "閫斿緞鐐癸細"; + this.lblVia.TextAlign = System.Drawing.ContentAlignment.MiddleRight; + // + // chkViaPoint + // + this.chkViaPoint.Dock = System.Windows.Forms.DockStyle.Left; + this.chkViaPoint.Font = new System.Drawing.Font("寰蒋闆呴粦", 10F); + this.chkViaPoint.Location = new System.Drawing.Point(131, 227); + this.chkViaPoint.Name = "chkViaPoint"; + this.chkViaPoint.Size = new System.Drawing.Size(104, 30); + this.chkViaPoint.TabIndex = 12; + this.chkViaPoint.Text = "鏄"; + // + // lblStartType + // + this.lblStartType.Dock = System.Windows.Forms.DockStyle.Fill; + this.lblStartType.Font = new System.Drawing.Font("寰蒋闆呴粦", 10F); + this.lblStartType.Location = new System.Drawing.Point(11, 260); + this.lblStartType.Name = "lblStartType"; + this.lblStartType.Size = new System.Drawing.Size(114, 36); + this.lblStartType.TabIndex = 13; + this.lblStartType.Text = "鍚姩绫诲瀷锛"; + this.lblStartType.TextAlign = System.Drawing.ContentAlignment.MiddleRight; + // + // cmbStartType + // + this.cmbStartType.Dock = System.Windows.Forms.DockStyle.Fill; + this.cmbStartType.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.cmbStartType.Font = new System.Drawing.Font("寰蒋闆呴粦", 10F); + this.cmbStartType.Items.AddRange(new object[] { + "Api", + "Plc", + "ButtonBox", + "AutoLoop", + "Charge"}); + this.cmbStartType.Location = new System.Drawing.Point(131, 263); + this.cmbStartType.Name = "cmbStartType"; + this.cmbStartType.Size = new System.Drawing.Size(366, 31); + this.cmbStartType.TabIndex = 14; + // + // flpButtons + // + this.flpButtons.AutoSize = true; + this.flpButtons.Controls.Add(this.btnSave); + this.flpButtons.Controls.Add(this.btnCancel); + this.flpButtons.Dock = System.Windows.Forms.DockStyle.Left; + this.flpButtons.Location = new System.Drawing.Point(131, 299); + this.flpButtons.Name = "flpButtons"; + this.flpButtons.Size = new System.Drawing.Size(292, 262); + this.flpButtons.TabIndex = 15; + // + // btnSave + // + this.btnSave.BackColor = System.Drawing.Color.LightBlue; + this.btnSave.FlatStyle = System.Windows.Forms.FlatStyle.Flat; + this.btnSave.Font = new System.Drawing.Font("寰蒋闆呴粦", 11F, System.Drawing.FontStyle.Bold); + this.btnSave.Location = new System.Drawing.Point(3, 3); + this.btnSave.Name = "btnSave"; + this.btnSave.Size = new System.Drawing.Size(140, 40); + this.btnSave.TabIndex = 0; + this.btnSave.Text = "淇濆瓨"; + this.btnSave.UseVisualStyleBackColor = false; + this.btnSave.Click += new System.EventHandler(this.btnSave_Click); + // + // btnCancel + // + this.btnCancel.BackColor = System.Drawing.SystemColors.Control; + this.btnCancel.FlatStyle = System.Windows.Forms.FlatStyle.Flat; + this.btnCancel.Font = new System.Drawing.Font("寰蒋闆呴粦", 11F); + this.btnCancel.Location = new System.Drawing.Point(149, 3); + this.btnCancel.Name = "btnCancel"; + this.btnCancel.Size = new System.Drawing.Size(140, 40); + this.btnCancel.TabIndex = 1; + this.btnCancel.Text = "鍙栨秷"; + this.btnCancel.UseVisualStyleBackColor = false; + this.btnCancel.Click += new System.EventHandler(this.btnCancel_Click); + // + // btnEdit + // + this.btnEdit.Location = new System.Drawing.Point(0, 0); + this.btnEdit.Name = "btnEdit"; + this.btnEdit.Size = new System.Drawing.Size(75, 23); + this.btnEdit.TabIndex = 0; + this.btnEdit.Visible = false; + // + // btnDelete + // + this.btnDelete.Location = new System.Drawing.Point(0, 0); + this.btnDelete.Name = "btnDelete"; + this.btnDelete.Size = new System.Drawing.Size(75, 23); + this.btnDelete.TabIndex = 0; + this.btnDelete.Visible = false; + // + // LoopViewer + // + this.ClientSize = new System.Drawing.Size(1200, 600); + this.Controls.Add(this.splitContainer); + this.Font = new System.Drawing.Font("寰蒋闆呴粦", 9F); + this.MinimumSize = new System.Drawing.Size(1000, 420); + this.Name = "LoopViewer"; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen; + this.Text = "浠诲姟鍒楄〃绠$悊鍣"; + this.splitContainer.Panel1.ResumeLayout(false); + this.splitContainer.Panel2.ResumeLayout(false); + ((System.ComponentModel.ISupportInitialize)(this.splitContainer)).EndInit(); + this.splitContainer.ResumeLayout(false); + this.pnlMiddle.ResumeLayout(false); + this.flpMiddle.ResumeLayout(false); + this.grpEdit.ResumeLayout(false); + this.tlpEdit.ResumeLayout(false); + this.tlpEdit.PerformLayout(); + ((System.ComponentModel.ISupportInitialize)(this.numCurrent)).EndInit(); + ((System.ComponentModel.ISupportInitialize)(this.numTarget)).EndInit(); + ((System.ComponentModel.ISupportInitialize)(this.numTraffic)).EndInit(); + ((System.ComponentModel.ISupportInitialize)(this.numPriority)).EndInit(); + this.flpButtons.ResumeLayout(false); + this.ResumeLayout(false); + + } + } +} \ No newline at end of file diff --git a/StandardScene.Core/Chained/LoopViewer.cs b/StandardScene.Core/Chained/LoopViewer.cs new file mode 100644 index 0000000..7a81bc4 --- /dev/null +++ b/StandardScene.Core/Chained/LoopViewer.cs @@ -0,0 +1,595 @@ +锘縰sing Newtonsoft.Json; +using StandardScene.Model; +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Drawing; +using System.IO; +using System.Linq; +using System.Windows.Forms; + + +namespace LoopViewerApp +{ + public partial class LoopViewer : Form + { + private readonly string jsonPath = + Path.Combine(Application.StartupPath, "tasklist.json"); + + private List tasks = new List(); + + // -1 琛ㄧず鏂板妯″紡锛>=0 琛ㄧず姝e湪缂栬緫瀵瑰簲绱㈠紩 + private int editingIndex = -1; + + public LoopViewer() + { + InitializeComponent(); + + if (LicenseManager.UsageMode == LicenseUsageMode.Designtime) + return; + + // 搴旂敤 ChargeStationManagementForm 椋庢牸鐨勮繍琛屾椂鏍峰紡璋冩暣 + ApplyChargeStyle(); + + EnsureComboItems(); + + // 鍚敤澶氶夊苟缁戝畾鍙抽敭鑿滃崟涓 Delete 閿垹闄ゅ姛鑳 + try + { + if (lstTasks != null) + { + lstTasks.MultiSelect = true; + + // 鍙抽敭鑿滃崟锛氬垹闄 + var ctx = new ContextMenuStrip(); + ctx.Items.Add("鍒犻櫎", null, (s, e) => OnDeleteSelectedTasks()); + lstTasks.ContextMenuStrip = ctx; + + // 閿洏鍒犻櫎閿粦瀹 + lstTasks.KeyDown += lstTasks_KeyDown; + } + } + catch (Exception ex) + { + System.Diagnostics.Debug.WriteLine($"LoopViewer context menu init error: {ex}"); + } + + try + { + InitOrLoadJson(); + RenderListView(); + UpdateSaveButtonText(); + } + catch (Exception ex) + { + System.Diagnostics.Debug.WriteLine($"LoopViewer initialization error: {ex}"); + } + } + + private void lstTasks_KeyDown(object sender, KeyEventArgs e) + { + try + { + if (e.KeyCode == Keys.Delete) + { + OnDeleteSelectedTasks(); + e.Handled = true; + } + } + catch (Exception ex) + { + System.Diagnostics.Debug.WriteLine($"lstTasks_KeyDown error: {ex}"); + } + } + + /// + /// 鍒犻櫎 ListView 涓変腑鐨勪换鍔★紙鏀寔澶氶夛級 + /// + private void OnDeleteSelectedTasks() + { + try + { + if (lstTasks == null || lstTasks.SelectedIndices.Count == 0) + return; + + // 鏀堕泦琚変腑鐨勭储寮曞苟鎸夐檷搴忓垹闄わ紝閬垮厤绱㈠紩绉诲姩闂 + var selectedIndices = lstTasks.SelectedIndices.Cast().OrderByDescending(i => i).ToList(); + + // 鏋勯犵‘璁ゆ彁绀 + string prompt; + if (selectedIndices.Count == 1) + { + int idx = selectedIndices[0]; + if (idx >= 0 && idx < tasks.Count) + prompt = $"纭鍒犻櫎浠诲姟 ID={tasks[idx].Id}锛"; + else + prompt = "纭鍒犻櫎閫変腑浠诲姟锛"; + } + else + { + prompt = $"纭鍒犻櫎鎵閫 {selectedIndices.Count} 涓换鍔★紵"; + } + + if (MessageBox.Show(prompt, "纭", MessageBoxButtons.YesNo, MessageBoxIcon.Question) != DialogResult.Yes) + return; + + // 鍒犻櫎浠诲姟 + foreach (var idx in selectedIndices) + { + if (idx >= 0 && idx < tasks.Count) + { + tasks.RemoveAt(idx); + } + } + + // 濡傛灉琚垹闄ら」鍖呭惈褰撳墠姝e湪缂栬緫鐨勯」锛岄鍑虹紪杈戠姸鎬 + if (editingIndex >= 0) + { + if (editingIndex >= tasks.Count || selectedIndices.Any(i => i == editingIndex)) + { + editingIndex = -1; + UpdateSaveButtonText(); + ClearPanelInputs(); + } + else + { + // 閲嶆柊璁$畻缂栬緫绱㈠紩鍦ㄥ垹闄ゅ悗鐨勬柊浣嶇疆 + int removedBefore = selectedIndices.Count(i => i < editingIndex); + editingIndex -= removedBefore; + } + } + + // 鎸佷箙鍖栧苟鍒锋柊鍒楄〃瑙嗗浘 + Save(); + RenderListView(); + } + catch (Exception ex) + { + System.Diagnostics.Debug.WriteLine($"OnDeleteSelectedTasks error: {ex}"); + MessageBox.Show("鍒犻櫎澶辫触锛" + ex.Message); + } + } + + /// + /// 灏 LoopViewer 鐨勮繍琛屾椂鏍峰紡璋冩暣涓轰笌 ChargeStationManagementForm 鎺ヨ繎鐨勮瑙夐鏍硷細 + /// - 鍏ㄥ眬瀛椾綋璁句负寰蒋闆呴粦 + /// - 琛ㄥご鏆栬壊鏇挎崲涓鸿摑鑹叉矇绋抽鏍硷紙鍜屽厖鐢电晫闈竴鑷达級 + /// - 鎸夐挳瀛楀彿銆佽儗鏅壊涓庡厖鐢电晫闈繚鎸佷竴鑷达紙淇濆瓨/鍒犻櫎/鍙栨秷锛 + /// - 鍒楄〃瑙嗗浘璁剧疆涓烘暣琛岄夋嫨銆佹棤杈规銆佷氦鏇胯儗鏅瓑 + /// 娉ㄦ剰锛氫笉淇敼 Designer 鏂囦欢锛屼粎鍦ㄨ繍琛屾椂缁熶竴鎺т欢琛ㄧ幇锛岄伩鍏嶇牬鍧忚璁″櫒鐢熸垚浠g爜銆 + /// + private void ApplyChargeStyle() + { + try + { + // 绐椾綋绾ц缃 + this.StartPosition = FormStartPosition.CenterScreen; + this.MinimumSize = new System.Drawing.Size(1327, 738); + this.Font = new Font("寰蒋闆呴粦", 9F, FontStyle.Regular); + + // 璋冩暣 ListView锛堝鏋滃瓨鍦級 + if (lstTasks != null) + { + lstTasks.View = View.Details; + lstTasks.FullRowSelect = true; + lstTasks.GridLines = false; + lstTasks.HeaderStyle = ColumnHeaderStyle.Nonclickable; + lstTasks.OwnerDraw = true; // 宸叉湁鑷畾涔夌粯鍒 + lstTasks.BackColor = Color.White; + lstTasks.ForeColor = Color.FromArgb(33, 33, 33); + // 澶氶夌敱鍒濆鍖栨椂鎺у埗锛堣繖閲屼笉寮哄埗锛 + } + + // 涓嬫媺妗嗙粺涓瀛椾綋 + if (cmbTaskKind != null) cmbTaskKind.Font = new Font("寰蒋闆呴粦", 10F, FontStyle.Regular); + if (cmbStartType != null) cmbStartType.Font = new Font("寰蒋闆呴粦", 10F, FontStyle.Regular); + + // 鏁板艰緭鍏ユ缁熶竴瀛椾綋 + if (numCurrent != null) numCurrent.Font = new Font("寰蒋闆呴粦", 10F, FontStyle.Regular); + if (numTarget != null) numTarget.Font = new Font("寰蒋闆呴粦", 10F, FontStyle.Regular); + if (numTraffic != null) numTraffic.Font = new Font("寰蒋闆呴粦", 10F, FontStyle.Regular); + if (numPriority != null) numPriority.Font = new Font("寰蒋闆呴粦", 10F, FontStyle.Regular); + + // 鏍囩瀛椾綋缁熶竴 + if (lblEditingId != null) lblEditingId.Font = new Font("寰蒋闆呴粦", 10F, FontStyle.Bold); + + // 鎸夐挳椋庢牸锛氫笌 ChargeStationManagementForm 淇濇寔涓鑷寸殑瑙嗚浼樺厛绾 + if (btnSave != null) + { + btnSave.BackColor = Color.LightBlue; + btnSave.ForeColor = Color.Black; + btnSave.Font = new Font("寰蒋闆呴粦", 11F, FontStyle.Bold); + btnSave.FlatStyle = FlatStyle.Flat; + } + if (btnDelete != null) + { + btnDelete.BackColor = Color.LightCoral; + btnDelete.ForeColor = Color.Black; + btnDelete.Font = new Font("寰蒋闆呴粦", 11F, FontStyle.Bold); + btnDelete.FlatStyle = FlatStyle.Flat; + } + if (btnCancel != null) + { + btnCancel.BackColor = SystemColors.Control; + btnCancel.ForeColor = Color.Black; + btnCancel.Font = new Font("寰蒋闆呴粦", 11F, FontStyle.Regular); + btnCancel.FlatStyle = FlatStyle.Flat; + } + + // 濡傛灉瀛樺湪棰濆鐨勬搷浣滄寜閽紙渚嬪鍦ㄩ潰鏉夸笂锛夛紝灏濊瘯缁熶竴椋庢牸锛堝閿欙級 + foreach (Control ctrl in this.Controls) + { + if (ctrl is Panel pnl) + { + pnl.Padding = new Padding(12); + } + else if (ctrl is Button btn) + { + // 宸茶缃富瑕佹寜閽紝鍏朵粬鎸夐挳浣跨敤涓ч鏍 + if (btn == btnSave || btn == btnDelete || btn == btnCancel) continue; + btn.Font = new Font("寰蒋闆呴粦", 10F, FontStyle.Regular); + } + } + } + catch (Exception ex) + { + System.Diagnostics.Debug.WriteLine($"ApplyChargeStyle error: {ex}"); + } + } + + private void EnsureComboItems() + { + try + { + if (cmbTaskKind != null && cmbTaskKind.Items.Count == 0) + { + cmbTaskKind.Items.AddRange(new object[] { "Loop", "BranchPoint", "JoinPoint" }); + cmbTaskKind.SelectedIndex = 0; + } + + if (cmbStartType != null && cmbStartType.Items.Count == 0) + { + cmbStartType.Items.AddRange(new object[] { "Api", "Plc", "ButtonBox", "AutoLoop" }); + cmbStartType.SelectedIndex = 3; + } + + // 纭繚涓嬫媺妗嗗瓧浣撲竴鑷达紙闃叉 Designer 鏈缃級 + if (cmbTaskKind != null) cmbTaskKind.Font = new Font("寰蒋闆呴粦", 10F, FontStyle.Regular); + if (cmbStartType != null) cmbStartType.Font = new Font("寰蒋闆呴粦", 10F, FontStyle.Regular); + } + catch { } + } + + #region 鍒濆鍖/鍔犺浇 + private void InitOrLoadJson() + { + try + { + if (!File.Exists(jsonPath)) + File.WriteAllText(jsonPath, "[]"); + + var text = File.ReadAllText(jsonPath); + tasks = JsonConvert.DeserializeObject>(text) ?? new List(); + } + catch (Exception ex) + { + tasks = new List(); + System.Diagnostics.Debug.WriteLine($"Load tasks failed: {ex}"); + } + } + #endregion + + #region ID 鑷閫昏緫 + + /// + /// 鑾峰彇涓嬩竴涓彲鐢ㄧ殑浠诲姟ID锛堝綋鍓嶆渶澶D + 1锛 + /// + /// 鏂扮殑浠诲姟ID + private int GetNextTaskId() + { + if (tasks == null || tasks.Count == 0) + return 1; + + int maxId = tasks.Max(t => t.Id); + return maxId + 1; + } + + #endregion + + #region OwnerDraw 缁樺埗锛堝凡鎸夎姹傦細琛ㄥご鍔犵矖榛戝瓧 + 閱掔洰搴曡壊锛岄変腑琛屼负鍙︿竴绉嶉鑹诧級 + private void lstTasks_DrawColumnHeader(object sender, DrawListViewColumnHeaderEventArgs e) + { + try + { + // 涓 ChargeStationManagementForm 琛ㄥご淇濇寔涓鑷寸殑娣辫摑鑳屾櫙涓庣櫧鑹插姞绮楀瓧浣 + using (var backBrush = new SolidBrush(Color.FromArgb(63, 81, 181))) // 娣辫摑锛堜笌 Charge 鐣岄潰涓鑷达級 + using (var textBrush = new SolidBrush(Color.White)) // 鐧借壊鏂囧瓧 + using (var font = new Font("寰蒋闆呴粦", 9, FontStyle.Bold)) + { + e.Graphics.FillRectangle(backBrush, e.Bounds); + var sf = new StringFormat { LineAlignment = StringAlignment.Center, Alignment = StringAlignment.Near }; + var rect = e.Bounds; + rect.Inflate(-8, 0); + e.Graphics.DrawString(e.Header.Text, font, textBrush, rect, sf); + + // 鍒嗛殧绾 + using (var pen = new Pen(Color.FromArgb(200, 200, 200))) + { + e.Graphics.DrawLine(pen, e.Bounds.Left, e.Bounds.Bottom - 1, e.Bounds.Right, e.Bounds.Bottom - 1); + } + } + } + catch + { + e.DrawBackground(); + e.DrawText(); + } + } + + private void lstTasks_DrawItem(object sender, DrawListViewItemEventArgs e) + { + // 鐢 DrawSubItem 缁樺埗鍏ㄩ儴鍐呭浠ヤ繚璇佹瘡鍒楀榻 + } + + private void lstTasks_DrawSubItem(object sender, DrawListViewSubItemEventArgs e) + { + try + { + var item = e.Item; + bool selected = item.Selected; + Rectangle bounds = e.Bounds; + + // 閫変腑琛岄鑹诧細涓 ChargeStationManagementForm 淇濇寔涓鑷寸殑钃濊壊寮鸿皟 + Color selectedBack = Color.FromArgb(0, 120, 215); + Color selectedFore = Color.White; + + // 闈為変腑琛屼氦鏇胯儗鏅 + Color evenBack = Color.White; + Color oddBack = Color.FromArgb(250, 251, 253); + Color normalFore = Color.FromArgb(33, 33, 33); + + // 濉厖鑳屾櫙 + if (selected) + { + using (var selBrush = new SolidBrush(selectedBack)) + { + e.Graphics.FillRectangle(selBrush, bounds); + } + } + else + { + using (var back = new SolidBrush(e.ItemIndex % 2 == 0 ? evenBack : oddBack)) + { + e.Graphics.FillRectangle(back, bounds); + } + } + + // 缁樺埗鏂囨湰锛堝姞涓鐐瑰唴杈硅窛锛 + string text = e.SubItem.Text ?? string.Empty; + Color fore = selected ? selectedFore : normalFore; + TextFormatFlags flags = TextFormatFlags.Left | TextFormatFlags.VerticalCenter; + Rectangle textRect = bounds; + textRect.Inflate(-6, 0); + + using (var font = new Font("寰蒋闆呴粦", 9)) + { + TextRenderer.DrawText(e.Graphics, text, font, textRect, fore, flags); + } + } + catch + { + e.DrawBackground(); + e.DrawText(); + } + } + #endregion + + #region 娓叉煋/淇濆瓨 + private void RenderListView() + { + try + { + if (lstTasks == null) return; + lstTasks.BeginUpdate(); + lstTasks.Items.Clear(); + foreach (var t in tasks) + { + var lvi = new ListViewItem(new[] + { + t.Id.ToString(), // ID 鍒 + t.Kind.ToString(), + t.CurrentStationId.ToString(), + t.TargetStationId.ToString(), + t.TrafficControl.ToString(), + t.Priority.ToString(), + t.IsViaPoint ? "鏄" : "鍚", + t.StartType.ToString() + }); + lstTasks.Items.Add(lvi); + } + lstTasks.EndUpdate(); + } + catch (Exception ex) + { + System.Diagnostics.Debug.WriteLine($"RenderListView failed: {ex}"); + } + } + + private void Save() + { + try + { + File.WriteAllText(jsonPath, JsonConvert.SerializeObject(tasks, Formatting.Indented)); + } + catch (Exception ex) + { + MessageBox.Show("淇濆瓨澶辫触锛" + ex.Message); + } + } + #endregion + + #region 鎸夐挳浜嬩欢锛堝湪鍚屼竴鐣岄潰鏂板/缂栬緫锛 + private void UpdateSaveButtonText() + { + if (btnSave != null) + { + // 鏂囨鍥哄畾涓"淇濆瓨" + btnSave.Text = "淇濆瓨"; + } + } + + private void btnSave_Click(object sender, EventArgs e) + { + try + { + // 浠庨潰鏉胯鍙栧硷紝鐩存帴鍦ㄧ晫闈㈠唴缂栬緫/鏂板 + Enum.TryParse(cmbTaskKind?.SelectedItem?.ToString() ?? "Loop", out var kind); + Enum.TryParse(cmbStartType?.SelectedItem?.ToString() ?? "AutoLoop", out var st); + + if (editingIndex >= 0 && editingIndex < tasks.Count) + { + // 鏇存柊妯″紡锛氫繚鐣欏師鏈塈D + var existingTask = tasks[editingIndex]; + existingTask.Kind = kind; + existingTask.CurrentStationId = (int)(numCurrent?.Value ?? 0); + existingTask.TargetStationId = (int)(numTarget?.Value ?? 0); + existingTask.TrafficControl = (int)(numTraffic?.Value ?? 0); + existingTask.Priority = (int)(numPriority?.Value ?? 1); + existingTask.IsViaPoint = chkViaPoint?.Checked ?? false; + existingTask.StartType = st; + } + else + { + // 鏂板妯″紡锛氳嚜鍔ㄥ垎閰嶆柊ID + var t = new LoopTask + { + Id = GetNextTaskId(), // 鑷ID + Kind = kind, + CurrentStationId = (int)(numCurrent?.Value ?? 0), + TargetStationId = (int)(numTarget?.Value ?? 0), + TrafficControl = (int)(numTraffic?.Value ?? 0), + Priority = (int)(numPriority?.Value ?? 1), + IsViaPoint = chkViaPoint?.Checked ?? false, + StartType = st + }; + tasks.Add(t); + } + + Save(); + RenderListView(); + // 鎭㈠鏂板鐘舵 + editingIndex = -1; + UpdateSaveButtonText(); + ClearPanelInputs(); + } + catch (Exception ex) + { + System.Diagnostics.Debug.WriteLine($"btnSave_Click error: {ex}"); + MessageBox.Show("鎿嶄綔澶辫触锛" + ex.Message); + } + } + + private void btnCancel_Click(object sender, EventArgs e) + { + // 鍙栨秷缂栬緫锛屾竻绌洪潰鏉垮苟鍥炲埌"娣诲姞"妯″紡 + editingIndex = -1; + UpdateSaveButtonText(); + ClearPanelInputs(); + } + + private void btnEdit_Click(object sender, EventArgs e) + { + try + { + if (lstTasks == null || lstTasks.SelectedIndices.Count == 0) return; + int idx = lstTasks.SelectedIndices[0]; + if (idx < 0 || idx >= tasks.Count) return; + + editingIndex = idx; + LoadTaskToPanel(tasks[idx]); + UpdateSaveButtonText(); + } + catch (Exception ex) + { + System.Diagnostics.Debug.WriteLine($"btnEdit_Click error: {ex}"); + } + } + + private void btnDelete_Click(object sender, EventArgs e) + { + // 鍏煎鏃х殑鍒犻櫎鎸夐挳锛氬鐢ㄧ粺涓鍒犻櫎閫昏緫 + OnDeleteSelectedTasks(); + } + #endregion + + #region 鍙屽嚮缂栬緫锛堝悓闈㈡澘锛 + private void lstTasks_MouseDoubleClick(object sender, MouseEventArgs e) + { + try + { + if (lstTasks == null) return; + var item = lstTasks.GetItemAt(e.X, e.Y); + if (item == null) return; + int idx = item.Index; + if (idx < 0 || idx >= tasks.Count) return; + + editingIndex = idx; + LoadTaskToPanel(tasks[idx]); + UpdateSaveButtonText(); + } + catch (Exception ex) + { + System.Diagnostics.Debug.WriteLine($"lstTasks_MouseDoubleClick error: {ex}"); + } + } + #endregion + + #region 杈呭姪锛氶潰鏉胯鍐 + private void LoadTaskToPanel(LoopTask t) + { + if (t == null) return; + try + { + // 鏄剧ず褰撳墠缂栬緫鐨勪换鍔D锛堝彧璇绘樉绀猴級 + if (lblEditingId != null) lblEditingId.Text = $"缂栬緫浠诲姟 ID: {t.Id}"; + + if (cmbTaskKind != null) cmbTaskKind.SelectedItem = t.Kind.ToString(); + if (numCurrent != null) numCurrent.Value = Math.Max(numCurrent.Minimum, Math.Min(numCurrent.Maximum, t.CurrentStationId)); + if (numTarget != null) numTarget.Value = Math.Max(numTarget.Minimum, Math.Min(numTarget.Maximum, t.TargetStationId)); + if (numTraffic != null) numTraffic.Value = Math.Max(numTraffic.Minimum, Math.Min(numTraffic.Maximum, t.TrafficControl)); + if (numPriority != null) numPriority.Value = Math.Max(numPriority.Minimum, Math.Min(numPriority.Maximum, t.Priority)); + if (chkViaPoint != null) chkViaPoint.Checked = t.IsViaPoint; + if (cmbStartType != null) cmbStartType.SelectedItem = t.StartType.ToString(); + } + catch (Exception ex) + { + System.Diagnostics.Debug.WriteLine($"LoadTaskToPanel error: {ex}"); + } + } + + private void ClearPanelInputs() + { + try + { + // 娓呴櫎缂栬緫ID鏄剧ず + if (lblEditingId != null) lblEditingId.Text = "鏂板浠诲姟"; + + if (cmbTaskKind != null) cmbTaskKind.SelectedIndex = 0; + if (numCurrent != null) numCurrent.Value = 0; + if (numTarget != null) numTarget.Value = 0; + if (numTraffic != null) numTraffic.Value = 0; + if (numPriority != null) numPriority.Value = 1; + if (chkViaPoint != null) chkViaPoint.Checked = false; + if (cmbStartType != null) cmbStartType.SelectedIndex = 3; + } + catch (Exception ex) + { + System.Diagnostics.Debug.WriteLine($"ClearPanelInputs error: {ex}"); + } + } + + #endregion + + + } +} \ No newline at end of file diff --git a/StandardScene.Core/Chained/LoopViewer.resx b/StandardScene.Core/Chained/LoopViewer.resx new file mode 100644 index 0000000..1af7de1 --- /dev/null +++ b/StandardScene.Core/Chained/LoopViewer.resx @@ -0,0 +1,120 @@ +锘 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/StandardScene.Core/Chained/TransportDeliveryCallbacks.cs b/StandardScene.Core/Chained/TransportDeliveryCallbacks.cs new file mode 100644 index 0000000..ff19cb4 --- /dev/null +++ b/StandardScene.Core/Chained/TransportDeliveryCallbacks.cs @@ -0,0 +1,206 @@ +using SimpleCore; +using SimpleCore.Library; +using StandardScene.Utils; +using System; +using System.Linq; +using System.Net.Http; +using System.Net.Http.Headers; +using System.Threading.Tasks; +using StandardScene.Model; + +namespace StandardScene.Chained +{ + /// + /// 缁熶竴绠$悊 TransportDelivery 鐩稿叧鐨勪换鍔″洖璋冩柟娉曪紝骞堕氳繃鍥炶皟娉ㄥ唽琛ㄦ彁渚涘彲鎭㈠鎬с + /// + public static class TransportDeliveryCallbacks + { + public const string KeyOnStarted = "Transport.OnMissionStarted"; + public const string KeyOnFetched = "Transport.OnFetched"; + public const string KeyOnPut = "Transport.OnPut"; + public const string KeyOnFinished = "Transport.OnMissionFinished"; + public const string KeyOnFailed = "Transport.OnMissionFailed"; + public const string KeyOnTerminated = "Transport.OnMissionTerminated"; + + private static bool _initialized; + private static readonly HttpClient _httpClient = new HttpClient(); + private static string _callbackUrl = "http://127.0.0.1:20101/api/v1/MDCS/State"; + + static TransportDeliveryCallbacks() + { + if (_initialized) return; + _initialized = true; + + DeliveryCallbackRegistry.RegisterOnStart (KeyOnStarted, OnMissionStarted); + DeliveryCallbackRegistry.RegisterDoneFetch (KeyOnFetched, OnFetched); + DeliveryCallbackRegistry.RegisterDonePut (KeyOnPut, OnPut); + DeliveryCallbackRegistry.RegisterDoneMission(KeyOnFinished, OnMissionFinished); + DeliveryCallbackRegistry.RegisterFailed (KeyOnFailed, OnMissionFailed); + DeliveryCallbackRegistry.RegisterOnTerminated(KeyOnTerminated, OnMissionTerminated); + } + + /// + /// 鏄惧紡璋冪敤浠ョ‘淇濋潤鎬佹瀯閫犲嚱鏁板凡鎵ц锛堟敞鍐屾墍鏈夐粯璁ゅ洖璋冿級銆 + /// + public static void EnsureInitialized() + { + // 璁块棶鏈被锛岀‘淇濋潤鎬佹瀯閫犲凡鎵ц + if (!_initialized) + { + // 瑙﹀彂 static ctor锛圕LR 淇濊瘉绾跨▼瀹夊叏锛 + System.Runtime.CompilerServices.RuntimeHelpers.RunClassConstructor(typeof(TransportDeliveryCallbacks).TypeHandle); + } + } + + /// + /// 閰嶇疆浠诲姟鐘舵佸洖璋冪殑瀹屾暣 URL锛堜緥濡 http://ip:port/api/v1/MDCS/State锛夈 + /// 寤鸿鍦 TransportMission 鍚姩鎴栨瀯閫犳椂璋冪敤涓娆° + /// + public static void ConfigureCallbackUrl(string url) + { + if (!string.IsNullOrWhiteSpace(url)) + { + _callbackUrl = url; + } + } + + private static void DispatchMissionState(TransportDelivery d, MissionState.MissionStateEnum state) + { + var ms = new MissionState + { + MissionId = d.TaskId, + CarCode = d.UsingCar?.id.ToString() ?? "0", + TriggerTime = DateTime.Now, + State = state + }; + MissionStatePost(ms); + } + + private static async void MissionStatePost(MissionState ms) + { + var retryCount = 10; + var retryDelay = TimeSpan.FromSeconds(1); + var content = new StringContent(ms.ToJson()); + content.Headers.ContentType = new MediaTypeHeaderValue("application/json"); + + for (int i = 0; i < retryCount; i++) + { + try + { + var resp = await _httpClient.PostAsync(_callbackUrl, content); + Diagnosis.Post($"{ms.State}鍥炶皟缁撴灉 => {resp.Content.ReadAsStringAsync().Result}"); + var body = resp.Content.ReadAsStringAsync().Result.JsonTo(); + var result = body.Code == 200 ? "鎴愬姛" : "澶辫触"; + Diagnosis.Post($"杞﹁締缂栧彿锛歿ms.CarCode} 浠诲姟id锛歿ms.MissionId} 鐘舵佸洖璋冩墽琛寋result}"); + break; + } + catch (Exception ex) + { + Diagnosis.Log($"{ms.State}鐘舵佸洖璋冨け璐 => ex:{ex}"); + if (i == retryCount - 1) + { + Diagnosis.Log($"StringContent:{content}", "apiError"); + } + await Task.Delay(retryDelay); + } + } + } + + public static async void OnMissionStarted(ChainedDeliveryMission.Delivery delivery) + { + var d = (TransportDelivery)delivery; + Diagnosis.Log($"task(#{d.TaskId}) started"); + + if (!string.IsNullOrWhiteSpace(d.TaskIdsString)) + { + Commons.AddOrUpdateTag(d.UsingCar.tags, "taskIdsString", d.TaskIdsString); + Diagnosis.Log( + $"holdCarTagAddOrUpdate--TaskId:{d.TaskId},task:[{d.ToJson()}].taskIdsString:{d.TaskIdsString}", + "holdCarTag", + true); + } + + Commons.DeleteTag(d.UsingCar.tags, "holdCar"); + if (!string.IsNullOrEmpty(d.HoldCarSite)) + { + Commons.AddOrUpdateTag(d.UsingCar.tags, "holdCar", d.HoldCarSite); + Diagnosis.Log( + $"holdCarTagAddOrUpdate--TaskId:{d.TaskId},task:[{d.ToJson()}].holdCar:{d.HoldCarSite}", + "holdCarTag", + true); + } + + DispatchMissionState(d, MissionState.MissionStateEnum.Started); + } + + public static async void OnFetched(ChainedDeliveryMission.Delivery delivery) + { + var d = (TransportDelivery)delivery; + if (d.Src == d.Dst) return; + + Diagnosis.Log($"task(#{d.TaskId}) fetched"); + DispatchMissionState(d, MissionState.MissionStateEnum.Fetched); + } + + public static async void OnPut(ChainedDeliveryMission.Delivery delivery) + { + var d = (TransportDelivery)delivery; + if (d.Src == d.Dst) return; + + Diagnosis.Log($"task(#{d.TaskId}) put"); + DispatchMissionState(d, MissionState.MissionStateEnum.Put); + } + + public static async void OnMissionFailed(ChainedDeliveryMission.Delivery delivery) + { + var d = (TransportDelivery)delivery; + Diagnosis.Log($"task(#{d.TaskId}) failed"); + DispatchMissionState(d, MissionState.MissionStateEnum.Failed); + } + + public static async void OnMissionFinished(ChainedDeliveryMission.Delivery delivery) + { + var d = (TransportDelivery)delivery; + Diagnosis.Log($"task(#{d.TaskId}) finished"); + + if (!string.IsNullOrWhiteSpace(d.MGTaskCode!) && d.MGTaskCode.Contains("-")) + { + var phase = d.MGTaskCode.Split('-')[1]; + var last = int.Parse(phase) - 1; + if (last > 0 && string.IsNullOrWhiteSpace(d.TaskIdsString)) + { + var holdCar = SimpleLib.GetAllCars() + .FirstOrDefault(e => e.tags.TryGetValue("taskIdsString", out var v) && v.Contains(d.TaskId)); + if (holdCar != null) + { + Commons.DeleteTag(holdCar.tags, "taskIdsString"); + Diagnosis.Log( + $"holdCarTagDelete-2锛歄nMissionFinished--TaskId:{d.TaskId},task:[{d.ToJson()}].taskIdsString:{d.TaskIdsString}", + "holdCarTag", + true); + } + } + } + + DispatchMissionState(d, MissionState.MissionStateEnum.Finished); + } + + public static async Task OnMissionTerminated(ChainedDeliveryMission.Delivery delivery, string ISRelease) + { + try + { + using var hc = new HttpClient(); + var task = await hc.GetAsync($"http://{delivery.UsingCar.address}:8008/car/startOrPause?ISRelease={ISRelease}"); + var resp = task.Content.ReadAsStringAsync().Result.JsonTo(); + var result = resp.Code == 200 ? "鎴愬姛" : "澶辫触"; + return 1; + } + catch (Exception ex) + { + Diagnosis.Log($"鏆傚仠鎭㈠澶辫触 => ex:{ExceptionFormatter.FormatEx(ex)}"); + return 0; + } + } + } +} + diff --git a/StandardScene.Core/Chained/TransportMission.cs b/StandardScene.Core/Chained/TransportMission.cs new file mode 100644 index 0000000..84f9386 --- /dev/null +++ b/StandardScene.Core/Chained/TransportMission.cs @@ -0,0 +1,599 @@ +using Newtonsoft.Json; +using SimpleLite; +using SimpleLite.RCS; +using SimpleLite.RCS.CarTypes; +using SimpleLite.RCS.Signal; +using SimpleLite.CADTools; +using SimpleLite.Props; +using SimpleLite.UI; +using SimpleCore; +using SimpleCore.Compiler; +using SimpleCore.Library; +using SimpleCore.PropType; +using StandardScene.Model; +using StandardScene; +using StandardScene.Utils; +using System; +using System.Collections.Generic; +using System.Drawing; +using System.IO; +using System.Linq; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using System.Windows.Forms; +using Microsoft.Win32.SafeHandles; + +namespace StandardScene.Chained +{ + /// + /// 浠诲姟绫诲瀷鏋氫妇 + /// + public enum TaskType + { + 绉诲姩 = 1, // 绉诲姩浠诲姟锛堝綋鍓嶆湭浣跨敤锛 + 鎼繍 = 2, // 鎼繍浠诲姟 + 瑁呰溅 = 3, // 瑁呰溅浠诲姟 + 鍗歌溅 = 4, // 鍗歌溅浠诲姟 + } + + /// + /// 杩愯緭浠诲姟绫 + /// 缁ф壙鑷狝bstractDelivery锛屽疄鐜板叿浣撶殑杩愯緭浠诲姟鍔熻兘 + /// + /// public class TransportDelivery : AbstractChainedDeliveryMission.AbstractDelivery + public class TransportDelivery : ChainedDeliveryMission.Delivery + { + /// 浠诲姟绫诲瀷锛堟灇涓惧硷級 + public TaskType Type; + /// 浠诲姟绫诲瀷锛堝瓧绗︿覆褰㈠紡锛 + public string TaskType; + /// 浣跨敤鐨勫皬杞﹀悕绉 + public string UsingCarName = "/"; + /// 鐗╂枡淇℃伅 + public string Material = ""; + /// 鍗犺溅绔欑偣 + public string HoldCarSite = ""; + /// 闃舵浠诲姟浠g爜锛堢敤浜庢爣璁伴樁娈典换鍔★級 + public string MGTaskCode = ""; + /// 浠诲姟ID瀛楃涓诧紙鐢ㄤ簬鏍囪闃舵浠诲姟锛屽彲鍖呭惈澶氫釜浠诲姟ID锛岀敤閫楀彿鍒嗛殧锛 + public string TaskIdsString = ""; + + /// + /// 鏋勯犲嚱鏁 + /// 娉ㄥ唽浠诲姟鐢熷懡鍛ㄦ湡鍥炶皟鍑芥暟 + /// + public TransportDelivery() + { + // 娉ㄥ唽浠诲姟寮濮嬪洖璋 + OnStart += d => { TransportOnStart((TransportDelivery)d); }; + // 娉ㄥ唽鍙栬揣瀹屾垚鍥炶皟 + DoneFetch += d => { TransportDoneFetch((TransportDelivery)d); }; + // 娉ㄥ唽鏀捐揣瀹屾垚鍥炶皟 + DonePut += d => { TransportDonePut((TransportDelivery)d); }; + // 娉ㄥ唽浠诲姟瀹屾垚鍥炶皟 + DoneMission += d => { TransportDoneMission((TransportDelivery)d); }; + } + + /// + /// 浠诲姟寮濮嬫椂鐨勫洖璋冨鐞 + /// + /// 杩愯緭浠诲姟瀵硅薄 + private void TransportOnStart(TransportDelivery d) + { + Diagnosis.Post($"{d.TaskId}___{d.Src}->{d.Dst}: step-onStart"); + } + + /// + /// 鍙栬揣瀹屾垚鏃剁殑鍥炶皟澶勭悊 + /// + /// 杩愯緭浠诲姟瀵硅薄 + private void TransportDoneFetch(TransportDelivery d) + { + Diagnosis.Post($"{d.TaskId}___{d.Src}->{d.Dst}: step-doneFetch"); + } + + /// + /// 鏀捐揣瀹屾垚鏃剁殑鍥炶皟澶勭悊 + /// + /// 杩愯緭浠诲姟瀵硅薄 + private void TransportDonePut(TransportDelivery d) + { + Diagnosis.Post($"{d.TaskId}___{d.Src}->{d.Dst}: step-donePut"); + } + + + /// + /// 浠诲姟瀹屾垚鏃剁殑鍥炶皟澶勭悊 + /// + /// 杩愯緭浠诲姟瀵硅薄 + private void TransportDoneMission(TransportDelivery d) + { + Diagnosis.Post($"{d.TaskId}___{d.Src}->{d.Dst}: step-doneMission"); + } + } + + /// + /// 鎼繍浠诲姟杩涚▼绫 + /// 缁ф壙鑷狝bstractChainedDeliveryMission锛屽疄鐜板叿浣撶殑鎼繍浠诲姟璋冨害 + /// + [MissionType(Name = "鎼繍浠诲姟杩涚▼", editor = typeof(TransportMission))] + internal class TransportMission : ChainedDeliveryMission + { + /// 鏄惁鍚敤灏辫繎浠诲姟绛栫暐锛"1"=鍚敤锛"0"=绂佺敤锛 + private static readonly string NearestTask = "1"; + + /// 浠诲姟鏌ョ湅鍣ㄧ獥鍙e璞 + private DeliveryViewer dv; + + public TransportMission() + { + // 纭繚鍥炶皟闈欐佺被宸插畬鎴愭敞鍐 + TransportDeliveryCallbacks.EnsureInitialized(); + + // 鏍规嵁閰嶇疆鐨 MissionCallbackURL 璁剧疆鍥炶皟鍦板潃锛堣嫢鏈夛級 + var p = Param; + if (p != null && !string.IsNullOrWhiteSpace(p.MissionCallbackURL)) + { + if (Commons.IsValidHttpUrl(p.MissionCallbackURL)) + { + _lastCallbackUrl = p.MissionCallbackURL; + TransportDeliveryCallbacks.ConfigureCallbackUrl(_lastCallbackUrl); + } + else + { + Diagnosis.Log($"鏃犳晥鐨 MissionCallbackURL 閰嶇疆: {p.MissionCallbackURL}", "TransportMission"); + } + } + } + + public class TransportMissionParam + { + /// + /// 閫掗佷换鍔$姸鎬佸洖璋冨湴鍧銆 + /// + public string MissionCallbackURL; + } + + public TransportMissionParam Param => StringDictConvert.Convert(fields); + + private readonly bool _onDisplay = true; + private string _lastCallbackUrl; + + protected override Delivery CreateDeliveryFromSnapshot(DeliveryStateSnapshot snap) + { + var usingCar = SimpleLib.GetCar(snap.UsingCarId); + if (snap.UsingCarId != -1 && usingCar == null) + { + Diagnosis.Post($"CreateDeliveryFromSnapshot[{snap.Id}] failed:usingCar[{snap.UsingCarId}] not found"); + return null; + } + if (usingCar != null && usingCar is not Car) + { + Diagnosis.Post($"CreateDeliveryFromSnapshot[{snap.Id}] skipped:usingCar[{snap.UsingCarId}] is not Car type"); + return null; + } + + var d = new TransportDelivery + { + Id = snap.Id, + TaskId = snap.TaskId ?? string.Empty, + Src = snap.Src, + Dst = snap.Dst, + SkipFetch = snap.SkipFetch, + SkipPut = snap.SkipPut, + Putting = snap.Putting, + CreateTime = snap.CreateTime, + StartTime = snap.StartTime, + FinishTime = snap.FinishTime, + CarType = snap.CarType, + UsingCar = (Car)usingCar + }; + + if (usingCar != null && string.Equals(snap.Status, "putting", StringComparison.OrdinalIgnoreCase) && !CheckCarLoaded(usingCar)) + { + d.Error = true; + } + + // 鎭㈠鍥炶皟閰嶇疆涓庝簨浠剁粦瀹 + d.ReportOnStarted = snap.ReportOnStarted; + d.ReportOnFetched = snap.ReportOnFetched; + d.ReportOnPut = snap.ReportOnPut; + d.ReportOnFinished = snap.ReportOnFinished; + d.ReportOnFailed = snap.ReportOnFailed; + d.ReportOnTerminated = snap.ReportOnTerminated; + + d.OnStartCallbackKeys = snap.OnStartCallbackKeys ?? new List(); + d.DoneFetchCallbackKeys = snap.DoneFetchCallbackKeys ?? new List(); + d.DonePutCallbackKeys = snap.DonePutCallbackKeys ?? new List(); + d.DoneMissionCallbackKeys = snap.DoneMissionCallbackKeys ?? new List(); + d.FailedCallbackKeys = snap.FailedCallbackKeys ?? new List(); + d.OnTerminatedCallbackKeys = snap.OnTerminatedCallbackKeys ?? new List(); + + // 缁熶竴閫氳繃 Attacher 鎸傝浇鎵鏈夊洖璋 + DeliveryCallbackAttacher.AttachAll(d); + + return d; + } + + protected override bool CheckCarLoaded(AbstractCar car) + { + if(car is DummyCar) + return true; + var carLoaded = Commons.GetCarStatus((Car)car, "Loaded"); + if (!string.IsNullOrEmpty(carLoaded) && bool.TryParse(carLoaded, out var result)) + return result; + return false; + } + + /// + /// 鎵ц鏂规硶锛堝惎鍔ㄤ换鍔¤皟搴﹁繘绋嬶級 + /// 鍚姩鍩虹被鐨勪换鍔¤皟搴﹀惊鐜紝骞跺惎鍔ㄧ姸鎬佹樉绀虹嚎绋 + /// + public override void Execute() + { + base.Execute(); // 璋冪敤鍩虹被Execute鏂规硶锛屽惎鍔ㄤ换鍔¤皟搴﹀惊鐜 + //HideSimpleConsole(); // 闅愯棌鎺у埗鍙扮獥鍙 + + // 鍚姩鐘舵佹樉绀虹嚎绋嬶紝瀹炴椂鏄剧ず灏忚溅鐘舵佸拰浠诲姟鎵ц鎯呭喌 + new Thread(() => + { + while (true) + { + Thread.Sleep(100); // 姣100ms鏇存柊涓娆℃樉绀 + var painter = SimpleMonitor.getPainter("TransportMissionPainter"); + painter.clear(); + if (!_onDisplay) continue; // 濡傛灉鏈惎鐢ㄦ樉绀猴紝璺宠繃 + + // 妫鏌ュ苟鏇存柊浠诲姟鐘舵佸洖璋 URL锛堝鏋滈厤缃彂鐢熷彉鍖栵級 + var p = Param; + var currentUrl = p?.MissionCallbackURL; + if (!string.IsNullOrWhiteSpace(currentUrl) && currentUrl != _lastCallbackUrl) + { + // 浣跨敤 Commons 涓殑缁熶竴 URL 鏍¢獙閫昏緫 + if (Commons.IsValidHttpUrl(currentUrl)) + { + TransportDeliveryCallbacks.ConfigureCallbackUrl(currentUrl); + _lastCallbackUrl = currentUrl; + } + else + { + Diagnosis.Log($"鏃犳晥鐨 MissionCallbackURL 閰嶇疆: {currentUrl}", "TransportMission"); + } + } + + var onMissionCnt = 0; // 姝e湪鎵ц浠诲姟鐨勫皬杞︽暟閲 + var carStr = $"{string.Join("\n", SimpleLib.GetAllCars().Select(cc => + { + var status = ""; + if (cc.tags.TryGetValue("occupied", out var occupiedStr)) + { + onMissionCnt++; + status = occupiedStr; + if (cc.tags.TryGetValue("idle", out var _)) + { + Diagnosis.Post($"strange idle tag, {cc.name}({cc.id})"); + cc.tags.Remove("idle"); + + } + } + else if (cc.tags.TryGetValue("idle", out var idleStr)) status = DateTime.TryParse(idleStr, out var idleTime) ? $"idle:{idleTime}" : $"idle:{idleStr}"; + return $"{cc.name}({cc.id})\t{status}"; + }))}"; + //var waitingMissions = GetDeliveries() + // .Where(dd => dd.GetStatus() == DeliveryStatus.Waiting).ToList(); + painter.drawTextFixed( + $"灏忚溅锛歕t寮鍔ㄧ巼锛歿onMissionCnt}/{SimpleLib.GetAllCars().Length}\n{carStr}\n\n", + new SolidBrush(Color.Black), + VirtualPainter.DrawPosition.LeftTop, Color.AliceBlue); + painter = null; + } + }).Start(); + } + + /// + /// 鍙樻洿浠诲姟浼樺厛绾э紙閲嶅啓鍩虹被鏂规硶锛 + /// 鏍规嵁閰嶇疆鍐冲畾鏄惁鎵ц灏辫繎浠诲姟绛栫暐 + /// + public override void ChangePriority() + { + try + { + if (NearestTask == "1") // NearestTask 绛変簬1 鎵ц灏辫繎鍘熷垯 + { + // 灏辫繎浠诲姟鎵ц锛堝綋鍓嶈娉ㄩ噴锛屾湭鍚敤锛 + // NearestTaskExecute(); + } + else + { + // 濡傛灉鏈惎鐢ㄥ氨杩戝師鍒欙紝绉婚櫎鎵鏈夊皬杞︾殑灏辫繎閫夎溅鏍囪 + foreach (var car in SimpleLib.GetAllCars().OfType()) + { + Commons.DeleteTag(car.tags, "changePriority"); + } + } + + } + catch (Exception ex) + { + Console.WriteLine("鍚屼竴宸ュ簭灏辫繎鎵ц灏辫繎浠诲姟鎵ц锛" + ex.ToString()); + + } + } + + /// + /// 涓閿垵濮嬪寲鎵鏈夊皬杞 + /// 灏嗙姸鎬佷负"姝e父"鎴"涓婄嚎"涓旀湭鍒濆鍖栫殑灏忚溅杩涜閲嶇疆 + /// + [MethodMember(Name = "涓閿垵濮嬪寲")] + public void ResetAll() + { + foreach (var car in SimpleLib.GetAllCars().OfType().Where(c => c.lstatus.Contains("姝e父") || c.lstatus.Contains("涓婄嚎"))) + { + // 濡傛灉灏忚溅鏈垵濮嬪寲锛圙etLastSite杩斿洖-1锛夛紝鎵ц閲嶇疆 + if (car.GetLastSite() == -1) + ((Car)car).Reset(); + + } + } + + /// + /// 涓閿笂绾挎墍鏈夊皬杞 + /// 灏嗙姸鎬佷负"姝e父"鎴"涓婄嚎"涓斿凡鍒濆鍖栫殑灏忚溅鏍囪涓哄湪绾跨姸鎬 + /// + [MethodMember(Name = "涓閿笂绾")] + public void OnlineAll() + { + foreach (var car in SimpleLib.GetAllCars().OfType().Where(c => c.lstatus.Contains("姝e父") || c.lstatus.Contains("涓婄嚎"))) + { + // 濡傛灉灏忚溅宸插垵濮嬪寲锛屾爣璁颁负鍦ㄧ嚎 + if (car.GetLastSite() != -1) + { + Commons.AddOrUpdateTag(car.tags, "Online", "true"); + } + } + } + + /// + /// 鏌ョ湅浠诲姟鍒楄〃鐣岄潰 + /// 鎵撳紑浠诲姟鏌ョ湅鍣ㄧ獥鍙o紝鏄剧ず鎵鏈変换鍔$殑鐘舵 + /// + [MethodMember(Name = "鏌ョ湅浠诲姟", Description = "鏄剧ず鐣岄潰")] + public void Print() + { + dv = new DeliveryViewer(); + dv.Show(); + } + + /// + /// 鎵嬪姩鍒涘缓鍗曞彇璐т换鍔 + /// 閫氳繃UI浜や簰閫夋嫨灏忚溅鍜屽彇璐х偣锛屽垱寤轰粎鍙栬揣浠诲姟锛堣烦杩囧彇璐ф楠わ紝灏忚溅浠庡綋鍓嶄綅缃Щ鍔ㄥ埌鍙栬揣鐐癸級 + /// + [MethodMember(Name = "鍗曞彇璐", Description = "澧炲姞骞舵帓闃熸惉杩愪换鍔¢摼")] + public async void ManualEnqueueFetch() + { + try + { + G.pushStatus("閫夋嫨灏忚溅"); + var selected = SimpleMonitor.selected.ToArray(); + if (selected.Length == 0) { MessageBox.Show("璇峰厛閫夋嫨闇瑕佹帶鍒剁殑灏忚溅锛"); return; } + var obj = selected[0]; + if (obj is Car car) + { + G.pushStatus("閫夋嫨鍙栬揣浣"); + // 绛夊緟鐢ㄦ埛鍦║I涓夋嫨鍙栬揣鐐 + var pt1 = await Program.UI.getPoint(new UIOps.getPointOptions() { site = true }); + + // 鍒涘缓杩愯緭浠诲姟锛氫粠灏忚溅褰撳墠浣嶇疆鍒伴夋嫨鐨勫彇璐х偣 + var d = new TransportDelivery + { + CarType = "Car", + Src = car.GetLastSite(), // 璧峰鐐逛负灏忚溅褰撳墠浣嶇疆 + Dst = pt1.site, // 鐩爣鐐逛负閫夋嫨鐨勫彇璐х偣 + SkipFetch = true, // 璺宠繃鍙栬揣姝ラ锛堝洜涓哄皬杞﹀凡缁忓湪璧峰鐐癸級 + UsingCar = car, // 鎸囧畾浣跨敤鐨勫皬杞 + PutPlanInfo = new() { { "action", "fetch" } }, // 璺緞鍔ㄤ綔璁剧疆涓篺etch + }; + + G.pushStatus($"鎺掑簭浜嗕竴涓獅car.name}鎼繍浠诲姟{d.Id}: {car.GetLastSite()} -> {d.Dst}"); + Enqueue(d); + } + else + { + MessageBox.Show("璇烽夋嫨闇瑕佹帶鍒剁殑灏忚溅锛"); + } + } + catch + { + G.pushStatus($"缁撴潫浠诲姟閾"); + } + } + + //鍐欎竴涓啋娉℃帓搴忕殑绠楁硶锛屾寜鐓ц窛绂绘帓搴 + + [MethodMember(Name = "鍒囨崲鍚庡彴鏄剧ず")] + public void SwitchBackgroundDisplay() + { + ShowSimpleConsole(); + } + /// + /// 鎵嬪姩鍒涘缓鍗曟斁璐т换鍔 + /// 閫氳繃UI浜や簰閫夋嫨灏忚溅鍜屾斁璐х偣锛屽垱寤轰粎鏀捐揣浠诲姟锛堝皬杞︿粠褰撳墠浣嶇疆绉诲姩鍒版斁璐х偣骞舵斁璐э級 + /// + [MethodMember(Name = "鍗曟斁璐", Description = "澧炲姞骞舵帓闃熸惉杩愪换鍔¢摼")] + public async void ManualEnqueuePut() + { + try + { + G.pushStatus("閫夋嫨灏忚溅"); + var selected = SimpleMonitor.selected.ToArray(); + if (selected.Length == 0) { MessageBox.Show("璇峰厛閫夋嫨闇瑕佹帶鍒剁殑灏忚溅锛"); return; } + var obj = selected[0]; + if (obj is Car car) + { + G.pushStatus("閫夋嫨鍙栬揣浣"); + // 绛夊緟鐢ㄦ埛鍦║I涓夋嫨鏀捐揣鐐癸紙娉ㄩ噴鍐欑殑鏄"鍙栬揣浣"浣嗗疄闄呮槸鏀捐揣鐐癸級 + var pt1 = await Program.UI.getPoint(new UIOps.getPointOptions() { site = true }); + var srcSite = SimpleLib.GetSite(pt1.site); + + // 鍒涘缓杩愯緭浠诲姟锛氫粠灏忚溅褰撳墠浣嶇疆鍒伴夋嫨鐨勬斁璐х偣 + var d = new TransportDelivery + { + CarType = "Car", + Src = car.GetLastSite(), // 璧峰鐐逛负灏忚溅褰撳墠浣嶇疆 + Dst = pt1.site, // 鐩爣鐐逛负閫夋嫨鐨勬斁璐х偣 + UsingCar = car, // 鎸囧畾浣跨敤鐨勫皬杞 + SkipFetch = true, // 璺宠繃鍙栬揣姝ラ + PutPlanInfo = new() { { "action", "put" } }, // 璺緞鍔ㄤ綔璁剧疆涓簆ut + + }; + G.pushStatus($"鎺掑簭浜嗕竴涓獅car.name}鎼繍浠诲姟{d.Id}: {srcSite.id} -> {d.Dst}"); + Enqueue(d); + } + else + { + MessageBox.Show("璇烽夋嫨闇瑕佹帶鍒剁殑灏忚溅锛"); + } + } + catch + { + G.pushStatus($"缁撴潫浠诲姟閾"); + } + } + + /// + /// 鎵嬪姩鍒涘缓瀹屾暣鐨勫彇鏀捐揣浠诲姟 + /// 閫氳繃UI浜や簰閫夋嫨鍙栬揣鐐瑰拰鏀捐揣鐐癸紝鍒涘缓瀹屾暣鐨勬惉杩愪换鍔★紙鍙栬揣->鏀捐揣锛 + /// + [MethodMember(Name = "鍙栨斁璐", Description = "澧炲姞骞舵帓闃熸惉杩愪换鍔¢摼")] + public async void ManualEnqueue() + { + try + { + G.pushStatus("閫夋嫨鍙栬揣浣"); + // 绛夊緟鐢ㄦ埛鍦║I涓夋嫨鍙栬揣鐐 + var pt1 = await Program.UI.getPoint(new UIOps.getPointOptions() { site = true }); + var srcSite = SimpleLib.GetSite(pt1.site); + + G.pushStatus("閫夋嫨鏀捐揣浣"); + // 绛夊緟鐢ㄦ埛鍦║I涓夋嫨鏀捐揣鐐 + var pt2 = await Program.UI.getPoint(new UIOps.getPointOptions() { site = true }); + var dstSite = SimpleLib.GetSite(pt2.site); + + // 鍒涘缓瀹屾暣鐨勮繍杈撲换鍔★細浠庡彇璐х偣鍒版斁璐х偣 + var d = new TransportDelivery + { + CarType = "Car", + Src = srcSite.id, // 鍙栬揣鐐 + Dst = dstSite.id, // 鏀捐揣鐐 + TaskId = $"ManualTask-{DateTime.Now}" // 鐢熸垚浠诲姟ID + }; + + G.pushStatus($"鎺掑簭浜嗕竴涓弶杞︽惉杩愪换鍔d.Id}: {srcSite.id} -> {dstSite.id}"); + Enqueue(d); + } + catch + { + G.pushStatus($"缁撴潫浠诲姟閾"); + } + } + + /// + /// 鎵嬪姩鍒涘缓绉诲姩浠诲姟锛堝幓鏌愬湴锛 + /// 閫氳繃UI浜や簰閫夋嫨鐩爣绔欑偣锛屽垱寤虹Щ鍔ㄤ换鍔★紙璧风偣鍜岀粓鐐圭浉鍚岋紝鍙Щ鍔ㄤ笉鏀捐揣锛 + /// + [MethodMember(Name = "鍘绘煇鍦", Description = "鍘绘煇鍦")] + public async void ManualEnqueueGo() + { + try + { + G.pushStatus("閫夋嫨鍓嶅線鐨勭珯鐐"); + // 绛夊緟鐢ㄦ埛鍦║I涓夋嫨鐩爣绔欑偣 + var pt1 = await Program.UI.getPoint(new UIOps.getPointOptions() { site = true }); + var srcSite = SimpleLib.GetSite(pt1.site); + + // 鍒涘缓绉诲姩浠诲姟锛氳捣鐐瑰拰缁堢偣鐩稿悓锛堝彧绉诲姩鍒扮洰鏍囩偣锛屼笉鎵ц鍙栨斁璐ф搷浣滐級 + var d = new TransportDelivery + { + CarType = "Car", + Src = pt1.site, // 璧风偣锛堝疄闄呬笉浼氬彇璐э級 + Dst = pt1.site, // 缁堢偣锛堝疄闄呬笉浼氭斁璐э級 + TaskId = $"ManualTask-{DateTime.Now}", + SrcFindLoop = false, // 璧风偣涓嶆煡鎵惧洖璺 + DstFindLoop = false // 缁堢偣涓嶆煡鎵惧洖璺 + }; + + G.pushStatus($"鎺掑簭浜嗕竴涓弶杞︽惉杩愪换鍔d.Id}: {srcSite.id} -> {srcSite.id}"); + Enqueue(d); + } + catch + { + G.pushStatus($"缁撴潫浠诲姟閾"); + } + } + + /// + /// 渚涙寜閽洅杩涚▼鍙嶅皠璋冪敤锛欱uttonMission.ExecuteButtonActionInternal 鍙嶅皠璋冪敤鏈柟娉曞悗锛 + /// 鐢 HandleMethodResult 瑙f瀽杩斿洖鍊硷紝椹卞姩 OnActionExecuted 鎴愬姛/澶辫触鍙嶉銆 + /// 杩斿洖 true 琛ㄧず宸插叆闃燂紱false 琛ㄧず绔欑偣鏃犳晥銆傝嫢 Enqueue 鎶涢敊锛岀敱鎸夐挳杩涚▼鎹曡幏鍚庡悓鏍疯涓哄け璐ャ + /// 鎸夐挳閰嶇疆绀轰緥锛歍riggerMission=TransportMission锛孴riggerMethod=EnqueueTransportByButton锛孴riggerMethodParams=鍙栬揣绔欑偣ID,鏀捐揣绔欑偣ID + /// + /// 鍙栬揣绔欑偣 ID + /// 鏀捐揣绔欑偣 ID + public bool EnqueueTransportByButton(int srcSiteId, int dstSiteId) + { + if (SimpleLib.GetSite(srcSiteId) == null || SimpleLib.GetSite(dstSiteId) == null) + { + Diagnosis.Log($"鎸夐挳鎺掗槦鎼繍澶辫触锛氱珯鐐逛笉瀛樺湪 src={srcSiteId}, dst={dstSiteId}", "TransportMission", true); + return false; + } + + var d = new TransportDelivery + { + CarType = "Car", + Src = srcSiteId, + Dst = dstSiteId, + TaskId = $"Button-{DateTime.Now:yyyyMMddHHmmssfff}" + }; + Enqueue(d); + Diagnosis.Post($"鎸夐挳鎺掗槦鎼繍 {d.Id}: {srcSiteId} -> {dstSiteId}"); + return true; + } + + // ========== 鎺у埗鍙扮獥鍙f樉绀烘帶鍒 ========== + + /// Windows API锛氳幏鍙栨帶鍒跺彴绐楀彛鍙ユ焺 + [DllImport("kernel32.dll")] + static extern IntPtr GetConsoleWindow(); + + /// Windows API锛氭樉绀/闅愯棌绐楀彛 + [DllImport("user32.dll")] + static extern bool ShowWindow(IntPtr hWnd, int nCmdShow); + + /// 鎺у埗鍙版樉绀虹姸鎬佹爣蹇 + private static bool ShowConsole = false; + + /// + /// 鍒囨崲鎺у埗鍙扮獥鍙f樉绀/闅愯棌 + /// + public static void ShowSimpleConsole() + { + ShowConsole = !ShowConsole; // 鍒囨崲鏄剧ず鐘舵 + var handle = GetConsoleWindow(); + int n = ShowConsole ? 0 : 5; // 0=鏄剧ず锛5=闅愯棌 + Console.WriteLine(n); + ShowWindow(handle, n); + } + + /// + /// 闅愯棌鎺у埗鍙扮獥鍙 + /// + public static void HideSimpleConsole() + { + var handle = GetConsoleWindow(); + ShowWindow(handle, 0); // 0琛ㄧず闅愯棌绐楀彛 + } + } + +} diff --git a/StandardScene.Core/Charge/AbstractChargeLogicMission.cs b/StandardScene.Core/Charge/AbstractChargeLogicMission.cs new file mode 100644 index 0000000..e019d82 --- /dev/null +++ b/StandardScene.Core/Charge/AbstractChargeLogicMission.cs @@ -0,0 +1,1378 @@ +using Newtonsoft.Json; +using SimpleLite; +using SimpleLite.RCS; +using SimpleLite.RCS.CarTypes; +using SimpleLite.CADTools; +using SimpleLite.Props; +using SimpleLite.UI; +using SimpleCore; +using SimpleCore.Compiler; +using SimpleCore.Library; +using SimpleCore.PropType; +using StandardScene.Chained; +using StandardScene.InterLock; +using StandardScene.Utils; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using StandardScene.Model; +using static StandardScene.Chained.ChainedDeliveryMission; + +namespace StandardScene.Charge +{ + #region Enums and Status Classes + + /// + /// 鍏呯數鍐崇瓥鏋氫妇 + /// + public enum ChargeDecision + { + MustCharge, + AvailableToTask, + NoCharge, + } + + /// + /// 鍏呯數浠诲姟鐘舵佺被 + /// + public class AbstractChargeMissionStatus : MissionStatus + { + #region 杩愯鏃剁姸鎬 + + /// 绌洪棽鍏呯數妗╂暟閲 + public int freeChargeCount; + + /// 褰撳墠鏈浣庣數閲忚溅杈嗙殑SOC + public float lowerSocCar; + + /// 绌洪棽杞﹁締姣斾緥 + public double freeCarPercent; + + #endregion + + #region 鐢甸噺闃堝奸厤缃 + + /// 蹇呴』鍏呯數鐢甸噺闃堝硷紙浣庝簬姝ゅ煎繀椤诲厖鐢碉級 + public double mustChargeSoc; + + /// 绌洪棽鍏呯數鐢甸噺闃堝 + public double idleChargeSoc; + + /// 浠诲姟鍙敤鐢甸噺闃堝硷紙楂樹簬姝ゅ煎彲鎵ц浠诲姟锛 + public double taskAvailableSoc; + + /// 鍏佽鎵撴柇鍏呯數鐨勬渶浣庣數閲 + public double allowInterruptSoc; + + /// 婊$數闃堝 + public double fullChargeSoc; + + #endregion + + #region 鏃堕棿閰嶇疆 + + /// 绌洪棽鍚庤Е鍙戝厖鐢电殑绛夊緟绉掓暟 + public double idleChargeSeconds; + + /// 蹇呴』鍏呯數鐨勬渶鐭鏁 + public double mustChargeSeconds; + + /// 绌洪棽鍚庤Е鍙戝洖寰呭懡鐨勭瓑寰呯鏁 + public double idleSeconds; + + /// 琛ョ數淇濇寔鍒嗛挓鏁帮紙鍏呭埌100%鍚庣户缁繚鎸侊級 + public double topUpMinutes = 5; + + #endregion + + #region 绛栫暐寮鍏 + + /// 鏈灏忓厑璁哥┖闂茶溅鍘诲厖鐢电殑浠诲姟鏁 + public int minAllowFreeCarToChargeTaskCnt; + + /// 鍏佽鍏呯數鏁伴噺 + public int allowChargeCnt; + + /// 鏄惁鍏佽鎵撴柇鍏呯數鍘绘墽琛屼换鍔 + public bool allowInterruptTask; + + /// 鏄惁浣跨敤鏈浣庣數閲忎紭鍏堝厖鐢 + public bool useLowerSocForCharge; + + /// 浣庣數閲忚溅杈嗘崲鍏呴槇鍊煎樊 + public int lowPowerCarSwapChangeThresholdDelta = 15; + + /// 鏄惁鍚敤鍏呯數寮傚父妫娴 + public bool enableErrorChargeDetection = false; + + /// 鏄惁浣跨敤鍏呯數绔欒繃婊ゅ櫒 + public bool useChargeSiteFilter = false; + + #endregion + } + + /// + /// 鍗曚釜杞﹁締澶勭悊杩囩▼涓殑涓婁笅鏂囦俊鎭 + /// + public class CarChargeContext + { + /// 褰撳墠澶勭悊鐨勮溅杈 + public Car Car { get; set; } + + /// 褰撳墠鐢甸噺鐧惧垎姣 + public double Soc { get; set; } + + /// 褰撳墠鍏呯數鐢垫祦 + public double ElectricCurrent { get; set; } + + /// 鏄惁蹇呴』鍏呯數 + public bool MustCharge { get; set; } + + /// 鏄惁搴旇鍘诲厖鐢 + public bool ShouldGoCharge { get; set; } + + /// 鏄惁搴旇鍘诲緟鍛界偣 + public bool ShouldGoStandby { get; set; } + + /// 杞﹁締鏄惁鍦ㄥ緟鍛界偣 + public bool CarInStandbySite { get; set; } + + /// 杞﹁締鏄惁鍦ㄥ厖鐢电珯 + public bool CarInChargeSite { get; set; } + + /// 鍏呯數鍚庢槸鍚﹀彲鎵ц浠诲姟 + public bool ChargingTaskAvailable { get; set; } + + /// 绛夊緟鎵ц鐨勪换鍔℃暟閲 + public int WaitingMissions { get; set; } + + /// 绌洪棽寮濮嬫椂闂 + public DateTime IdleTime { get; set; } + + /// 鐩爣绫诲瀷锛1鍏呯數锛2寰呭懡 + public int TargetType { get; set; } + + /// 鐩爣璺緞瑙勫垝 + public SegmentPlan TargetPlan { get; set; } + } + + #endregion + + /// + /// 鎶借薄鍏呯數浠诲姟鍩虹被 + /// 璐熻矗绠$悊杞﹁締鐨勮嚜鍔ㄥ厖鐢靛拰寰呭懡璋冨害 + /// + public class AbstractChargeLogiceMission : AbstractInterlockMission + { + #region Constants + + /// 涓诲惊鐜棿闅旓紙姣锛 + protected const int LOOP_INTERVAL_MS = 500; + + /// 蹇呴』鍏呯數瑙﹀彂寤惰繜锛堟绉掞級 + protected const int MUST_CHARGE_TRIGGER_DELAY_MS = 5000; + + /// 鐩爣绫诲瀷锛氬厖鐢 + protected const int TARGET_TYPE_CHARGE = 1; + + /// 鐩爣绫诲瀷锛氬緟鍛 + protected const int TARGET_TYPE_STANDBY = 2; + + /// 鏈澶т笉鍙揪绔欑偣璁板綍鏁 + protected const int MAX_UNDELIVERABLE_SITES = 5; + + /// 瑙﹀彂浠诲姟鎵撴柇鐨勬渶灏忕瓑寰呬换鍔℃暟 + protected const int MAX_WAIT_WAITMISSION = 1; + + /// 琛ョ數鐩稿叧鏍囩鍚嶇О + private static readonly string[] TopUpTags = { "topup", "topupInProgress", "topupStart", "topupEnd", "topupMinutes", "topupRequested" }; + + #endregion + + #region Fields and Properties + + public override MissionStatus status { get; set; } = new AbstractChargeMissionStatus(); + + [JsonIgnore] private protected Thread myThread; + [JsonIgnore] public Func chargeStrategy = null; + [JsonIgnore] public bool started = false; + + [JsonIgnore] private int _interruptingCarId = -1; + [JsonIgnore] private int _bestInterruptionCandidateId = -1; + [JsonIgnore] private readonly object _interruptLock = new object(); + + /// 鑾峰彇鐘舵佸璞$殑绫诲瀷杞崲 + protected AbstractChargeMissionStatus AcmStatus => (AbstractChargeMissionStatus)status; + + #endregion + + #region Virtual Methods - 鎵╁睍鐐 + + /// + /// 灏忚溅鍒拌揪鍏呯數绔欐垨寰呭懡鐐规椂鐨勫姩浣 + /// + public virtual void ArriveAction(Car car, Site site) { } + + /// + /// 灏忚溅绂诲紑鍏呯數绔欐垨寰呭懡鐐规椂鐨勫姩浣 + /// + public virtual void LeaveAction(Car car, Site site) { } + + /// + /// 鏍规嵁灏忚溅鑾峰彇瀵瑰簲杞︽墍鍏佽鍋滈潬鐨勪紤鎭偣绫诲瀷 + /// + public virtual string GetStandbyType(AbstractCar car) => "standby"; + + /// + /// 鏍规嵁灏忚溅鑾峰彇瀵瑰簲杞︽墍鍏佽鍋滈潬鐨勫厖鐢电偣绫诲瀷 + /// + public virtual string GetChargeType(AbstractCar car) => "Charge"; + + /// + /// 鑾峰彇灏忚溅瀵瑰簲杞﹀瀷寰呮墽琛岀殑浠诲姟鏁伴噺 + /// + public virtual int WaitingMissions(AbstractCar car) + { + var waitingDeliveries = SimpleProject.proj.Missions + .OfType() + .First() + .GetDeliveries() + .FindAll(p => p.GetStatus() == DeliveryStatus.Waiting); + return waitingDeliveries.Count; + } + + /// + /// 棰濆鐨勫鍏呯數绔欑殑绛涢夎姹傦紙濡傚湪绾跨姸鎬侊級 + /// + public virtual bool ChargeSiteFilter(Site site) => true; + + /// + /// 鑾峰彇涓嶅湪鍏呯數鐨勬渶浣庣數閲忚溅杈嗙殑SOC鍊 + /// + public virtual float LowerCarSoc(AbstractCar car, List allChargeSite) => 0; + + /// + /// 鍒ゆ柇杞﹁締鏄惁鍦ㄧ嚎 + /// + public virtual bool IsOnlineCar(Car car) => false; + + /// + /// 鏄惁绂佹灏忚溅鍥炲緟鍛界偣鎴栧厖鐢电偣 + /// + public virtual bool ForbidBackToStandbyOrCharge(AbstractCar car) => false; + + /// + /// 楠岃瘉杞﹁締鏄惁鍙互澶勭悊 + /// + protected virtual bool ValidateCarForProcessing(Car car) + { + if (Commons.GetVehicleStatus(car)!= VehicleStatus.Normal) return false; + if (car.GetLastSite() == -1) return false; + if (car.fields.ContainsKey("arm")) return false; + return true; + } + + /// + /// 鍒ゆ柇鍏呯數妗╂槸鍚﹀紓甯 + /// + protected virtual bool IsChargePointError(Car car, int chargeSiteId) + { + return car != null && Commons.CarValue(car, "electricCurrent") < 0; + } + + #endregion + + #region Mission Execution - 涓绘墽琛岄昏緫 + + [MethodMember(Name = "鍚姩杩涚▼", Description = "寮濮嬪鐞嗗厖鐢电淮鎶よ繘绋")] + public override void Execute() + { + status.status = "宸插惎鍔"; + if (started) return; + started = true; + + myThread = new Thread(ChargeLoop) { Name = "AbstractChargeMission" }; + myThread.Start(); + } + + /// + /// 鍏呯數涓诲惊鐜 + /// + private void ChargeLoop() + { + while (started) + { + Thread.Sleep(LOOP_INTERVAL_MS); + + try + { + UpdateMissionParameters(); + + // 鎸夌數閲忎粠浣庡埌楂樺鐞嗘墍鏈夎溅杈 + var orderedCars = SimpleLib.GetAllCars() + .OfType() + .OrderBy(cc => Commons.CarValue(cc, "Soc")); + + foreach (var car in orderedCars) + { + var allChargeSites = GetChargeSitesForCar(car); + var chargingCarCount = CalculateGlobalStats(allChargeSites); + ProcessSingleCar(car, allChargeSites, chargingCarCount); + } + } + catch (Exception e) + { + Diagnosis.Log($"chargeMission error:{ExceptionFormatter.FormatEx(e)}", "error", true); + } + } + } + + /// + /// 鑾峰彇杞﹁締鍙敤鐨勫厖鐢电珯鐐瑰垪琛 + /// + private List GetChargeSitesForCar(Car car) + { + var chargeType = GetChargeType(car); + var allChargeSites = SimpleLib.GetAllSites() + .Where(p => p.fields.TryGetValue("group", out var group) && chargeType.Contains(group)) + .ToList(); + + if (AcmStatus.useChargeSiteFilter) + { + allChargeSites = allChargeSites.Where(ChargeSiteFilter).ToList(); + } + + return allChargeSites; + } + + #endregion + + #region Top-Up (琛ョ數) 鍔熻兘 + + [MethodMember(Name = "琛ョ數鍔熻兘", Description = "灏嗘寚瀹氬皬杞﹀彂閫佸幓琛ョ數锛堝埌100%骞朵繚鎸佽嫢骞插垎閽燂級")] + public void StartTopUp() + { + try + { + G.pushStatus("閫夋嫨灏忚溅"); + var obj = SimpleMonitor.selected.ToArray()[0]; + if (obj is Car car) + { + if (car == null) + { + Diagnosis.Post($"StartTopUp failed: car not found", "StartTopUpCharge", true); + return; + } + + Commons.AddOrUpdateTag(car.tags, "topup", "requested"); + Commons.AddOrUpdateTag(car.tags, "shouldCharge", "true"); + Diagnosis.Log($"StartTopUp: car {car.name}({car.id}) requested top-up", "StartTopUpCharge", true); + } + } + catch (Exception ex) + { + Diagnosis.Log($"StartTopUp error: {ExceptionFormatter.FormatEx(ex)}", "error", true); + } + } + + /// + /// 娴嬭瘯Block鍔熻兘锛堝唴閮ㄨ皟璇曠敤锛 + /// + public void TestBlock() + { + G.pushStatus("閫夋嫨灏忚溅"); + var obj = SimpleMonitor.selected.ToArray()[0]; + if (obj is Car car && car != null) + { + CommandToBlock(car.id); + } + } + + /// + /// 澶勭悊琛ョ數鐢熷懡鍛ㄦ湡 + /// + private void HandleTopUpLifecycle(CarChargeContext context) + { + var car = context.Car; + + // 濡傛灉娌℃湁琛ョ數鐩稿叧鏍囩锛岀洿鎺ヨ繑鍥 + if (!IsInTopUpMode(car)) return; + + // 琛ョ數璇锋眰浣嗘湭鍒板厖鐢电珯锛岀‘淇漵houldCharge瀛樺湪 + if (car.tags.Contains("topup") && !context.CarInChargeSite) + { + Commons.AddOrUpdateTag(car.tags, "shouldCharge", "true"); + return; + } + + // 宸插埌鍏呯數绔欎笖澶勪簬琛ョ數妯″紡 + if (car.tags.Contains("topup") && context.CarInChargeSite) + { + HandleTopUpAtChargeSite(context); + } + } + + /// + /// 澶勭悊杞﹁締鍦ㄥ厖鐢电珯鏃剁殑琛ョ數閫昏緫 + /// + private void HandleTopUpAtChargeSite(CarChargeContext context) + { + var car = context.Car; + + // 鐢甸噺杈惧埌100%涓旀湭寮濮嬭鏃讹紝鍚姩琛ョ數璁℃椂鍣 + if (context.Soc >= 100 && !car.tags.Contains("topupInProgress")) + { + StartTopUpTimer(car); + } + + // 琛ョ數杩涜涓紝妫鏌ユ槸鍚﹀畬鎴 + if (car.tags.Contains("topupInProgress")) + { + CheckTopUpCompletion(car, context.Soc); + } + } + + /// + /// 鍚姩琛ョ數璁℃椂鍣 + /// + private void StartTopUpTimer(Car car) + { + double minutes = AcmStatus.topUpMinutes; + if (car.tags.TryGetValue("topupMinutes", out var mStr) && + double.TryParse(mStr, out var mVal) && mVal > 0) + { + minutes = mVal; + } + + var start = DateTime.Now; + var end = start.AddMinutes(minutes); + Commons.AddOrUpdateTag(car.tags, "topupStart", start.ToString("o")); + Commons.AddOrUpdateTag(car.tags, "topupEnd", end.ToString("o")); + Commons.AddOrUpdateTag(car.tags, "topupInProgress", "true"); + Diagnosis.Log($"TopUp started for car {car.name}({car.id}), until {end}", "Charge", true); + } + + /// + /// 妫鏌ヨˉ鐢垫槸鍚﹀畬鎴 + /// + private void CheckTopUpCompletion(Car car, double soc) + { + if (!car.tags.TryGetValue("topupEnd", out var endStr) || + !DateTime.TryParse(endStr, out var endTime)) + { + return; + } + + if (DateTime.Now >= endTime && soc >= 100) + { + ClearTopUpTags(car); + Commons.DeleteTag(car.tags, "shouldCharge"); + Commons.AddOrUpdateTag(car.tags, "mustToStandby", "true"); + Diagnosis.Log($"TopUp completed for car {car.name}({car.id})", "Charge", true); + } + } + + /// + /// 娓呯悊鎵鏈夎ˉ鐢电浉鍏虫爣绛 + /// + private void ClearTopUpTags(Car car) + { + foreach (var tag in TopUpTags) + { + Commons.DeleteTag(car.tags, tag); + } + } + + /// + /// 鍒ゆ柇杞﹁締鏄惁澶勪簬琛ョ數妯″紡 + /// + private bool IsInTopUpMode(Car car) + { + return car.tags.Contains("topup") || car.tags.Contains("topupInProgress"); + } + + #endregion + + #region Parameter Management - 鍙傛暟绠$悊 + + private void UpdateMissionParameters() + { + try + { + // 浠庡厖鐢电瓥鐣ラ厤缃湇鍔¤鍙栧弬鏁 + var config = ChargeStationHelper.GetChargeStrategyConfig(); + + if (config != null) + { + // SOC 鐩稿叧鍙傛暟 + AcmStatus.mustChargeSoc = config.MustChargeSoc; + AcmStatus.idleChargeSoc = config.IdleChargeSoc; + AcmStatus.taskAvailableSoc = config.TaskAvailableSoc; + AcmStatus.fullChargeSoc = config.FullChargeSoc; + AcmStatus.allowInterruptSoc = config.AllowInterruptSoc; + + // 鏃堕棿鐩稿叧鍙傛暟 + AcmStatus.idleChargeSeconds = config.IdleChargeSeconds; + AcmStatus.idleSeconds = config.IdleSeconds; + AcmStatus.mustChargeSeconds = config.MustChargeSeconds; + AcmStatus.topUpMinutes = config.TopUpMinutes; + + // 浠诲姟鐩稿叧鍙傛暟 + AcmStatus.minAllowFreeCarToChargeTaskCnt = config.MinAllowFreeCarToChargeTaskCnt; + + // 寮鍏冲弬鏁 + AcmStatus.allowInterruptTask = config.AllowInterruptTask; + AcmStatus.useLowerSocForCharge = config.UseLowerSocForCharge; + AcmStatus.enableErrorChargeDetection = config.EnableErrorChargeDetection; + AcmStatus.useChargeSiteFilter = config.UseChargeSiteFilter; + + //Diagnosis.Log("鍏呯數绛栫暐鍙傛暟宸蹭粠閰嶇疆鏂囦欢鍔犺浇"); + } + else + { + // 閰嶇疆涓虹┖锛屼娇鐢ㄦ棫鐨勬柟寮忥紙浠 fields 璇诲彇锛 + LoadParametersFromFields(); + } + } + catch (Exception ex) + { + Diagnosis.Log($"鍔犺浇鍏呯數绛栫暐鍙傛暟澶辫触锛屼娇鐢╢ields鎴栭粯璁ゅ: {ex.Message}"); + // 鍑洪敊鏃朵娇鐢ㄦ棫鐨勬柟寮忥紙浠 fields 璇诲彇锛 + LoadParametersFromFields(); + } + } + + /// + /// 浠 fields 瀛楀吀鍔犺浇鍙傛暟锛堝吋瀹规棫鏂瑰紡锛 + /// + private void LoadParametersFromFields() + { + AcmStatus.mustChargeSoc = ParseDouble("mustChargeSoc", 20); + AcmStatus.idleChargeSoc = ParseDouble("idleChargeSoc", 90); + AcmStatus.taskAvailableSoc = ParseDouble("taskAvailableSoc", 60); + AcmStatus.fullChargeSoc = ParseDouble("fullChargeSoc", 90); + AcmStatus.allowInterruptSoc = ParseDouble("allowInterruptSoc", 45); + + AcmStatus.idleChargeSeconds = ParseDouble("idleChargeSeconds", 30); + AcmStatus.idleSeconds = ParseDouble("idleSeconds", 5); + AcmStatus.mustChargeSeconds = ParseDouble("mustChargeSeconds", 60); + AcmStatus.topUpMinutes = ParseDouble("topUpMinutes", 5); + + AcmStatus.minAllowFreeCarToChargeTaskCnt = ParseInt("minAllowFreeCarToChargeTaskNub", 0); + AcmStatus.allowInterruptTask = ParseBool("allowInterruptTask", false); + AcmStatus.useLowerSocForCharge = ParseBool("useLowerSocForCharge", true); + AcmStatus.enableErrorChargeDetection = ParseBool("enableErrorChargeDetection", false); + AcmStatus.useChargeSiteFilter = ParseBool("useChargeSiteFilter", false); + } + + private double ParseDouble(string key, double defaultValue) + { + return fields.TryGetValue(key, out var str) && double.TryParse(str, out var val) ? val : defaultValue; + } + + private int ParseInt(string key, int defaultValue) + { + return fields.TryGetValue(key, out var str) && int.TryParse(str, out var val) ? val : defaultValue; + } + + private bool ParseBool(string key, bool defaultValue) + { + return fields.TryGetValue(key, out var str) && bool.TryParse(str, out var val) ? val : defaultValue; + } + + #endregion + + #region Global Stats - 鍏ㄥ眬缁熻 + + private int CalculateGlobalStats(List allChargeSites) + { + var validCars = SimpleLib.GetAllCars().Where(p => p.GetLastSite() != -1).ToList(); + int chargingCarCount = 0; + int freeCarCount = 0; + int onlineCarCount = 0; + + foreach (var car in validCars) + { + if (IsCarOccupyingChargeSite(car, allChargeSites)) + chargingCarCount++; + + if (car.tags.Contains("idle") && car.status.pendingLocks.Length == 0) + freeCarCount++; + + if (IsOnlineCar((Car)car)) + onlineCarCount++; + } + + if (onlineCarCount != 0) + AcmStatus.freeCarPercent = (float)freeCarCount / onlineCarCount; + + return chargingCarCount; + } + + private bool IsCarOccupyingChargeSite(AbstractCar car, List allChargeSites) + { + return allChargeSites.Any(site => + car.status.holdingLocks.Contains(site.id) || + car.status.pendingLocks.Contains(site.id)); + } + + #endregion + + #region Car Processing - 鍗曡溅澶勭悊涓绘祦绋 + + private void ProcessSingleCar(Car car, List allChargeSites, int chargingCarCount) + { + if (!ValidateCarForProcessing(car)) return; + + var context = BuildCarContext(car, allChargeSites, chargingCarCount); + + // 娓呯悊寰呭懡鏍囩 + if (context.CarInStandbySite && car.tags.Contains("mustToStandby")) + Commons.DeleteTag(car.tags, "mustToStandby"); + + // 浠诲姟鎵撴柇妫鏌 + HandleTaskInterruption(context); + + // 鍓嶇疆鏉′欢妫鏌 + if (IsCarBusy(car) || ForbidBackToStandbyOrCharge(car)) return; + + // 蹇呴』鍏呯數閫昏緫 + HandleMustChargeLogic(context); + + // 鏇存柊绌洪棽鍜屽厖鐢电姸鎬 + if (!UpdateIdleAndChargeStatus(context)) return; + + // 澶勭悊琛ョ數鐢熷懡鍛ㄦ湡 + HandleTopUpLifecycle(context); + + // 鍐冲畾鍏呯數鎴栧洖寰呭懡 + DecideChargeOrStandby(context); + + // 瑙勫垝璺緞骞舵墽琛 + PlanAndExecuteMovement(context, allChargeSites); + } + + private CarChargeContext BuildCarContext(Car car, List allChargeSites, int chargingCarCount) + { + var soc = Commons.CarValue(car, "Soc"); + var electricCurrent = Commons.CarValue(car, "ElectricCurrent"); + var carSiteId = car.GetLastSite(); + + var context = new CarChargeContext + { + Car = car, + Soc = soc, + ElectricCurrent = electricCurrent, + MustCharge = soc < AcmStatus.mustChargeSoc, + WaitingMissions = WaitingMissions(car), + CarInStandbySite = SimpleLib.GetAllSites() + .Any(p => p.fields.ContainsKey("standby") && p.id == carSiteId), + CarInChargeSite = car.status.holdingLocks.Length == 1 && + SimpleLib.GetAllSites().Any(p => p.fields.ContainsKey("Charge") && p.id == car.status.holdingLocks.First()) + }; + + // 鏇存柊鍏ㄥ眬鐘舵 + AcmStatus.freeChargeCount = allChargeSites.Count - chargingCarCount; + AcmStatus.lowerSocCar = LowerCarSoc(car, allChargeSites); + + if (car.tags.TryGetValue("idle", out var strIdleTime)) + context.IdleTime = DateTime.Parse(strIdleTime); + + return context; + } + + private bool IsCarBusy(Car car) + { + return car.tags.Contains("occupied") || + car.tags.Contains("blocking") || + car.status.pendingLocks.Length != 0; + } + + #endregion + + #region Charging Logic - 鍏呯數閫昏緫 + + /// + /// 澶勭悊浠诲姟鎵撴柇閫昏緫 + /// + private void HandleTaskInterruption(CarChargeContext context) + { + lock (_interruptLock) + { + // 娓呯悊杩囨椂鐨勬墦鏂寔鏈夎 + CleanupStaleInterruptHolder(); + + var occupiedValue = GetOccupiedTagValue(context.Car); + var isMaintenanceTask = IsMaintenanceTask(occupiedValue); + + // 鍒ゆ柇鏄惁鍙互鑰冭檻瑙﹀彂鎵撴柇 + var canConsiderTrigger = CanConsiderInterrupt(context, isMaintenanceTask); + + // 璇勯夋渶浣冲欓夎溅 + if (canConsiderTrigger) + { + SelectBestInterruptCandidate(context); + } + + // 鍙湁鏈浣冲欓夎溅鎵嶈Е鍙戞墦鏂 + bool isBestCandidate = context.Car.id == _bestInterruptionCandidateId; + + LadderLogic.TriggerOnce(canConsiderTrigger && isBestCandidate, 5000, () => + { + ExecuteInterrupt(context, occupiedValue); + }, context.Car.id); + } + } + + /// + /// 娓呯悊杩囨椂鐨勬墦鏂寔鏈夎 + /// + private void CleanupStaleInterruptHolder() + { + if (_interruptingCarId == -1) return; + + var holder = SimpleLib.GetAllCars().OfType().FirstOrDefault(c => c.id == _interruptingCarId); + if (holder == null) + { + _interruptingCarId = -1; + return; + } + + var holderOccupied = GetOccupiedTagValue(holder); + if (!IsMaintenanceTask(holderOccupied)) + { + _interruptingCarId = -1; + } + } + + /// + /// 鍒ゆ柇鏄惁涓虹淮鎶や换鍔★紙鍏呯數鎴栧緟鍛斤級 + /// + private bool IsMaintenanceTask(string occupiedValue) + { + return occupiedValue.Contains("toCharge") || occupiedValue.Contains("toStandby"); + } + + /// + /// 鍒ゆ柇鏄惁鍙互鑰冭檻瑙﹀彂鎵撴柇 + /// + private bool CanConsiderInterrupt(CarChargeContext context, bool isMaintenanceTask) + { + return context.WaitingMissions > 0 && context.Soc > AcmStatus.allowInterruptSoc && + FreeCarNub() == 0 && AcmStatus.allowInterruptTask && + isMaintenanceTask && _interruptingCarId == -1; + } + + /// + /// 璇勯夋渶浣虫墦鏂欓夎溅 + /// + private void SelectBestInterruptCandidate(CarChargeContext context) + { + // 鑾峰彇鎵鏈夋弧瓒虫墦鏂潯浠剁殑杞﹁締 + var candidates = SimpleLib.GetAllCars().OfType() + .Where(c => + { + var cOccupied = GetOccupiedTagValue(c); + var cSoc = Commons.CarValue(c, "Soc"); + return IsMaintenanceTask(cOccupied) && cSoc > AcmStatus.allowInterruptSoc; + }).ToList(); + + if (!candidates.Any()) return; + + // 鑾峰彇绗竴涓瓑寰呬换鍔 + var firstMission = SimpleProject.proj.Missions.OfType() + .First() + .GetDeliveries() + .FindAll(p => p.GetStatus() == DeliveryStatus.Waiting) + .OrderBy(p => p.CreateTime) + .FirstOrDefault(); + + Car bestCandidate; + + if (firstMission != null) + { + // 浼樺厛閫夋嫨璺濈浠诲姟璧风偣鏈杩戠殑杞︼紝鍏舵閫夋嫨鐢甸噺鏈楂樼殑杞 + bestCandidate = candidates + .OrderBy(c => CalculateRouteLength(c, ((TransportDelivery)firstMission).Src)) + .ThenByDescending(c => Commons.CarValue(c, "Soc")) + .FirstOrDefault(); + } + else + { + // 浠呮寜鐢甸噺鎺掑簭 + bestCandidate = candidates + .OrderByDescending(c => Commons.CarValue(c, "Soc")).FirstOrDefault(); + } + + if (bestCandidate != null) + { + _bestInterruptionCandidateId = bestCandidate.id; + Diagnosis.Post($"鏇存柊鏈浣冲欓夎溅ID:{bestCandidate.id}", "CommandToBlock", true); + } + } + + /// + /// 璁$畻杞﹁締鍒扮洰鏍囩珯鐐圭殑璺緞闀垮害 + /// + private float CalculateRouteLength(Car car, int targetSiteId) + { + var mPlan = new SegmentPlan { usingCar = car }; + try + { + var routeLength = mPlan.FindRoute( + SimpleLib.GetSite(car.GetLastSite()), + SimpleLib.GetSite(targetSiteId)); + Diagnosis.Post($"褰撳墠灏濊瘯鎵撴柇杞car.id}璺濈浠诲姟璧风偣{mPlan.Destination.id}璺濈:{routeLength}", "CommandToBlock", true); + return routeLength; + } + catch + { + return float.MaxValue; + } + } + + /// + /// 鎵ц鎵撴柇鎿嶄綔 + /// + private void ExecuteInterrupt(CarChargeContext context, string occupiedValue) + { + lock (_interruptLock) + { + if (_interruptingCarId == -1) + { + _interruptingCarId = context.Car.id; + Diagnosis.Post($"灏忚溅:{context.Car.name}锛岃Е鍙戞墦鏂换鍔★紝missions:{context.WaitingMissions}," + + $"isOccupied:{occupiedValue},soc:{context.Soc},freecar:{FreeCarNub()}", + "CommandToBlock", true); + CommandToBlock(context.Car.id); + _bestInterruptionCandidateId = -1; + } + else + { + Diagnosis.Post($"璺宠繃鎵撴柇锛氬凡鏈夎溅杈({_interruptingCarId})姝e湪琚墦鏂紝褰撳墠灏濊瘯杞:{context.Car.id}", + "CommandToBlock", true); + } + } + } + + /// + /// 澶勭悊蹇呴』鍏呯數閫昏緫 + /// + private void HandleMustChargeLogic(CarChargeContext context) + { + LadderLogic.TriggerOnce( + context.MustCharge && !context.Car.tags.Contains("shouldCharge"), + MUST_CHARGE_TRIGGER_DELAY_MS, + () => + { + Diagnosis.Log($"灏忚溅:{context.Car.name}锛屽繀椤诲厖鐢碉紝褰撳墠鐢甸噺:{context.Soc}", "Charge", true); + Commons.AddOrUpdateTag(context.Car.tags, "shouldCharge", "true"); + }, + context.Car.id); + } + + /// + /// 鏇存柊绌洪棽鍜屽厖鐢电姸鎬 + /// + private bool UpdateIdleAndChargeStatus(CarChargeContext context) + { + var car = context.Car; + + lock (Commons.PlanSession) + { + if (car.tags.Contains("occupied") || car.tags.Contains("blocking")) return false; + + if (!car.tags.Contains("idle")) + car.tags.Add("idle", DateTime.Now.ToString()); + + // 濡傛灉姝e湪琛ョ數锛屽嵆浣跨數閲忔弧浜嗕篃涓嶅簲鑷姩鍘诲緟鍛 + if (context.Soc > AcmStatus.fullChargeSoc && !IsInTopUpMode(car)) + context.ShouldGoStandby = true; + } + + if (!car.tags.TryGetValue("idle", out var strIdleTime)) return false; + context.IdleTime = DateTime.Parse(strIdleTime); + + // 娓呯悊鍏呯數鏍囪 + if (car.tags.Contains("charging") && !context.CarInChargeSite) + car.tags.Remove("charging"); + + // 鍒ゆ柇鏄惁闇瑕佸厖鐢 + var carFree = context.Soc <= AcmStatus.taskAvailableSoc && AcmStatus.freeChargeCount > 0; + var idleCharge = (context.Soc < AcmStatus.idleChargeSoc || carFree) && + (DateTime.Now - context.IdleTime).TotalSeconds > AcmStatus.idleChargeSeconds; + + UpdateShouldChargeTag(context, idleCharge); + + return true; + } + + /// + /// 鏇存柊shouldCharge鏍囩 + /// + private void UpdateShouldChargeTag(CarChargeContext context, bool idleCharge) + { + var car = context.Car; + + if ((context.MustCharge || (idleCharge && context.WaitingMissions <= AcmStatus.minAllowFreeCarToChargeTaskCnt)) && + !context.ShouldGoStandby) + { + bool lowerSocOk = (AcmStatus.useLowerSocForCharge && context.Soc <= AcmStatus.lowerSocCar) || !AcmStatus.useLowerSocForCharge; + + if (((context.MustCharge || idleCharge) && lowerSocOk) && + AcmStatus.freeChargeCount > 0 && context.Soc < AcmStatus.fullChargeSoc) + { + Commons.AddOrUpdateTag(car.tags, "shouldCharge", "true"); + Diagnosis.Log($"灏忚溅:{car.name}锛岄渶姹傚厖鐢碉紝褰撳墠鐢甸噺:{context.Soc},lowerSocCar:{AcmStatus.lowerSocCar}," + + $"mustCharge锛歿context.MustCharge},idleCharge:{idleCharge}锛寃aitingMissions锛歿context.WaitingMissions}", "Charge", true); + } + else + { + Commons.DeleteTag(car.tags, "shouldCharge"); + } + } + } + + #endregion + + #region Decision Making - 鍐崇瓥閫昏緫 + + /// + /// 鍐冲畾鍏呯數鎴栧洖寰呭懡 + /// + private void DecideChargeOrStandby(CarChargeContext context) + { + var car = context.Car; + + lock (Commons.PlanSession) + { + var carSiteId = car.GetLastSite(); + if (carSiteId == -1 || car.tags.Contains("occupied") || car.tags.Contains("blocking")) return; + + // 鏇存柊鍏呯數鐘舵 + UpdateChargingTag(context); + + // 妫鏌ュ厖鐢靛紓甯 + ErrorChargeTriggerToStandby(context); + + // 娓呴櫎shouldCharge鏍囪 + if ((context.ChargingTaskAvailable || + (context.WaitingMissions > 0 && context.Soc > AcmStatus.taskAvailableSoc && !car.tags.Contains("charging"))) && + car.tags.Contains("shouldCharge")) + { + Commons.DeleteTag(car.tags, "shouldCharge"); + } + + // 妫鏌ユ槸鍚﹀彲浠ョ寮鍏呯數绔欐墽琛屼换鍔 + CheckCanLeaveForTask(context); + + // 鍐冲畾鍘诲厖鐢 + if (car.tags.Contains("shouldCharge") && !CarInChargeSite(car, carSiteId)) + { + context.ShouldGoCharge = true; + Diagnosis.Log($"selected car:{car.name}|{car.id} to charge", "Charge", true); + } + + // 鍐冲畾鍘诲緟鍛 + if (!context.ShouldGoCharge) + { + CheckShouldGoStandby(context, carSiteId); + } + } + } + + /// + /// 鏇存柊鍏呯數鏍囩 + /// + private void UpdateChargingTag(CarChargeContext context) + { + var car = context.Car; + var isTopUping = IsInTopUpMode(car); + + if (context.CarInChargeSite) + { + if (context.Soc <= AcmStatus.fullChargeSoc || isTopUping) + { + if (!car.tags.Contains("charging")) + car.tags.Add("charging", DateTime.Now.ToString()); + } + else if (!isTopUping) + { + if (car.tags.Contains("charging")) + car.tags.Remove("charging"); + context.ChargingTaskAvailable = true; + } + } + } + + /// + /// 妫鏌ユ槸鍚﹀彲浠ョ寮鍏呯數绔欐墽琛屼换鍔 + /// + private void CheckCanLeaveForTask(CarChargeContext context) + { + var car = context.Car; + + var canInterruptForTask = context.WaitingMissions > MAX_WAIT_WAITMISSION && + context.Soc > AcmStatus.allowInterruptSoc; + + if (context.Soc > AcmStatus.taskAvailableSoc && car.tags.Contains("charging")) + { + var onlyOneCarCharge = SimpleLib.GetAllCars().Count(p => p.GetLastSite() != -1) == 1; + + var canLeaveButStillCharge = + (context.Soc - AcmStatus.lowerSocCar) <= AcmStatus.lowPowerCarSwapChangeThresholdDelta && + (context.WaitingMissions <= AcmStatus.minAllowFreeCarToChargeTaskCnt || + context.Soc <= AcmStatus.lowerSocCar || + onlyOneCarCharge || + (context.WaitingMissions == 0 && context.Soc < AcmStatus.fullChargeSoc)); + + // 琛ョ數妯″紡涓嶅厑璁哥寮 + if (IsInTopUpMode(car)) return; + + // 婊¤冻浠诲姟鎵撴柇鏉′欢鎴栨弧瓒冲師鏈夌寮鏉′欢 + if (canInterruptForTask || + (car.tags.TryGetValue("charging", out var cTime) && + DateTime.Parse(cTime).AddSeconds(AcmStatus.mustChargeSeconds) < DateTime.Now && + !canLeaveButStillCharge)) + { + car.tags.Remove("charging"); + context.ChargingTaskAvailable = true; + Commons.AddOrUpdateTag(car.tags, "mustToStandby", "true"); + Commons.DeleteTag(car.tags, "shouldCharge"); + if (canInterruptForTask) + { + Diagnosis.Log($"灏忚溅:{car.name}锛屽洜鏈変换鍔′笖鐢甸噺{context.Soc}楂樹簬{AcmStatus.allowInterruptSoc}锛屼腑鏂厖鐢靛幓鎵ц浠诲姟", "Charge", true); + } + } + } + } + + /// + /// 妫鏌ユ槸鍚﹀簲璇ュ幓寰呭懡鐐 + /// + private void CheckShouldGoStandby(CarChargeContext context, int carSiteId) + { + var car = context.Car; + var standbyType = GetStandbyType(car); + + bool isBlocked = SimpleLib.GetAllCars() + .Any(c => c.status.pendingLocks.Length != 0 && c.id != car.id && + c.status.pendingLocks.Contains(car.GetLastSite())); + + if (SimpleLib.GetSite(carSiteId).fields.ContainsKey(standbyType) && !isBlocked) return; + if ((DateTime.Now - context.IdleTime).TotalSeconds < AcmStatus.idleSeconds) return; + + context.ShouldGoStandby = true; + } + + /// + /// 鍏呯數寮傚父瑙﹀彂鍥炲緟鍛界偣 + /// + private void ErrorChargeTriggerToStandby(CarChargeContext context) + { + if (!AcmStatus.enableErrorChargeDetection || !context.CarInChargeSite) return; + + var currentChargeSite = context.Car.status.holdingLocks.First(); + bool isChargeError = IsChargePointError(context.Car, currentChargeSite); + + LadderLogic.TriggerOnce(isChargeError, 10000, () => + { + HandleChargeError(context, currentChargeSite); + }, context.Car.id); + } + + /// + /// 澶勭悊鍏呯數寮傚父 + /// + private void HandleChargeError(CarChargeContext context, int currentChargeSite) + { + Commons.DeleteTag(context.Car.tags, "shouldCharge"); + + if (context.Car.tags.Contains("charging")) + context.Car.tags.Remove("charging"); + + if (AcmStatus.freeChargeCount == 0) + { + Commons.AddOrUpdateTag(context.Car.tags, "mustToStandby", "true"); + context.ShouldGoStandby = true; + Diagnosis.Log($"灏忚溅:{context.Car.name}锛屽厖鐢垫々{currentChargeSite}寮傚父锛屾棤绌洪棽鍏呯數妗╋紝杩斿洖寰呭懡鐐", "ChargeError", true); + } + else + { + context.ShouldGoCharge = true; + Commons.AddOrUpdateTag(context.Car.tags, "mustToAnotherChargeSite", $"{currentChargeSite}"); + context.ShouldGoStandby = false; + Diagnosis.Log($"灏忚溅:{context.Car.name}锛屽厖鐢垫々{currentChargeSite}寮傚父锛屽垏鎹㈠埌鍏朵粬鍏呯數妗", "ChargeError", true); + } + } + + + /// + /// 鍒ゆ柇褰撳墠绔欑偣鏄惁鏄叾浠栧皬杞endingLocks鐨勭粓鐐癸紝鑻ユ槸鍒欓渶瑕侀伩璁 + /// + private bool CheckPendingEndpointConflict(int siteId, Car currentCar) + { + if (siteId == -1) return false; + foreach (var other in SimpleLib.GetAllCars()) + { + if (other.id == currentCar.id) continue; + var pending = other.status.pendingLocks.Contains(siteId); + var holding = other.status.holdingLocks.Contains(siteId); + if (holding || + pending) + { + return true; + } + if (other.tags.TryGetValue("conflictTask", out string conflictTask)) + { + + if (conflictTask.Contains(siteId.ToString())) + { + return true; + } + } + } + + return false; + } + + #endregion + + #region Movement Planning - 绉诲姩瑙勫垝 + + /// + /// 瑙勫垝璺緞骞舵墽琛岀Щ鍔 + /// + private void PlanAndExecuteMovement(CarChargeContext context, List allChargeSites) + { + var car = context.Car; + var carCharging = allChargeSites.Contains(SimpleLib.GetSite(car.GetLastSite())); + + // 瑙勫垝鍘诲厖鐢电珯 + if (ShouldPlanToCharge(context)) + { + context.TargetPlan = PlanToChargeSite(context); + if (context.TargetPlan != null) + { + car.tags.Add("occupied", "toCharge"); + context.TargetType = TARGET_TYPE_CHARGE; + } + } + + // 瑙勫垝鍘诲緟鍛界偣 + if (context.TargetPlan == null && ShouldPlanToStandby(context, carCharging)) + { + context.TargetPlan = PlanToStandbySite(context); + if (context.TargetPlan != null) + { + car.tags.Add("occupied", "toStandby"); + context.TargetType = TARGET_TYPE_STANDBY; + } + } + + if (context.TargetPlan == null) return; + + ExecuteMovement(context); + } + + private bool ShouldPlanToCharge(CarChargeContext context) + { + var car = context.Car; + return (context.ShouldGoCharge || + (car.tags.ContainsKey("mustToAnotherChargeSite") && car.tags.ContainsKey("shouldCharge"))) && + Commons.SelectCar(car, false, true, true) != -1 && + !car.tags.Contains("mustToStandby"); + } + + private bool ShouldPlanToStandby(CarChargeContext context, bool carCharging) + { + var car = context.Car; + return context.ShouldGoStandby && + (car.tags.Contains("mustToStandby") || !carCharging || (carCharging && context.ChargingTaskAvailable)) && + Commons.SelectCar(car, checkHoldCar: true) != -1; + } + + private SegmentPlan PlanToChargeSite(CarChargeContext context) + { + var car = context.Car; + return Commons.GetNearestPlan(car, site => + { + if (IsSiteOccupied(site)) return false; + + bool mustToAnotherChargeSite = car.tags.TryGetValue("mustToAnotherChargeSite", out var chargingSite) && + int.Parse(chargingSite) == site.id; + + return site.fields.ContainsKey("group") && + site.fields.ContainsKey("Charge") && + GetChargeType(car).Contains(site.fields["group"]) && + ((!mustToAnotherChargeSite && context.CarInChargeSite) || !context.CarInChargeSite); + }); + } + + private SegmentPlan PlanToStandbySite(CarChargeContext context) + { + var car = context.Car; + return Commons.SimpleToNearestPlan(car, site => + { + if (IsSiteOccupied(site)) return false; + if (IsUndeliverableSite(car, site.id)) return false; + + var isConflict= CheckPendingEndpointConflict(car.status.holdingLocks.First(),car) + &&(site.fields.ContainsKey("standby")|| site.fields.ContainsKey("giveWay")); + return ((site.fields.ContainsKey("standby") || isConflict)) && + !site.tags.Contains("unavailable") && + !context.CarInStandbySite; + }); + } + + /// + /// 鎵ц绉诲姩 + /// + private void ExecuteMovement(CarChargeContext context) + { + var car = context.Car; + var targetPlan = context.TargetPlan; + CarProgram program; + + try + { + program = targetPlan.Compile( + $"{car.name}({car.id}) maintenance. charging?{context.ShouldGoCharge}--{targetPlan.Destination.id}"); + } + catch (Exception ex) + { + HandleCarFailure(car, ex); + if (!context.ShouldGoCharge) + AddUndeliverableSite(car, targetPlan.Destination.id); + return; + } + + Task.Run(async () => + { + try + { + if (context.TargetType == TARGET_TYPE_STANDBY && car.tags.Contains("shouldCharge")) + LeaveAction(car, targetPlan.Source); + + car.tags.Add("dest", $"{targetPlan.Destination.id}"); + await program.Queue(); + + CleanupAfterMovement(car, targetPlan.Destination.id); + } + catch (Exception e) + { + Diagnosis.Post($"fail to standby/charge exception:{ExceptionFormatter.FormatEx(e)}"); + HandleCarFailure(car); + } + }); + } + + private void CleanupAfterMovement(Car car, int destinationId) + { + Commons.DeleteTag(car.tags, "mustToAnotherChargeSite"); + Commons.DeleteTag(car.tags, "undeliverableSite"); + Commons.DeleteTag(car.tags, "mustToStandby"); + car.siteID = destinationId; + lock (Commons.PlanSession) + car.tags.Remove("occupied"); + } + + #endregion + + #region Helper Methods - 杈呭姪鏂规硶 + + /// + /// 鑾峰彇occupied鏍囩鍊 + /// + public string GetOccupiedTagValue(Car car) + { + return car.tags.TryGetValue("occupied", out var occupiedValue) ? occupiedValue : ""; + } + + /// + /// 澶勭悊杞﹁締澶辫触 + /// + private void HandleCarFailure(Car car, Exception ex = null) + { + Diagnosis.Post($"car{car.id} failed going standby/charge, {ExceptionFormatter.FormatEx(ex)}"); + lock (Commons.PlanSession) + { + Commons.DeleteTag(car.tags, "occupied"); + car.tags.Add("idle", DateTime.Now.ToString()); + } + } + + /// + /// 鑾峰彇绌洪棽杞﹁締鏁伴噺 + /// + private int FreeCarNub() + { + return SimpleLib.GetAllCars() + .Count(p => !p.tags.Contains("occupied") && + p.status.holdingLocks.Length == 1 && + p.status.pendingLocks.Length == 0 && + !p.tags.ContainsKey("charging") && + p.tags.Contains("idle")); + } + + /// + /// 鍒ゆ柇杞﹁締鏄惁鍦ㄥ厖鐢电珯 + /// + private bool CarInChargeSite(Car car, int carSiteId) + { + var site = SimpleLib.GetSite(carSiteId); + if (!site.fields.ContainsKey("group")) return false; + if (!GetChargeType(car).Contains(site.fields["group"])) return false; + if (!site.fields.ContainsKey("Charge")) return false; + return true; + } + + /// + /// 鍒ゆ柇绔欑偣鏄惁琚崰鐢 + /// + private bool IsSiteOccupied(Site site) + { + return SimpleLib.GetAllCars().Any(o => + o.status.pendingLocks.Contains(site.id) || + o.status.holdingLocks.Contains(site.id)); + } + + /// + /// 鍒ゆ柇绔欑偣鏄惁涓嶅彲杈 + /// + private bool IsUndeliverableSite(Car car, int siteId) + { + if (!car.tags.TryGetValue("undeliverableSite", out var sites)) return false; + + var siteList = sites.Split(','); + if (siteList.Length > MAX_UNDELIVERABLE_SITES) + Commons.DeleteTag(car.tags, "undeliverableSite"); + + foreach (var s in siteList) + { + if (int.TryParse(s, out var id) && id == siteId) + { + Diagnosis.Log($"{car.name}浼戞伅鍥炴粴鍙戝幓寰呭懡鐐,sites[{sites}]澶辫触锛寀ndeliverableSite锛屾洿鎹笅涓珯鐐", + "UndeliverableSite", specialFolder: true); + return true; + } + } + return false; + } + + /// + /// 娣诲姞涓嶅彲杈剧珯鐐硅褰 + /// + private void AddUndeliverableSite(Car car, int siteId) + { + if (car.tags.TryGetValue("undeliverableSite", out var undeliverableSite)) + Commons.AddOrUpdateTag(car.tags, "undeliverableSite", $"{undeliverableSite},{siteId}"); + else + Commons.AddOrUpdateTag(car.tags, "undeliverableSite", siteId.ToString()); + } + + #endregion + } +} diff --git a/StandardScene.Core/Charge/AlarmConfig.cs b/StandardScene.Core/Charge/AlarmConfig.cs new file mode 100644 index 0000000..1013ee6 --- /dev/null +++ b/StandardScene.Core/Charge/AlarmConfig.cs @@ -0,0 +1,109 @@ +using System; +using System.ComponentModel; + +namespace StandardScene.Charge +{ + /// + /// 鎶ヨ閰嶇疆鏁版嵁妯″瀷 + /// + public class AlarmConfig + { + /// + /// 鎶ヨ缂栧彿锛堣嚜鍔ㄧ敓鎴愶級 + /// + [DisplayName("缂栧彿")] + public string AlarmId { get; set; } + + /// + /// 鎶ヨ缂栫爜鍊 + /// + [DisplayName("鎶ヨ缂栫爜")] + public int AlarmCode { get; set; } + + /// + /// 鎶ヨ鍐呭鎻忚堪 + /// + [DisplayName("鎶ヨ鍐呭")] + public string AlarmContent { get; set; } + + /// + /// 鎶ヨ绾у埆 + /// + [DisplayName("鎶ヨ绾у埆")] + public AlarmLevel Level { get; set; } + + /// + /// 鏄惁鍚敤 + /// + [DisplayName("鍚敤")] + public bool Enabled { get; set; } + + /// + /// 澶囨敞 + /// + [DisplayName("澶囨敞")] + public string Remarks { get; set; } + + /// + /// 鍒涘缓鏃堕棿 + /// + [DisplayName("鍒涘缓鏃堕棿")] + public DateTime CreatedTime { get; set; } + + /// + /// 鏈鍚庝慨鏀规椂闂 + /// + [DisplayName("淇敼鏃堕棿")] + public DateTime ModifiedTime { get; set; } + + public AlarmConfig() + { + AlarmId = GenerateAlarmId(); + Level = AlarmLevel.Medium; + Enabled = true; + CreatedTime = DateTime.Now; + ModifiedTime = DateTime.Now; + } + + /// + /// 鐢熸垚鎶ヨ缂栧彿 + /// + private static string GenerateAlarmId() + { + return $"ALM{DateTime.Now:yyyyMMddHHmmss}{new Random().Next(100, 999)}"; + } + + /// + /// 楠岃瘉鏁版嵁鏈夋晥鎬 + /// + public bool IsValid(out string errorMessage) + { + if (string.IsNullOrWhiteSpace(AlarmId)) + { + errorMessage = "鎶ヨ缂栧彿涓嶈兘涓虹┖"; + return false; + } + + if (AlarmCode < 0) + { + errorMessage = "鎶ヨ缂栫爜涓嶈兘涓鸿礋鏁"; + return false; + } + + if (string.IsNullOrWhiteSpace(AlarmContent)) + { + errorMessage = "鎶ヨ鍐呭涓嶈兘涓虹┖"; + return false; + } + + errorMessage = string.Empty; + return true; + } + + public override string ToString() + { + return $"[{AlarmCode}] {AlarmContent}"; + } + } +} + diff --git a/StandardScene.Core/Charge/AlarmConfigDataService.cs b/StandardScene.Core/Charge/AlarmConfigDataService.cs new file mode 100644 index 0000000..0254dbb --- /dev/null +++ b/StandardScene.Core/Charge/AlarmConfigDataService.cs @@ -0,0 +1,310 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using Newtonsoft.Json; + +namespace StandardScene.Charge +{ + /// + /// 鎶ヨ閰嶇疆鏁版嵁鏈嶅姟锛堝崟渚嬫ā寮忥級 + /// + public class AlarmConfigDataService + { + private static AlarmConfigDataService _instance; + private static readonly object _lock = new object(); + private List _alarmConfigs; + private readonly string _dataFilePath; + + private AlarmConfigDataService() + { + _dataFilePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Config", "AlarmConfigs.json"); + LoadData(); + } + + /// + /// 鑾峰彇鍗曚緥瀹炰緥 + /// + public static AlarmConfigDataService Instance + { + get + { + if (_instance == null) + { + lock (_lock) + { + if (_instance == null) + { + _instance = new AlarmConfigDataService(); + } + } + } + return _instance; + } + } + + /// + /// 浠庢枃浠跺姞杞芥暟鎹 + /// + private void LoadData() + { + try + { + // 纭繚鏁版嵁鐩綍瀛樺湪 + var directory = Path.GetDirectoryName(_dataFilePath); + if (!string.IsNullOrEmpty(directory) && !Directory.Exists(directory)) + { + Directory.CreateDirectory(directory); + } + + if (File.Exists(_dataFilePath)) + { + var json = File.ReadAllText(_dataFilePath); + if (!string.IsNullOrWhiteSpace(json)) + { + _alarmConfigs = JsonConvert.DeserializeObject>(json); + } + + // 濡傛灉鍙嶅簭鍒楀寲澶辫触鎴栦负null锛屽垱寤烘柊鍒楄〃 + if (_alarmConfigs == null) + { + _alarmConfigs = new List(); + } + } + else + { + _alarmConfigs = new List(); + InitializeDefaultAlarms(); + SaveData(); + } + } + catch (Exception ex) + { + System.Diagnostics.Debug.WriteLine($"鍔犺浇鎶ヨ閰嶇疆鏁版嵁澶辫触: {ex.Message}"); + _alarmConfigs = new List(); + InitializeDefaultAlarms(); + } + } + + /// + /// 鍒濆鍖栭粯璁ゆ姤璀﹂厤缃 + /// + private void InitializeDefaultAlarms() + { + //_alarmConfigs.Add(new AlarmConfig + //{ + // AlarmCode = 1001, + // AlarmContent = "鐢靛帇杩囬珮", + // Level = AlarmLevel.High, + // Enabled = true, + // Remarks = "鐢靛帇瓒呰繃棰濆畾鍊10%" + //}); + + //_alarmConfigs.Add(new AlarmConfig + //{ + // AlarmCode = 1002, + // AlarmContent = "鐢靛帇杩囦綆", + // Level = AlarmLevel.High, + // Enabled = true, + // Remarks = "鐢靛帇浣庝簬棰濆畾鍊10%" + //}); + + //_alarmConfigs.Add(new AlarmConfig + //{ + // AlarmCode = 1003, + // AlarmContent = "鐢垫祦杩囧ぇ", + // Level = AlarmLevel.Critical, + // Enabled = true, + // Remarks = "鐢垫祦瓒呰繃棰濆畾鍊" + //}); + + //_alarmConfigs.Add(new AlarmConfig + //{ + // AlarmCode = 2001, + // AlarmContent = "娓╁害寮傚父", + // Level = AlarmLevel.High, + // Enabled = true, + // Remarks = "娓╁害瓒呰繃瀹夊叏鑼冨洿" + //}); + + //_alarmConfigs.Add(new AlarmConfig + //{ + // AlarmCode = 3001, + // AlarmContent = "閫氳瓒呮椂", + // Level = AlarmLevel.Medium, + // Enabled = true, + // Remarks = "閫氳鍝嶅簲鏃堕棿瓒呰繃闃堝" + //}); + + //_alarmConfigs.Add(new AlarmConfig + //{ + // AlarmCode = 3002, + // AlarmContent = "杩炴帴鏂紑", + // Level = AlarmLevel.Critical, + // Enabled = true, + // Remarks = "缃戠粶杩炴帴涓柇" + //}); + } + + /// + /// 淇濆瓨鏁版嵁鍒版枃浠 + /// + private void SaveData() + { + try + { + var directory = Path.GetDirectoryName(_dataFilePath); + if (!Directory.Exists(directory)) + { + Directory.CreateDirectory(directory); + } + + var json = JsonConvert.SerializeObject(_alarmConfigs, Formatting.Indented); + File.WriteAllText(_dataFilePath, json); + } + catch (Exception ex) + { + throw new Exception($"淇濆瓨鏁版嵁澶辫触: {ex.Message}"); + } + } + + /// + /// 鑾峰彇鎵鏈夋姤璀﹂厤缃 + /// + public List GetAllAlarmConfigs() + { + lock (_lock) + { + if (_alarmConfigs == null) + { + _alarmConfigs = new List(); + } + return new List(_alarmConfigs); + } + } + + /// + /// 鏍规嵁缂栧彿鑾峰彇鎶ヨ閰嶇疆 + /// + public AlarmConfig GetAlarmConfig(string alarmId) + { + lock (_lock) + { + return _alarmConfigs.FirstOrDefault(a => a.AlarmId == alarmId); + } + } + /// + /// 鏍规嵁缂栧彿鑾峰彇鎶ヨ閰嶇疆 + /// + public AlarmConfig GetAlarmConfigAlarmCode(int alarmCode) + { + lock (_lock) + { + return _alarmConfigs.FirstOrDefault(a => a.AlarmCode == alarmCode); + } + } + /// + /// 鏍规嵁鎶ヨ缂栫爜鑾峰彇鎶ヨ閰嶇疆 + /// + public AlarmConfig GetAlarmConfigByCode(int alarmCode) + { + lock (_lock) + { + return _alarmConfigs.FirstOrDefault(a => a.AlarmCode == alarmCode); + } + } + + /// + /// 娣诲姞鎶ヨ閰嶇疆 + /// + public bool AddAlarmConfig(AlarmConfig alarmConfig, out string errorMessage) + { + lock (_lock) + { + if (!alarmConfig.IsValid(out errorMessage)) + { + return false; + } + + // 妫鏌ョ紪鐮佹槸鍚﹀凡瀛樺湪 + if (_alarmConfigs.Any(a => a.AlarmCode == alarmConfig.AlarmCode)) + { + errorMessage = $"鎶ヨ缂栫爜 {alarmConfig.AlarmCode} 宸插瓨鍦"; + return false; + } + + _alarmConfigs.Add(alarmConfig); + SaveData(); + errorMessage = string.Empty; + return true; + } + } + + /// + /// 鏇存柊鎶ヨ閰嶇疆 + /// + public bool UpdateAlarmConfig(AlarmConfig alarmConfig, out string errorMessage) + { + lock (_lock) + { + if (!alarmConfig.IsValid(out errorMessage)) + { + return false; + } + + var index = _alarmConfigs.FindIndex(a => a.AlarmId == alarmConfig.AlarmId); + if (index == -1) + { + errorMessage = "鎶ヨ閰嶇疆涓嶅瓨鍦"; + return false; + } + + // 妫鏌ョ紪鐮佹槸鍚︿笌鍏朵粬閰嶇疆鍐茬獊 + if (_alarmConfigs.Any(a => a.AlarmId != alarmConfig.AlarmId && a.AlarmCode == alarmConfig.AlarmCode)) + { + errorMessage = $"鎶ヨ缂栫爜 {alarmConfig.AlarmCode} 宸茶鍏朵粬閰嶇疆浣跨敤"; + return false; + } + + alarmConfig.ModifiedTime = DateTime.Now; + _alarmConfigs[index] = alarmConfig; + SaveData(); + errorMessage = string.Empty; + return true; + } + } + + /// + /// 鍒犻櫎鎶ヨ閰嶇疆 + /// + public bool DeleteAlarmConfig(string alarmId, out string errorMessage) + { + lock (_lock) + { + var alarmConfig = _alarmConfigs.FirstOrDefault(a => a.AlarmId == alarmId); + if (alarmConfig == null) + { + errorMessage = "鎶ヨ閰嶇疆涓嶅瓨鍦"; + return false; + } + + _alarmConfigs.Remove(alarmConfig); + SaveData(); + errorMessage = string.Empty; + return true; + } + } + + /// + /// 閲嶆柊鍔犺浇鏁版嵁 + /// + public void Reload() + { + lock (_lock) + { + LoadData(); + } + } + } +} + diff --git a/StandardScene.Core/Charge/AlarmConfigManagementForm.Designer.cs b/StandardScene.Core/Charge/AlarmConfigManagementForm.Designer.cs new file mode 100644 index 0000000..58539ac --- /dev/null +++ b/StandardScene.Core/Charge/AlarmConfigManagementForm.Designer.cs @@ -0,0 +1,596 @@ +namespace StandardScene.Charge +{ + partial class AlarmConfigManagementForm + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle3 = new System.Windows.Forms.DataGridViewCellStyle(); + System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle4 = new System.Windows.Forms.DataGridViewCellStyle(); + this.splitContainer = new System.Windows.Forms.SplitContainer(); + this.pnlList = new System.Windows.Forms.Panel(); + this.dgvAlarmConfigs = new System.Windows.Forms.DataGridView(); + this.pnlListButtons = new System.Windows.Forms.Panel(); + this.lblStatistics = new System.Windows.Forms.Label(); + this.btnClose = new System.Windows.Forms.Button(); + this.btnRefresh = new System.Windows.Forms.Button(); + this.pnlSearch = new System.Windows.Forms.Panel(); + this.cmbLevelFilter = new System.Windows.Forms.ComboBox(); + this.lblLevelFilter = new System.Windows.Forms.Label(); + this.txtSearch = new System.Windows.Forms.TextBox(); + this.lblSearch = new System.Windows.Forms.Label(); + this.pnlEdit = new System.Windows.Forms.Panel(); + this.grpEditInfo = new System.Windows.Forms.GroupBox(); + this.txtRemarks = new System.Windows.Forms.TextBox(); + this.lblRemarks = new System.Windows.Forms.Label(); + this.chkEnabled = new System.Windows.Forms.CheckBox(); + this.cmbLevel = new System.Windows.Forms.ComboBox(); + this.lblLevel = new System.Windows.Forms.Label(); + this.txtAlarmContent = new System.Windows.Forms.TextBox(); + this.lblAlarmContent = new System.Windows.Forms.Label(); + this.numAlarmCode = new System.Windows.Forms.NumericUpDown(); + this.lblAlarmCode = new System.Windows.Forms.Label(); + this.txtAlarmId = new System.Windows.Forms.TextBox(); + this.lblAlarmId = new System.Windows.Forms.Label(); + this.pnlEditButtons = new System.Windows.Forms.Panel(); + this.btnCancel = new System.Windows.Forms.Button(); + this.btnDelete = new System.Windows.Forms.Button(); + this.btnSave = new System.Windows.Forms.Button(); + this.colAlarmId = new System.Windows.Forms.DataGridViewTextBoxColumn(); + this.colAlarmCode = new System.Windows.Forms.DataGridViewTextBoxColumn(); + this.colAlarmContent = new System.Windows.Forms.DataGridViewTextBoxColumn(); + this.colLevel = new System.Windows.Forms.DataGridViewTextBoxColumn(); + this.colEnabled = new System.Windows.Forms.DataGridViewTextBoxColumn(); + this.colRemarks = new System.Windows.Forms.DataGridViewTextBoxColumn(); + ((System.ComponentModel.ISupportInitialize)(this.splitContainer)).BeginInit(); + this.splitContainer.Panel1.SuspendLayout(); + this.splitContainer.Panel2.SuspendLayout(); + this.splitContainer.SuspendLayout(); + this.pnlList.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.dgvAlarmConfigs)).BeginInit(); + this.pnlListButtons.SuspendLayout(); + this.pnlSearch.SuspendLayout(); + this.pnlEdit.SuspendLayout(); + this.grpEditInfo.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.numAlarmCode)).BeginInit(); + this.pnlEditButtons.SuspendLayout(); + this.SuspendLayout(); + // + // splitContainer + // + this.splitContainer.Dock = System.Windows.Forms.DockStyle.Fill; + this.splitContainer.Location = new System.Drawing.Point(0, 0); + this.splitContainer.Margin = new System.Windows.Forms.Padding(4); + this.splitContainer.Name = "splitContainer"; + // + // splitContainer.Panel1 + // + this.splitContainer.Panel1.Controls.Add(this.pnlList); + // + // splitContainer.Panel2 + // + this.splitContainer.Panel2.Controls.Add(this.pnlEdit); + this.splitContainer.Size = new System.Drawing.Size(1400, 750); + this.splitContainer.SplitterDistance = 900; + this.splitContainer.SplitterWidth = 5; + this.splitContainer.TabIndex = 0; + // + // pnlList + // + this.pnlList.Controls.Add(this.dgvAlarmConfigs); + this.pnlList.Controls.Add(this.pnlListButtons); + this.pnlList.Controls.Add(this.pnlSearch); + this.pnlList.Dock = System.Windows.Forms.DockStyle.Fill; + this.pnlList.Location = new System.Drawing.Point(0, 0); + this.pnlList.Margin = new System.Windows.Forms.Padding(4); + this.pnlList.Name = "pnlList"; + this.pnlList.Size = new System.Drawing.Size(900, 750); + this.pnlList.TabIndex = 0; + // + // dgvAlarmConfigs + // + this.dgvAlarmConfigs.AllowUserToAddRows = false; + this.dgvAlarmConfigs.AllowUserToDeleteRows = false; + this.dgvAlarmConfigs.AutoSizeColumnsMode = System.Windows.Forms.DataGridViewAutoSizeColumnsMode.Fill; + this.dgvAlarmConfigs.BackgroundColor = System.Drawing.Color.White; + this.dgvAlarmConfigs.BorderStyle = System.Windows.Forms.BorderStyle.None; + this.dgvAlarmConfigs.CellBorderStyle = System.Windows.Forms.DataGridViewCellBorderStyle.SingleHorizontal; + dataGridViewCellStyle3.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleLeft; + dataGridViewCellStyle3.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(63)))), ((int)(((byte)(81)))), ((int)(((byte)(181))))); + dataGridViewCellStyle3.Font = new System.Drawing.Font("寰蒋闆呴粦", 9F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + dataGridViewCellStyle3.ForeColor = System.Drawing.Color.White; + dataGridViewCellStyle3.SelectionBackColor = System.Drawing.Color.FromArgb(((int)(((byte)(63)))), ((int)(((byte)(81)))), ((int)(((byte)(181))))); + dataGridViewCellStyle3.SelectionForeColor = System.Drawing.SystemColors.HighlightText; + dataGridViewCellStyle3.WrapMode = System.Windows.Forms.DataGridViewTriState.True; + this.dgvAlarmConfigs.ColumnHeadersDefaultCellStyle = dataGridViewCellStyle3; + this.dgvAlarmConfigs.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize; + this.dgvAlarmConfigs.Columns.AddRange(new System.Windows.Forms.DataGridViewColumn[] { + this.colAlarmId, + this.colAlarmCode, + this.colAlarmContent, + this.colLevel, + this.colEnabled, + this.colRemarks}); + dataGridViewCellStyle4.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleLeft; + dataGridViewCellStyle4.BackColor = System.Drawing.Color.White; + dataGridViewCellStyle4.Font = new System.Drawing.Font("寰蒋闆呴粦", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + dataGridViewCellStyle4.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(64)))), ((int)(((byte)(64)))), ((int)(((byte)(64))))); + dataGridViewCellStyle4.SelectionBackColor = System.Drawing.Color.FromArgb(((int)(((byte)(197)))), ((int)(((byte)(202)))), ((int)(((byte)(233))))); + dataGridViewCellStyle4.SelectionForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(33)))), ((int)(((byte)(33)))), ((int)(((byte)(33))))); + dataGridViewCellStyle4.WrapMode = System.Windows.Forms.DataGridViewTriState.False; + this.dgvAlarmConfigs.DefaultCellStyle = dataGridViewCellStyle4; + this.dgvAlarmConfigs.Dock = System.Windows.Forms.DockStyle.Fill; + this.dgvAlarmConfigs.EnableHeadersVisualStyles = false; + this.dgvAlarmConfigs.GridColor = System.Drawing.Color.FromArgb(((int)(((byte)(224)))), ((int)(((byte)(224)))), ((int)(((byte)(224))))); + this.dgvAlarmConfigs.Location = new System.Drawing.Point(0, 62); + this.dgvAlarmConfigs.Margin = new System.Windows.Forms.Padding(4); + this.dgvAlarmConfigs.MultiSelect = false; + this.dgvAlarmConfigs.Name = "dgvAlarmConfigs"; + this.dgvAlarmConfigs.ReadOnly = true; + this.dgvAlarmConfigs.RowHeadersVisible = false; + this.dgvAlarmConfigs.RowHeadersWidth = 30; + this.dgvAlarmConfigs.RowTemplate.Height = 35; + this.dgvAlarmConfigs.SelectionMode = System.Windows.Forms.DataGridViewSelectionMode.FullRowSelect; + this.dgvAlarmConfigs.Size = new System.Drawing.Size(900, 600); + this.dgvAlarmConfigs.TabIndex = 2; + this.dgvAlarmConfigs.CellDoubleClick += new System.Windows.Forms.DataGridViewCellEventHandler(this.dgvAlarmConfigs_CellDoubleClick); + // + // pnlListButtons + // + this.pnlListButtons.Controls.Add(this.lblStatistics); + this.pnlListButtons.Controls.Add(this.btnClose); + this.pnlListButtons.Controls.Add(this.btnRefresh); + this.pnlListButtons.Dock = System.Windows.Forms.DockStyle.Bottom; + this.pnlListButtons.Location = new System.Drawing.Point(0, 662); + this.pnlListButtons.Margin = new System.Windows.Forms.Padding(4); + this.pnlListButtons.Name = "pnlListButtons"; + this.pnlListButtons.Padding = new System.Windows.Forms.Padding(13, 12, 13, 12); + this.pnlListButtons.Size = new System.Drawing.Size(900, 88); + this.pnlListButtons.TabIndex = 1; + // + // lblStatistics + // + this.lblStatistics.AutoSize = true; + this.lblStatistics.Font = new System.Drawing.Font("寰蒋闆呴粦", 10F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + this.lblStatistics.Location = new System.Drawing.Point(20, 31); + this.lblStatistics.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); + this.lblStatistics.Name = "lblStatistics"; + this.lblStatistics.Size = new System.Drawing.Size(204, 24); + this.lblStatistics.TabIndex = 2; + this.lblStatistics.Text = "鎬绘暟: 0 | 鍚敤: 0 | 绂佺敤: 0"; + // + // btnClose + // + this.btnClose.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); + this.btnClose.Font = new System.Drawing.Font("寰蒋闆呴粦", 10F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + this.btnClose.Location = new System.Drawing.Point(753, 19); + this.btnClose.Margin = new System.Windows.Forms.Padding(4); + this.btnClose.Name = "btnClose"; + this.btnClose.Size = new System.Drawing.Size(120, 50); + this.btnClose.TabIndex = 1; + this.btnClose.Text = "鍏抽棴"; + this.btnClose.UseVisualStyleBackColor = true; + this.btnClose.Click += new System.EventHandler(this.btnClose_Click); + // + // btnRefresh + // + this.btnRefresh.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); + this.btnRefresh.Font = new System.Drawing.Font("寰蒋闆呴粦", 10F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + this.btnRefresh.Location = new System.Drawing.Point(620, 19); + this.btnRefresh.Margin = new System.Windows.Forms.Padding(4); + this.btnRefresh.Name = "btnRefresh"; + this.btnRefresh.Size = new System.Drawing.Size(120, 50); + this.btnRefresh.TabIndex = 0; + this.btnRefresh.Text = "鍒锋柊"; + this.btnRefresh.UseVisualStyleBackColor = true; + this.btnRefresh.Click += new System.EventHandler(this.btnRefresh_Click); + // + // pnlSearch + // + this.pnlSearch.Controls.Add(this.cmbLevelFilter); + this.pnlSearch.Controls.Add(this.lblLevelFilter); + this.pnlSearch.Controls.Add(this.txtSearch); + this.pnlSearch.Controls.Add(this.lblSearch); + this.pnlSearch.Dock = System.Windows.Forms.DockStyle.Top; + this.pnlSearch.Location = new System.Drawing.Point(0, 0); + this.pnlSearch.Margin = new System.Windows.Forms.Padding(4); + this.pnlSearch.Name = "pnlSearch"; + this.pnlSearch.Padding = new System.Windows.Forms.Padding(13, 12, 13, 12); + this.pnlSearch.Size = new System.Drawing.Size(900, 62); + this.pnlSearch.TabIndex = 0; + // + // cmbLevelFilter + // + this.cmbLevelFilter.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.cmbLevelFilter.Font = new System.Drawing.Font("寰蒋闆呴粦", 10F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + this.cmbLevelFilter.FormattingEnabled = true; + this.cmbLevelFilter.Location = new System.Drawing.Point(550, 16); + this.cmbLevelFilter.Margin = new System.Windows.Forms.Padding(4); + this.cmbLevelFilter.Name = "cmbLevelFilter"; + this.cmbLevelFilter.Size = new System.Drawing.Size(150, 31); + this.cmbLevelFilter.TabIndex = 3; + this.cmbLevelFilter.SelectedIndexChanged += new System.EventHandler(this.cmbLevelFilter_SelectedIndexChanged); + // + // lblLevelFilter + // + this.lblLevelFilter.AutoSize = true; + this.lblLevelFilter.Font = new System.Drawing.Font("寰蒋闆呴粦", 10F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + this.lblLevelFilter.Location = new System.Drawing.Point(463, 21); + this.lblLevelFilter.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); + this.lblLevelFilter.Name = "lblLevelFilter"; + this.lblLevelFilter.Size = new System.Drawing.Size(61, 23); + this.lblLevelFilter.TabIndex = 2; + this.lblLevelFilter.Text = "绾у埆锛"; + // + // txtSearch + // + this.txtSearch.Font = new System.Drawing.Font("寰蒋闆呴粦", 10F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + this.txtSearch.Location = new System.Drawing.Point(100, 16); + this.txtSearch.Margin = new System.Windows.Forms.Padding(4); + this.txtSearch.Name = "txtSearch"; + this.txtSearch.Size = new System.Drawing.Size(300, 29); + this.txtSearch.TabIndex = 1; + this.txtSearch.TextChanged += new System.EventHandler(this.txtSearch_TextChanged); + // + // lblSearch + // + this.lblSearch.AutoSize = true; + this.lblSearch.Font = new System.Drawing.Font("寰蒋闆呴粦", 10F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + this.lblSearch.Location = new System.Drawing.Point(13, 21); + this.lblSearch.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); + this.lblSearch.Name = "lblSearch"; + this.lblSearch.Size = new System.Drawing.Size(61, 23); + this.lblSearch.TabIndex = 0; + this.lblSearch.Text = "鎼滅储锛"; + // + // pnlEdit + // + this.pnlEdit.Controls.Add(this.grpEditInfo); + this.pnlEdit.Controls.Add(this.pnlEditButtons); + this.pnlEdit.Dock = System.Windows.Forms.DockStyle.Fill; + this.pnlEdit.Location = new System.Drawing.Point(0, 0); + this.pnlEdit.Margin = new System.Windows.Forms.Padding(4); + this.pnlEdit.Name = "pnlEdit"; + this.pnlEdit.Size = new System.Drawing.Size(495, 750); + this.pnlEdit.TabIndex = 0; + // + // grpEditInfo + // + this.grpEditInfo.Controls.Add(this.txtRemarks); + this.grpEditInfo.Controls.Add(this.lblRemarks); + this.grpEditInfo.Controls.Add(this.chkEnabled); + this.grpEditInfo.Controls.Add(this.cmbLevel); + this.grpEditInfo.Controls.Add(this.lblLevel); + this.grpEditInfo.Controls.Add(this.txtAlarmContent); + this.grpEditInfo.Controls.Add(this.lblAlarmContent); + this.grpEditInfo.Controls.Add(this.numAlarmCode); + this.grpEditInfo.Controls.Add(this.lblAlarmCode); + this.grpEditInfo.Controls.Add(this.txtAlarmId); + this.grpEditInfo.Controls.Add(this.lblAlarmId); + this.grpEditInfo.Dock = System.Windows.Forms.DockStyle.Fill; + this.grpEditInfo.Font = new System.Drawing.Font("寰蒋闆呴粦", 10F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + this.grpEditInfo.Location = new System.Drawing.Point(0, 0); + this.grpEditInfo.Margin = new System.Windows.Forms.Padding(4); + this.grpEditInfo.Name = "grpEditInfo"; + this.grpEditInfo.Padding = new System.Windows.Forms.Padding(20, 19, 20, 19); + this.grpEditInfo.Size = new System.Drawing.Size(495, 625); + this.grpEditInfo.TabIndex = 1; + this.grpEditInfo.TabStop = false; + this.grpEditInfo.Text = "鎶ヨ閰嶇疆淇℃伅"; + // + // txtRemarks + // + this.txtRemarks.Font = new System.Drawing.Font("寰蒋闆呴粦", 10F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + this.txtRemarks.Location = new System.Drawing.Point(130, 350); + this.txtRemarks.Margin = new System.Windows.Forms.Padding(4); + this.txtRemarks.Multiline = true; + this.txtRemarks.Name = "txtRemarks"; + this.txtRemarks.ScrollBars = System.Windows.Forms.ScrollBars.Vertical; + this.txtRemarks.Size = new System.Drawing.Size(330, 80); + this.txtRemarks.TabIndex = 10; + // + // lblRemarks + // + this.lblRemarks.Font = new System.Drawing.Font("寰蒋闆呴粦", 10F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + this.lblRemarks.Location = new System.Drawing.Point(27, 350); + this.lblRemarks.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); + this.lblRemarks.Name = "lblRemarks"; + this.lblRemarks.Size = new System.Drawing.Size(100, 31); + this.lblRemarks.TabIndex = 9; + this.lblRemarks.Text = "澶囨敞锛"; + this.lblRemarks.TextAlign = System.Drawing.ContentAlignment.TopRight; + // + // chkEnabled + // + this.chkEnabled.AutoSize = true; + this.chkEnabled.Checked = true; + this.chkEnabled.CheckState = System.Windows.Forms.CheckState.Checked; + this.chkEnabled.Font = new System.Drawing.Font("寰蒋闆呴粦", 10F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + this.chkEnabled.Location = new System.Drawing.Point(130, 300); + this.chkEnabled.Margin = new System.Windows.Forms.Padding(4); + this.chkEnabled.Name = "chkEnabled"; + this.chkEnabled.Size = new System.Drawing.Size(83, 27); + this.chkEnabled.TabIndex = 8; + this.chkEnabled.Text = "鍚敤涓"; + this.chkEnabled.UseVisualStyleBackColor = true; + this.chkEnabled.Visible = false; + // + // cmbLevel + // + this.cmbLevel.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.cmbLevel.Font = new System.Drawing.Font("寰蒋闆呴粦", 10F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + this.cmbLevel.FormattingEnabled = true; + this.cmbLevel.Location = new System.Drawing.Point(130, 244); + this.cmbLevel.Margin = new System.Windows.Forms.Padding(4); + this.cmbLevel.Name = "cmbLevel"; + this.cmbLevel.Size = new System.Drawing.Size(330, 31); + this.cmbLevel.TabIndex = 7; + // + // lblLevel + // + this.lblLevel.Font = new System.Drawing.Font("寰蒋闆呴粦", 10F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + this.lblLevel.Location = new System.Drawing.Point(27, 244); + this.lblLevel.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); + this.lblLevel.Name = "lblLevel"; + this.lblLevel.Size = new System.Drawing.Size(100, 31); + this.lblLevel.TabIndex = 6; + this.lblLevel.Text = "鎶ヨ绾у埆锛"; + this.lblLevel.TextAlign = System.Drawing.ContentAlignment.MiddleRight; + // + // txtAlarmContent + // + this.txtAlarmContent.Font = new System.Drawing.Font("寰蒋闆呴粦", 10F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + this.txtAlarmContent.Location = new System.Drawing.Point(130, 181); + this.txtAlarmContent.Margin = new System.Windows.Forms.Padding(4); + this.txtAlarmContent.Multiline = true; + this.txtAlarmContent.Name = "txtAlarmContent"; + this.txtAlarmContent.Size = new System.Drawing.Size(330, 50); + this.txtAlarmContent.TabIndex = 5; + // + // lblAlarmContent + // + this.lblAlarmContent.Font = new System.Drawing.Font("寰蒋闆呴粦", 10F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + this.lblAlarmContent.Location = new System.Drawing.Point(27, 181); + this.lblAlarmContent.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); + this.lblAlarmContent.Name = "lblAlarmContent"; + this.lblAlarmContent.Size = new System.Drawing.Size(100, 31); + this.lblAlarmContent.TabIndex = 4; + this.lblAlarmContent.Text = "鎶ヨ鍐呭锛"; + this.lblAlarmContent.TextAlign = System.Drawing.ContentAlignment.MiddleRight; + // + // numAlarmCode + // + this.numAlarmCode.Font = new System.Drawing.Font("寰蒋闆呴粦", 10F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + this.numAlarmCode.Location = new System.Drawing.Point(130, 119); + this.numAlarmCode.Margin = new System.Windows.Forms.Padding(4); + this.numAlarmCode.Maximum = new decimal(new int[] { + 99999, + 0, + 0, + 0}); + this.numAlarmCode.Name = "numAlarmCode"; + this.numAlarmCode.Size = new System.Drawing.Size(330, 29); + this.numAlarmCode.TabIndex = 3; + // + // lblAlarmCode + // + this.lblAlarmCode.Font = new System.Drawing.Font("寰蒋闆呴粦", 10F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + this.lblAlarmCode.Location = new System.Drawing.Point(27, 119); + this.lblAlarmCode.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); + this.lblAlarmCode.Name = "lblAlarmCode"; + this.lblAlarmCode.Size = new System.Drawing.Size(100, 31); + this.lblAlarmCode.TabIndex = 2; + this.lblAlarmCode.Text = "鎶ヨ缂栫爜锛"; + this.lblAlarmCode.TextAlign = System.Drawing.ContentAlignment.MiddleRight; + // + // txtAlarmId + // + this.txtAlarmId.BackColor = System.Drawing.Color.LightGray; + this.txtAlarmId.Font = new System.Drawing.Font("寰蒋闆呴粦", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + this.txtAlarmId.Location = new System.Drawing.Point(130, 56); + this.txtAlarmId.Margin = new System.Windows.Forms.Padding(4); + this.txtAlarmId.Name = "txtAlarmId"; + this.txtAlarmId.ReadOnly = true; + this.txtAlarmId.Size = new System.Drawing.Size(330, 27); + this.txtAlarmId.TabIndex = 1; + this.txtAlarmId.Visible = false; + // + // lblAlarmId + // + this.lblAlarmId.Font = new System.Drawing.Font("寰蒋闆呴粦", 10F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + this.lblAlarmId.Location = new System.Drawing.Point(27, 56); + this.lblAlarmId.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); + this.lblAlarmId.Name = "lblAlarmId"; + this.lblAlarmId.Size = new System.Drawing.Size(100, 31); + this.lblAlarmId.TabIndex = 0; + this.lblAlarmId.Text = "缂栧彿锛"; + this.lblAlarmId.TextAlign = System.Drawing.ContentAlignment.MiddleRight; + this.lblAlarmId.Visible = false; + // + // pnlEditButtons + // + this.pnlEditButtons.Controls.Add(this.btnCancel); + this.pnlEditButtons.Controls.Add(this.btnDelete); + this.pnlEditButtons.Controls.Add(this.btnSave); + this.pnlEditButtons.Dock = System.Windows.Forms.DockStyle.Bottom; + this.pnlEditButtons.Location = new System.Drawing.Point(0, 625); + this.pnlEditButtons.Margin = new System.Windows.Forms.Padding(4); + this.pnlEditButtons.Name = "pnlEditButtons"; + this.pnlEditButtons.Padding = new System.Windows.Forms.Padding(13, 12, 13, 12); + this.pnlEditButtons.Size = new System.Drawing.Size(495, 125); + this.pnlEditButtons.TabIndex = 0; + // + // btnCancel + // + this.btnCancel.Font = new System.Drawing.Font("寰蒋闆呴粦", 11F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + this.btnCancel.Location = new System.Drawing.Point(333, 25); + this.btnCancel.Margin = new System.Windows.Forms.Padding(4); + this.btnCancel.Name = "btnCancel"; + this.btnCancel.Size = new System.Drawing.Size(133, 62); + this.btnCancel.TabIndex = 2; + this.btnCancel.Text = "鍙栨秷"; + this.btnCancel.UseVisualStyleBackColor = true; + this.btnCancel.Click += new System.EventHandler(this.btnCancel_Click); + // + // btnDelete + // + this.btnDelete.BackColor = System.Drawing.Color.LightCoral; + this.btnDelete.Font = new System.Drawing.Font("寰蒋闆呴粦", 11F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + this.btnDelete.Location = new System.Drawing.Point(180, 25); + this.btnDelete.Margin = new System.Windows.Forms.Padding(4); + this.btnDelete.Name = "btnDelete"; + this.btnDelete.Size = new System.Drawing.Size(133, 62); + this.btnDelete.TabIndex = 1; + this.btnDelete.Text = "鍒犻櫎"; + this.btnDelete.UseVisualStyleBackColor = false; + this.btnDelete.Click += new System.EventHandler(this.btnDelete_Click); + // + // btnSave + // + this.btnSave.BackColor = System.Drawing.Color.LightBlue; + this.btnSave.Font = new System.Drawing.Font("寰蒋闆呴粦", 11F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + this.btnSave.Location = new System.Drawing.Point(27, 25); + this.btnSave.Margin = new System.Windows.Forms.Padding(4); + this.btnSave.Name = "btnSave"; + this.btnSave.Size = new System.Drawing.Size(133, 62); + this.btnSave.TabIndex = 0; + this.btnSave.Text = "鏂板"; + this.btnSave.UseVisualStyleBackColor = false; + this.btnSave.Click += new System.EventHandler(this.btnSave_Click); + // + // colAlarmId + // + this.colAlarmId.HeaderText = "缂栧彿"; + this.colAlarmId.MinimumWidth = 6; + this.colAlarmId.Name = "colAlarmId"; + this.colAlarmId.ReadOnly = true; + this.colAlarmId.Visible = false; + // + // colAlarmCode + // + this.colAlarmCode.HeaderText = "鎶ヨ缂栫爜"; + this.colAlarmCode.MinimumWidth = 6; + this.colAlarmCode.Name = "colAlarmCode"; + this.colAlarmCode.ReadOnly = true; + // + // colAlarmContent + // + this.colAlarmContent.HeaderText = "鎶ヨ鍐呭"; + this.colAlarmContent.MinimumWidth = 6; + this.colAlarmContent.Name = "colAlarmContent"; + this.colAlarmContent.ReadOnly = true; + // + // colLevel + // + this.colLevel.HeaderText = "绾у埆"; + this.colLevel.MinimumWidth = 6; + this.colLevel.Name = "colLevel"; + this.colLevel.ReadOnly = true; + // + // colEnabled + // + this.colEnabled.HeaderText = "鍚敤"; + this.colEnabled.MinimumWidth = 6; + this.colEnabled.Name = "colEnabled"; + this.colEnabled.ReadOnly = true; + // + // colRemarks + // + this.colRemarks.HeaderText = "澶囨敞"; + this.colRemarks.MinimumWidth = 6; + this.colRemarks.Name = "colRemarks"; + this.colRemarks.ReadOnly = true; + // + // AlarmConfigManagementForm + // + this.AutoScaleDimensions = new System.Drawing.SizeF(8F, 15F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(1400, 750); + this.Controls.Add(this.splitContainer); + this.Margin = new System.Windows.Forms.Padding(4); + this.MinimumSize = new System.Drawing.Size(1200, 600); + this.Name = "AlarmConfigManagementForm"; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen; + this.Text = "鎶ヨ閰嶇疆绠$悊"; + this.splitContainer.Panel1.ResumeLayout(false); + this.splitContainer.Panel2.ResumeLayout(false); + ((System.ComponentModel.ISupportInitialize)(this.splitContainer)).EndInit(); + this.splitContainer.ResumeLayout(false); + this.pnlList.ResumeLayout(false); + ((System.ComponentModel.ISupportInitialize)(this.dgvAlarmConfigs)).EndInit(); + this.pnlListButtons.ResumeLayout(false); + this.pnlListButtons.PerformLayout(); + this.pnlSearch.ResumeLayout(false); + this.pnlSearch.PerformLayout(); + this.pnlEdit.ResumeLayout(false); + this.grpEditInfo.ResumeLayout(false); + this.grpEditInfo.PerformLayout(); + ((System.ComponentModel.ISupportInitialize)(this.numAlarmCode)).EndInit(); + this.pnlEditButtons.ResumeLayout(false); + this.ResumeLayout(false); + + } + + #endregion + + private System.Windows.Forms.SplitContainer splitContainer; + private System.Windows.Forms.Panel pnlList; + private System.Windows.Forms.DataGridView dgvAlarmConfigs; + private System.Windows.Forms.Panel pnlListButtons; + private System.Windows.Forms.Label lblStatistics; + private System.Windows.Forms.Button btnClose; + private System.Windows.Forms.Button btnRefresh; + private System.Windows.Forms.Panel pnlSearch; + private System.Windows.Forms.ComboBox cmbLevelFilter; + private System.Windows.Forms.Label lblLevelFilter; + private System.Windows.Forms.TextBox txtSearch; + private System.Windows.Forms.Label lblSearch; + private System.Windows.Forms.Panel pnlEdit; + private System.Windows.Forms.GroupBox grpEditInfo; + private System.Windows.Forms.TextBox txtRemarks; + private System.Windows.Forms.Label lblRemarks; + private System.Windows.Forms.CheckBox chkEnabled; + private System.Windows.Forms.ComboBox cmbLevel; + private System.Windows.Forms.Label lblLevel; + private System.Windows.Forms.TextBox txtAlarmContent; + private System.Windows.Forms.Label lblAlarmContent; + private System.Windows.Forms.NumericUpDown numAlarmCode; + private System.Windows.Forms.Label lblAlarmCode; + private System.Windows.Forms.TextBox txtAlarmId; + private System.Windows.Forms.Label lblAlarmId; + private System.Windows.Forms.Panel pnlEditButtons; + private System.Windows.Forms.Button btnCancel; + private System.Windows.Forms.Button btnDelete; + private System.Windows.Forms.Button btnSave; + private System.Windows.Forms.DataGridViewTextBoxColumn colAlarmId; + private System.Windows.Forms.DataGridViewTextBoxColumn colAlarmCode; + private System.Windows.Forms.DataGridViewTextBoxColumn colAlarmContent; + private System.Windows.Forms.DataGridViewTextBoxColumn colLevel; + private System.Windows.Forms.DataGridViewTextBoxColumn colEnabled; + private System.Windows.Forms.DataGridViewTextBoxColumn colRemarks; + } +} + diff --git a/StandardScene.Core/Charge/AlarmConfigManagementForm.cs b/StandardScene.Core/Charge/AlarmConfigManagementForm.cs new file mode 100644 index 0000000..9f50a2e --- /dev/null +++ b/StandardScene.Core/Charge/AlarmConfigManagementForm.cs @@ -0,0 +1,547 @@ +using System; +using System.Drawing; +using System.Linq; +using System.Windows.Forms; + +namespace StandardScene.Charge +{ + /// + /// 鎶ヨ閰嶇疆绠$悊绐椾綋 + /// + public partial class AlarmConfigManagementForm : Form + { + private readonly AlarmConfigDataService dataService; + private AlarmConfig selectedAlarmConfig; + + public AlarmConfigManagementForm() + { + try + { + InitializeComponent(); + dataService = AlarmConfigDataService.Instance; + + // 璁㈤槄Load浜嬩欢锛岀‘淇濇墍鏈夋帶浠堕兘宸插垵濮嬪寲鍚庡啀鍔犺浇鏁版嵁 + this.Load += AlarmConfigManagementForm_Load; + } + catch (Exception ex) + { + MessageBox.Show($"鍒濆鍖栨姤璀﹂厤缃鐞嗙獥浣撳け璐: {ex.Message}\n\n璇︾粏淇℃伅:\n{ex.StackTrace}", + "閿欒", MessageBoxButtons.OK, MessageBoxIcon.Error); + } + } + + /// + /// 绐椾綋鍔犺浇浜嬩欢 + /// + private void AlarmConfigManagementForm_Load(object sender, EventArgs e) + { + InitializeForm(); + } + + /// + /// 鍒濆鍖栫獥浣 + /// + private void InitializeForm() + { + try + { + // 鍒濆鍖栨姤璀︾骇鍒笅鎷夋 + if (cmbLevel != null) + { + cmbLevel.Items.Clear(); + cmbLevel.Items.Add("鏃"); + cmbLevel.Items.Add("浣"); + cmbLevel.Items.Add("涓"); + cmbLevel.Items.Add("楂"); + cmbLevel.Items.Add("涓ラ噸"); + cmbLevel.SelectedIndex = 2; // 榛樿閫夋嫨"涓" + } + + // 鍒濆鍖栫骇鍒瓫閫変笅鎷夋 + if (cmbLevelFilter != null) + { + cmbLevelFilter.Items.Clear(); + cmbLevelFilter.Items.Add("鍏ㄩ儴"); + cmbLevelFilter.Items.Add("鏃"); + cmbLevelFilter.Items.Add("浣"); + cmbLevelFilter.Items.Add("涓"); + cmbLevelFilter.Items.Add("楂"); + cmbLevelFilter.Items.Add("涓ラ噸"); + cmbLevelFilter.SelectedIndex = 0; + } + + LoadAlarmConfigs(); + ClearEditFields(); + } + catch (Exception ex) + { + MessageBox.Show($"鍒濆鍖栫獥浣撳け璐: {ex.Message}\n\n{ex.StackTrace}", "閿欒", + MessageBoxButtons.OK, MessageBoxIcon.Error); + } + } + + /// + /// 鍔犺浇鎶ヨ閰嶇疆鍒楄〃 + /// + private void LoadAlarmConfigs() + { + try + { + if (dgvAlarmConfigs == null) + { + return; // 鎺т欢杩樻湭鍒濆鍖栵紝鐩存帴杩斿洖 + } + + var alarmConfigs = dataService.GetAllAlarmConfigs(); + + if (alarmConfigs == null) + { + alarmConfigs = new System.Collections.Generic.List(); + } + + // 鏍规嵁绾у埆绛涢 + if (cmbLevelFilter != null && cmbLevelFilter.SelectedIndex > 0) + { + var filterLevel = (AlarmLevel)(cmbLevelFilter.SelectedIndex - 1); + alarmConfigs = alarmConfigs.Where(a => a.Level == filterLevel).ToList(); + } + + // 鏍规嵁鎼滅储鏂囨湰绛涢 + if (txtSearch != null && !string.IsNullOrWhiteSpace(txtSearch.Text)) + { + var searchText = txtSearch.Text.Trim().ToLower(); + alarmConfigs = alarmConfigs.Where(a => + a.AlarmId.ToLower().Contains(searchText) || + a.AlarmCode.ToString().Contains(searchText) || + a.AlarmContent.ToLower().Contains(searchText) + ).ToList(); + } + + dgvAlarmConfigs.Rows.Clear(); + + foreach (var alarm in alarmConfigs) + { + var index = dgvAlarmConfigs.Rows.Add( + alarm.AlarmId, + alarm.AlarmCode, + alarm.AlarmContent, + GetLevelText(alarm.Level), + alarm.Enabled ? "鏄" : "鍚", + alarm.Remarks + ); + + // 鏍规嵁绾у埆璁剧疆琛岄鑹 + var row = dgvAlarmConfigs.Rows[index]; + switch (alarm.Level) + { + case AlarmLevel.Critical: + row.DefaultCellStyle.BackColor = Color.FromArgb(255, 235, 238); // 娴呯孩鑹 + row.DefaultCellStyle.ForeColor = Color.FromArgb(183, 28, 28); + // 瀹夊叏鍦板垱寤虹矖浣撳瓧浣 + var baseFont = row.DefaultCellStyle.Font ?? dgvAlarmConfigs.DefaultCellStyle.Font ?? new Font("寰蒋闆呴粦", 9F); + row.DefaultCellStyle.Font = new Font(baseFont, FontStyle.Bold); + break; + case AlarmLevel.High: + row.DefaultCellStyle.BackColor = Color.FromArgb(255, 243, 224); // 娴呮鑹 + row.DefaultCellStyle.ForeColor = Color.FromArgb(230, 81, 0); + break; + case AlarmLevel.Medium: + row.DefaultCellStyle.BackColor = Color.FromArgb(255, 249, 196); // 娴呴粍鑹 + row.DefaultCellStyle.ForeColor = Color.FromArgb(245, 127, 23); + break; + case AlarmLevel.Low: + row.DefaultCellStyle.BackColor = Color.FromArgb(232, 245, 233); // 娴呯豢鑹 + row.DefaultCellStyle.ForeColor = Color.FromArgb(46, 125, 50); + break; + } + + // 濡傛灉鏈惎鐢紝鏄剧ず涓虹伆鑹 + if (!alarm.Enabled) + { + row.DefaultCellStyle.BackColor = Color.FromArgb(238, 238, 238); + row.DefaultCellStyle.ForeColor = Color.FromArgb(158, 158, 158); + } + } + + UpdateStatistics(); + UpdateTitleWithFilter(alarmConfigs.Count); + } + catch (Exception ex) + { + MessageBox.Show($"鍔犺浇鏁版嵁澶辫触: {ex.Message}", "閿欒", + MessageBoxButtons.OK, MessageBoxIcon.Error); + } + } + + /// + /// 鏇存柊缁熻淇℃伅 + /// + private void UpdateStatistics() + { + try + { + if (lblStatistics == null) + { + return; + } + + var alarmConfigs = dataService.GetAllAlarmConfigs(); + if (alarmConfigs == null) + { + alarmConfigs = new System.Collections.Generic.List(); + } + + var total = alarmConfigs.Count; + var enabled = alarmConfigs.Count(a => a.Enabled); + var disabled = total - enabled; + var critical = alarmConfigs.Count(a => a.Level == AlarmLevel.Critical); + var high = alarmConfigs.Count(a => a.Level == AlarmLevel.High); + + lblStatistics.Text = $"鎬绘暟: {total} | 鍚敤: {enabled} | 绂佺敤: {disabled} | 涓ラ噸: {critical} | 楂樼骇: {high}"; + } + catch (Exception ex) + { + System.Diagnostics.Debug.WriteLine($"鏇存柊缁熻淇℃伅澶辫触: {ex.Message}"); + } + } + + /// + /// 鏇存柊鏍囬鏄剧ず绛涢変俊鎭 + /// + private void UpdateTitleWithFilter(int displayCount) + { + try + { + var allConfigs = dataService.GetAllAlarmConfigs(); + var totalCount = allConfigs != null ? allConfigs.Count : 0; + + if (cmbLevelFilter != null && cmbLevelFilter.SelectedIndex > 0) + { + this.Text = $"鎶ヨ閰嶇疆绠$悊 - 鏄剧ず: {displayCount}/{totalCount} ({cmbLevelFilter.Text})"; + } + else + { + this.Text = $"鎶ヨ閰嶇疆绠$悊 - 鎬绘暟: {totalCount}"; + } + } + catch (Exception ex) + { + System.Diagnostics.Debug.WriteLine($"鏇存柊鏍囬澶辫触: {ex.Message}"); + this.Text = "鎶ヨ閰嶇疆绠$悊"; + } + } + + /// + /// 鑾峰彇绾у埆鏂囨湰 + /// + private string GetLevelText(AlarmLevel level) + { + switch (level) + { + case AlarmLevel.None: return "鏃"; + case AlarmLevel.Low: return "浣"; + case AlarmLevel.Medium: return "涓"; + case AlarmLevel.High: return "楂"; + case AlarmLevel.Critical: return "涓ラ噸"; + default: return "鏈煡"; + } + } + + /// + /// 娓呯┖缂栬緫瀛楁 + /// + private void ClearEditFields() + { + try + { + selectedAlarmConfig = null; + + if (txtAlarmId != null) + { + txtAlarmId.Text = ""; + txtAlarmId.Enabled = false; // 鏂板鏃剁紪鍙疯嚜鍔ㄧ敓鎴 + } + + if (numAlarmCode != null) + { + numAlarmCode.Value = 0; + numAlarmCode.Enabled = true; + numAlarmCode.ReadOnly = false; + } + + if (txtAlarmContent != null) + { + txtAlarmContent.Text = ""; + txtAlarmContent.Enabled = true; + txtAlarmContent.ReadOnly = false; + } + + if (cmbLevel != null) + { + cmbLevel.SelectedIndex = 2; // 涓 + cmbLevel.Enabled = true; + } + + if (chkEnabled != null) + { + chkEnabled.Checked = true; + chkEnabled.Enabled = true; + } + + if (txtRemarks != null) + { + txtRemarks.Text = ""; + txtRemarks.Enabled = true; + txtRemarks.ReadOnly = false; + } + + if (btnSave != null) + { + btnSave.Text = "鏂板"; + btnSave.Enabled = true; + } + + if (btnDelete != null) + { + btnDelete.Enabled = false; + } + } + catch (Exception ex) + { + System.Diagnostics.Debug.WriteLine($"娓呯┖缂栬緫瀛楁澶辫触: {ex.Message}"); + } + } + + /// + /// 浠庡瓧娈靛垱寤烘姤璀﹂厤缃 + /// + private AlarmConfig CreateAlarmConfigFromFields() + { + var alarmConfig = selectedAlarmConfig ?? new AlarmConfig(); + + alarmConfig.AlarmCode = (int)numAlarmCode.Value; + alarmConfig.AlarmContent = txtAlarmContent.Text.Trim(); + alarmConfig.Level = (AlarmLevel)cmbLevel.SelectedIndex; + alarmConfig.Enabled = chkEnabled.Checked; + alarmConfig.Remarks = txtRemarks.Text.Trim(); + + return alarmConfig; + } + + /// + /// 鍔犺浇鎶ヨ閰嶇疆鍒扮紪杈戝尯 + /// + private void LoadAlarmConfigToFields(AlarmConfig alarmConfig) + { + try + { + selectedAlarmConfig = alarmConfig; + + // 濉厖鏁版嵁 + if (txtAlarmId != null) + { + txtAlarmId.Text = alarmConfig.AlarmId; + txtAlarmId.Enabled = false; // 缂栧彿涓嶅彲淇敼 + } + + if (numAlarmCode != null) + { + numAlarmCode.Value = alarmConfig.AlarmCode; + numAlarmCode.Enabled = true; + numAlarmCode.ReadOnly = false; + } + + if (txtAlarmContent != null) + { + txtAlarmContent.Text = alarmConfig.AlarmContent; + txtAlarmContent.Enabled = true; + txtAlarmContent.ReadOnly = false; + } + + if (cmbLevel != null) + { + cmbLevel.SelectedIndex = (int)alarmConfig.Level; + cmbLevel.Enabled = true; + } + + if (chkEnabled != null) + { + chkEnabled.Checked = alarmConfig.Enabled; + chkEnabled.Enabled = true; + } + + if (txtRemarks != null) + { + txtRemarks.Text = alarmConfig.Remarks ?? ""; + txtRemarks.Enabled = true; + txtRemarks.ReadOnly = false; + } + + // 璁剧疆鎸夐挳鐘舵 + if (btnSave != null) + { + btnSave.Text = "淇濆瓨"; + btnSave.Enabled = true; + } + + if (btnDelete != null) + { + btnDelete.Enabled = true; + } + } + catch (Exception ex) + { + MessageBox.Show($"鍔犺浇鏁版嵁鍒扮紪杈戝尯澶辫触: {ex.Message}\n\n{ex.StackTrace}", "閿欒", + MessageBoxButtons.OK, MessageBoxIcon.Error); + } + } + + // ==================== 浜嬩欢澶勭悊 ==================== + + private void btnSave_Click(object sender, EventArgs e) + { + try + { + // 楠岃瘉鎶ヨ缂栫爜 + if (numAlarmCode.Value < 0) + { + MessageBox.Show("鎶ヨ缂栫爜涓嶈兘涓鸿礋鏁", "楠岃瘉澶辫触", + MessageBoxButtons.OK, MessageBoxIcon.Warning); + numAlarmCode.Focus(); + return; + } + + // 楠岃瘉鎶ヨ鍐呭 + if (string.IsNullOrWhiteSpace(txtAlarmContent.Text)) + { + MessageBox.Show("鎶ヨ鍐呭涓嶈兘涓虹┖", "楠岃瘉澶辫触", + MessageBoxButtons.OK, MessageBoxIcon.Warning); + txtAlarmContent.Focus(); + return; + } + + var alarmConfig = CreateAlarmConfigFromFields(); + string errorMessage; + + bool success; + if (selectedAlarmConfig == null) + { + // 鏂板 + success = dataService.AddAlarmConfig(alarmConfig, out errorMessage); + } + else + { + // 鏇存柊 + success = dataService.UpdateAlarmConfig(alarmConfig, out errorMessage); + } + + if (success) + { + MessageBox.Show("淇濆瓨鎴愬姛锛", "鎻愮ず", + MessageBoxButtons.OK, MessageBoxIcon.Information); + LoadAlarmConfigs(); + ClearEditFields(); + } + else + { + MessageBox.Show($"淇濆瓨澶辫触: {errorMessage}", "閿欒", + MessageBoxButtons.OK, MessageBoxIcon.Error); + } + } + catch (Exception ex) + { + MessageBox.Show($"淇濆瓨澶辫触: {ex.Message}", "閿欒", + MessageBoxButtons.OK, MessageBoxIcon.Error); + } + } + + private void btnDelete_Click(object sender, EventArgs e) + { + if (selectedAlarmConfig == null) + { + MessageBox.Show("璇峰厛閫夋嫨瑕佸垹闄ょ殑鎶ヨ閰嶇疆", "鎻愮ず", + MessageBoxButtons.OK, MessageBoxIcon.Warning); + return; + } + + var result = MessageBox.Show( + $"纭畾瑕佸垹闄ゆ姤璀﹂厤缃 [{selectedAlarmConfig.AlarmCode}] {selectedAlarmConfig.AlarmContent} 鍚楋紵", + "纭鍒犻櫎", + MessageBoxButtons.YesNo, + MessageBoxIcon.Question); + + if (result == DialogResult.Yes) + { + if (dataService.DeleteAlarmConfig(selectedAlarmConfig.AlarmId, out string errorMessage)) + { + MessageBox.Show("鍒犻櫎鎴愬姛锛", "鎻愮ず", + MessageBoxButtons.OK, MessageBoxIcon.Information); + LoadAlarmConfigs(); + ClearEditFields(); + } + else + { + MessageBox.Show($"鍒犻櫎澶辫触: {errorMessage}", "閿欒", + MessageBoxButtons.OK, MessageBoxIcon.Error); + } + } + } + + private void btnCancel_Click(object sender, EventArgs e) + { + ClearEditFields(); + } + + private void btnRefresh_Click(object sender, EventArgs e) + { + dataService.Reload(); + LoadAlarmConfigs(); + } + + private void btnClose_Click(object sender, EventArgs e) + { + this.Close(); + } + + private void dgvAlarmConfigs_CellDoubleClick(object sender, DataGridViewCellEventArgs e) + { + try + { + if (e.RowIndex >= 0 && e.RowIndex < dgvAlarmConfigs.Rows.Count) + { + var row = dgvAlarmConfigs.Rows[e.RowIndex]; + if (row.Cells[0].Value != null) + { + var alarmId = row.Cells[1].Value.ToString(); + var alarmConfig = dataService.GetAlarmConfigAlarmCode(int.Parse(alarmId)); + if (alarmConfig != null) + { + LoadAlarmConfigToFields(alarmConfig); + } + else + { + MessageBox.Show($"鏈壘鍒版姤璀﹂厤缃: {alarmId}", "鎻愮ず", + MessageBoxButtons.OK, MessageBoxIcon.Warning); + } + } + } + } + catch (Exception ex) + { + MessageBox.Show($"鍔犺浇鎶ヨ閰嶇疆澶辫触: {ex.Message}", "閿欒", + MessageBoxButtons.OK, MessageBoxIcon.Error); + } + } + + private void txtSearch_TextChanged(object sender, EventArgs e) + { + LoadAlarmConfigs(); + } + + private void cmbLevelFilter_SelectedIndexChanged(object sender, EventArgs e) + { + LoadAlarmConfigs(); + } + } +} + diff --git a/StandardScene.Core/Charge/AlarmConfigManagementForm.resx b/StandardScene.Core/Charge/AlarmConfigManagementForm.resx new file mode 100644 index 0000000..1af7de1 --- /dev/null +++ b/StandardScene.Core/Charge/AlarmConfigManagementForm.resx @@ -0,0 +1,120 @@ +锘 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/StandardScene.Core/Charge/ChargeStation.cs b/StandardScene.Core/Charge/ChargeStation.cs new file mode 100644 index 0000000..8da1643 --- /dev/null +++ b/StandardScene.Core/Charge/ChargeStation.cs @@ -0,0 +1,429 @@ +using System; +using System.ComponentModel; +using Newtonsoft.Json; + +namespace StandardScene.Charge +{ + /// + /// 鍏呯數妗╂暟鎹ā鍨 + /// + public class ChargeStation + { + /// + /// 鍏呯數妗╃紪鍙凤紙鍞竴鏍囪瘑锛 + /// + [DisplayName("缂栧彿")] + public string StationId { get; set; } = "1"; + + /// + /// 鍏呯數妗╁悕绉 + /// + [DisplayName("鍚嶇О")] + public string Name { get; set; } + + /// + /// 鍏呯數妗╃被鍨 + /// + [DisplayName("绫诲瀷")] + public ChargeStationType Type { get; set; } + + /// + /// 鍏呯數鏂瑰紡 + /// + [DisplayName("鍏呯數鏂瑰紡")] + public ChargeMethodType ChargeMethod { get; set; } + + /// + /// IP鍦板潃 + /// + [DisplayName("IP鍦板潃")] + public string IpAddress { get; set; } + + /// + /// 绔彛鍙 + /// + [DisplayName("绔彛")] + public int Port { get; set; } + + /// + /// 閫氳绫诲瀷 (UDP/TCP) + /// + [DisplayName("閫氳绫诲瀷")] + public string CommunicationType { get; set; } = "TCP"; + + /// + /// 棰濆畾鐢靛帇 (V) + /// + [DisplayName("鐢靛帇(V)")] + public double SetVoltage { get; set; } + + /// + /// 棰濆畾鐢垫祦 (A) + /// + [DisplayName("鐢垫祦(A)")] + public double SetElectricCurrent { get; set; } + + /// + /// 瀹炴椂鐢靛帇 (V) - 褰撳墠鍏呯數鏃剁殑瀹為檯鐢靛帇 + /// + [DisplayName("瀹炴椂鐢靛帇(V)")] + [JsonIgnore] + public double RealTimeVoltage { get; set; } + + /// + /// 瀹炴椂鐢垫祦 (A) - 褰撳墠鍏呯數鏃剁殑瀹為檯鐢垫祦 + /// + [DisplayName("瀹炴椂鐢垫祦(A)")] + [JsonIgnore] + public double RealTimeCurrent { get; set; } + + /// + /// 鏈鍚庡彂閫佹暟鎹椂闂 + /// + [DisplayName("鍙戦佹椂闂")] + [JsonIgnore] + public DateTime? LastSendTime { get; set; } + + /// + /// 鏈鍚庢帴鏀舵暟鎹椂闂 + /// + [DisplayName("鎺ユ敹鏃堕棿")] + [JsonIgnore] + public DateTime? LastReceiveTime { get; set; } + + /// + /// 鏄惁鏈夋姤璀 + /// + [DisplayName("鎶ヨ")] + [JsonIgnore] + public bool HasAlarm { get; set; } + + /// + /// 鎶ヨ淇℃伅 + /// + [DisplayName("鎶ヨ淇℃伅")] + [JsonIgnore] + public string AlarmMessage { get; set; } + + /// + /// 鎶ヨ绾у埆 + /// + [DisplayName("鎶ヨ绾у埆")] + public AlarmLevel AlarmLevel { get; set; } + + /// + /// 缃戠粶閫氳鐘舵 + /// + [DisplayName("閫氳鐘舵")] + [JsonIgnore] + public CommunicationStatus CommStatus { get; set; } + + /// + /// 鏈鍚庨氳鎴愬姛鏃堕棿 + /// + + [JsonIgnore] + [DisplayName("鏈鍚庨氳鏃堕棿")] + public DateTime? LastCommunicationTime { get; set; } + + /// + /// 鏈烘瀯浼哥缉鐘舵 + /// + [JsonIgnore] + [DisplayName("鏈烘瀯鐘舵")] + public MechanismStatus MechanismStatus { get; set; } + + [DisplayName("灞忚斀鏈烘瀯鐘舵佷氦浜")] + public bool ShieldSiteMechanismStatus { get; set; } + + + /// + /// 褰撳墠鍏呯數杞﹁締缂栧彿 + /// + [DisplayName("褰撳墠杞﹁締")] + [JsonIgnore] + public string CurrentVehicle { get; set; } + + /// + /// 褰撳墠鐢甸噺鐧惧垎姣 (0-100) + /// + [DisplayName("鐢甸噺")] + [JsonIgnore] + public double BatteryLevel { get; set; } + + /// + /// 鍙戦佸厖鐢电殑鐘舵 + /// + [DisplayName("鍏呯數鎸囦护鐘舵")] + [JsonIgnore] + public ChargeCommandStatus ChargeCommandStatus { get; set; } + + /// + /// 鍏呯數妗╃姸鎬 + /// + [DisplayName("鐘舵")] + [JsonIgnore] + public ChargeStationStatus Status { get; set; } + + /// + /// 鏄惁鍚敤 + /// + [DisplayName("鍚敤")] + public bool Enabled { get; set; } + + [DisplayName("鍋滈潬杞﹁締绫诲瀷")] + public ChargeStationCarType GroupCarType { get; set; } + + /// + /// 鍏宠仈鐨勭珯鐐笽D锛堝彲閫夛級 + /// + [DisplayName("绔欑偣ID")] + public int? SiteId { get; set; } + + /// + /// 澶囨敞 + /// + [DisplayName("澶囨敞")] + public string Remarks { get; set; } + + /// + /// 鍒涘缓鏃堕棿 + /// + [DisplayName("鍒涘缓鏃堕棿")] + public DateTime CreatedTime { get; set; } + + /// + /// 鏈鍚庝慨鏀规椂闂 + /// + [DisplayName("淇敼鏃堕棿")] + public DateTime ModifiedTime { get; set; } + + /// + /// 璁$畻鍔熺巼 (W) + /// + [JsonIgnore] + [DisplayName("鍔熺巼(W)")] + public double Power => SetVoltage * SetElectricCurrent; + + public ChargeStation() + { + StationId = GenerateStationId(); + Type = ChargeStationType.FRLDTall; // 榛樿FRLD楂樻鍏呯數妗 + ChargeMethod = ChargeMethodType.Ground; // 榛樿鍦板厖 + Status = ChargeStationStatus.Idle; + Enabled = true; + CreatedTime = DateTime.Now; + ModifiedTime = DateTime.Now; + Port = 502; // 榛樿Modbus TCP绔彛 + CommunicationType = "UDP"; // 榛樿UDP閫氳 + SetVoltage = 29.2; + SetElectricCurrent = 45.0; + } + + /// + /// 鐢熸垚鍏呯數妗╃紪鍙 + /// + private static string GenerateStationId() + { + return "1"; + //return $"CS{DateTime.Now:yyyyMMddHHmmss}{new Random().Next(1000, 9999)}"; + } + + /// + /// 楠岃瘉鏁版嵁鏈夋晥鎬 + /// + public bool IsValid(out string errorMessage) + { + if (string.IsNullOrWhiteSpace(StationId)) + { + errorMessage = "鍏呯數妗╃紪鍙蜂笉鑳戒负绌"; + return false; + } + + if (string.IsNullOrWhiteSpace(Name)) + { + errorMessage = "鍏呯數妗╁悕绉颁笉鑳戒负绌"; + return false; + } + + if (string.IsNullOrWhiteSpace(IpAddress)) + { + errorMessage = "IP鍦板潃涓嶈兘涓虹┖"; + return false; + } + + // 楠岃瘉IP鏍煎紡 + if (!System.Net.IPAddress.TryParse(IpAddress, out _)) + { + errorMessage = "IP鍦板潃鏍煎紡涓嶆纭"; + return false; + } + + // 楠岃瘉绔彛鑼冨洿 + if (Port < 1 || Port > 65535) + { + errorMessage = "绔彛鍙峰繀椤诲湪 1-65535 涔嬮棿"; + return false; + } + + // 楠岃瘉鐢靛帇鑼冨洿 + if (SetVoltage <= 0 || SetVoltage > 64) + { + errorMessage = "鐢靛帇蹇呴』鍦 0-64V 涔嬮棿"; + return false; + } + + // 楠岃瘉鐢垫祦鑼冨洿 + if (SetElectricCurrent <= 0 || SetElectricCurrent > 101) + { + errorMessage = "鐢垫祦蹇呴』鍦 0-101A 涔嬮棿"; + return false; + } + + errorMessage = string.Empty; + return true; + } + + public override string ToString() + { + return $"[{StationId}] {Name} ({IpAddress}:{Port}) - {Status}"; + } + } + + /// + /// 鍏呯數妗╃被鍨嬫灇涓 + /// + public enum ChargeStationType + { + [Description("FRLD楂樻鍏呯數妗")] + FRLDTall = 0, + + [Description("FRLD鐭鍏呯數妗")] + FRLDShort = 1, + + [Description("鐗ф槦鍏呯數妗")] + MuXing = 2 + + // 鍚庣画鍙湪姝ゅ娣诲姞鍏朵粬鍏呯數妗╃被鍨 + } + + public enum ChargeStationCarType + { + [Description("FRLD鍏呯數")] + FRLD = 0, + + [Description("鐗ф槦鍏呯數妗╁厖鐢")] + MuXing = 1 + + // 鍚庣画鍙湪姝ゅ娣诲姞鍏朵粬鍏呯數妗╃被鍨 + } + /// + /// 鍏呯數妗╃姸鎬佹灇涓 + /// + public enum ChargeStationStatus + { + [Description("绌洪棽")] + Idle = 0, + + [Description("鍏呯數涓")] + Charging = 1, + + [Description("鎶ヨ涓")] + Fault = 2, + + [Description("AGV鐢垫睜宸叉帴鍏")] + Battery = 3 + } + + /// + /// 鎶ヨ绾у埆鏋氫妇 + /// + public enum AlarmLevel + { + [Description("鏃")] + None = 0, + + [Description("浣")] + Low = 1, + + [Description("涓")] + Medium = 2, + + [Description("楂")] + High = 3, + + [Description("涓ラ噸")] + Critical = 4 + } + + /// + /// 鏈烘瀯浼哥缉鐘舵佹灇涓 + /// + public enum MechanismStatus + { + [Description("浼稿嚭")] + Extended = 1, + + [Description("缂╁洖")] + Retracted = 2, + + [Description("杩愬姩涓")] + Extending = 3, + + } + + /// + /// 缃戠粶閫氳鐘舵佹灇涓 + /// + public enum CommunicationStatus + { + [Description("鏈煡")] + Unknown = 0, + + [Description("姝e父")] + Normal = 1, + + [Description("寤惰繜")] + Delayed = 2, + + [Description("瓒呮椂")] + Timeout = 3, + + [Description("鏂紑")] + Disconnected = 4, + + [Description("閿欒")] + Error = 5 + } + + /// + /// 鍏呯數鎸囦护鐘舵佹灇涓 + /// + public enum ChargeCommandStatus + { + [Description("鍋滄")] + Stopped = 0, + + [Description("鍚姩")] + Started = 1 + } + + + /// + /// 鍏呯數鏂瑰紡鏋氫妇 + /// + public enum ChargeMethodType + { + [Description("鍦板厖")] + Ground = 0, + + [Description("灏惧厖")] + Rear = 1, + + [Description("渚у厖")] + Side = 2 + } +} + + + diff --git a/StandardScene.Core/Charge/ChargeStationDataService.cs b/StandardScene.Core/Charge/ChargeStationDataService.cs new file mode 100644 index 0000000..45e418f --- /dev/null +++ b/StandardScene.Core/Charge/ChargeStationDataService.cs @@ -0,0 +1,386 @@ +using DocumentFormat.OpenXml.Bibliography; +using Newtonsoft.Json; +using SimpleCore; +using SimpleCore.Library; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; + +namespace StandardScene.Charge +{ + /// + /// 鍏呯數妗╂暟鎹湇鍔 - 璐熻矗鏁版嵁鐨勬寔涔呭寲鍜岀鐞 + /// + public class ChargeStationDataService + { + private static ChargeStationDataService _instance; + private static readonly object lockObj = new object(); + + private List chargeStations; + private readonly string dataFilePath; + + // 鍗曚緥妯″紡 + public static ChargeStationDataService Instance + { + get + { + if (_instance == null) + { + lock (lockObj) + { + if (_instance == null) + { + _instance = new ChargeStationDataService(); + } + } + } + return _instance; + } + } + + private ChargeStationDataService() + { + // 鏁版嵁鏂囦欢璺緞锛氶」鐩牴鐩綍/Config/ChargeStations.json + var dataDir = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Config"); + if (!Directory.Exists(dataDir)) + { + Directory.CreateDirectory(dataDir); + } + + dataFilePath = Path.Combine(dataDir, "ChargeStations.json"); + chargeStations = new List(); + + LoadData(); + } + + /// + /// 鍔犺浇鏁版嵁 + /// + private void LoadData() + { + try + { + if (File.Exists(dataFilePath)) + { + var json = File.ReadAllText(dataFilePath); + chargeStations = JsonConvert.DeserializeObject>(json) + ?? new List(); + Diagnosis.Log($"鍔犺浇鍏呯數妗╂暟鎹垚鍔燂紝鍏 {chargeStations.Count} 鏉¤褰", "ChargeStation"); + } + else + { + chargeStations = new List(); + Diagnosis.Log("鍏呯數妗╂暟鎹枃浠朵笉瀛樺湪锛屽凡鍒涘缓鏂板垪琛", "ChargeStation"); + } + } + catch (Exception ex) + { + Diagnosis.Log($"鍔犺浇鍏呯數妗╂暟鎹け璐: {ExceptionFormatter.FormatEx(ex)}", "ChargeStation", true); + chargeStations = new List(); + } + } + + /// + /// 淇濆瓨鏁版嵁 + /// + private bool SaveData() + { + try + { + lock (lockObj) + { + var json = JsonConvert.SerializeObject(chargeStations, Formatting.Indented); + File.WriteAllText(dataFilePath, json); + //Diagnosis.Log($"淇濆瓨鍏呯數妗╂暟鎹垚鍔燂紝鍏 {chargeStations.Count} 鏉¤褰", "ChargeStation"); + return true; + } + } + catch (Exception ex) + { + Diagnosis.Log($"淇濆瓨鍏呯數妗╂暟鎹け璐: {ExceptionFormatter.FormatEx(ex)}", "ChargeStation", true); + return false; + } + } + + /// + /// 鑾峰彇鎵鏈夊厖鐢垫々 + /// + public List GetAllStations() + { + lock (lockObj) + { + return new List(chargeStations); + } + } + + /// + /// 鏍规嵁缂栧彿鑾峰彇鍏呯數妗 + /// + public ChargeStation GetStationById(string stationId) + { + lock (lockObj) + { + return chargeStations.FirstOrDefault(s => s.StationId == stationId); + } + } + + /// + /// 鏍规嵁IP鍦板潃鑾峰彇鍏呯數妗 + /// + public ChargeStation GetStationByIp(string ipAddress, int port) + { + lock (lockObj) + { + return chargeStations.FirstOrDefault(s => s.IpAddress == ipAddress && s.Port == port); + } + } + public ChargeStation GetStationByIp(string ipAddress) + { + lock (lockObj) + { + return chargeStations.FirstOrDefault(s => s.IpAddress == ipAddress); + } + } + + /// + /// 娣诲姞鍏呯數妗 + /// + public bool AddStation(ChargeStation station, out string errorMessage) + { + if (station == null) + { + errorMessage = "鍏呯數妗╂暟鎹笉鑳戒负绌"; + return false; + } + + // 楠岃瘉鏁版嵁 + if (!station.IsValid(out errorMessage)) + { + return false; + } + + lock (lockObj) + { + // 妫鏌ョ紪鍙锋槸鍚﹀凡瀛樺湪 + if (chargeStations.Any(s => s.StationId == station.StationId)) + { + errorMessage = $"鍏呯數妗╃紪鍙 {station.StationId} 宸插瓨鍦"; + return false; + } + + // 妫鏌P鍜岀鍙f槸鍚﹀凡琚娇鐢 + if (chargeStations.Any(s => s.IpAddress == station.IpAddress && s.Port == station.Port)) + { + errorMessage = $"IP鍦板潃 {station.IpAddress}:{station.Port} 宸茶浣跨敤"; + return false; + } + if (chargeStations.Any(s => s.SiteId == station.SiteId)) + { + errorMessage = $"SiteID {station.SiteId} 宸茶浣跨敤"; + return false; + } + + station.CreatedTime = DateTime.Now; + station.ModifiedTime = DateTime.Now; + + chargeStations.Add(station); + + if (SaveData()) + { + Diagnosis.Log($"娣诲姞鍏呯數妗╂垚鍔: {station}", "ChargeStation", true); + errorMessage = string.Empty; + return true; + } + else + { + chargeStations.Remove(station); + errorMessage = "淇濆瓨鏁版嵁澶辫触"; + return false; + } + } + } + + /// + /// 鏇存柊鍏呯數妗 + /// + public bool UpdateStation(ChargeStation station, out string errorMessage, bool isSave = false) + { + if (station == null) + { + errorMessage = "鍏呯數妗╂暟鎹笉鑳戒负绌"; + return false; + } + + // 楠岃瘉鏁版嵁 + if (!station.IsValid(out errorMessage)) + { + return false; + } + + lock (lockObj) + { + var existingStation = chargeStations.FirstOrDefault(s => s.StationId == station.StationId); + if (existingStation == null) + { + errorMessage = $"鍏呯數妗╃紪鍙 {station.StationId} 涓嶅瓨鍦"; + return false; + } + + // 妫鏌P鍜岀鍙f槸鍚︿笌鍏朵粬鍏呯數妗╁啿绐 + if (chargeStations.Any(s => s.StationId != station.StationId && + s.IpAddress == station.IpAddress && + s.Port == station.Port)) + { + errorMessage = $"IP鍦板潃 {station.IpAddress}:{station.Port} 宸茶鍏朵粬鍏呯數妗╀娇鐢"; + return false; + } + if (chargeStations.Any(s => s.StationId != station.StationId && s.SiteId == station.SiteId)) + { + errorMessage = $"SiteID {station.SiteId} 宸茶浣跨敤"; + return false; + } + + // 淇濈暀鍒涘缓鏃堕棿 + station.CreatedTime = existingStation.CreatedTime; + station.ModifiedTime = DateTime.Now; + + var index = chargeStations.IndexOf(existingStation); + + //杩涜璧嬪 + if (isSave) + { + + existingStation.StationId = station.StationId; + existingStation.Name = station.Name; + existingStation.Type = station.Type; + existingStation.ChargeMethod = station.ChargeMethod; + existingStation.IpAddress = station.IpAddress; + existingStation.Port = station.Port; + existingStation.SetVoltage = station.SetVoltage; + existingStation.SetElectricCurrent = station.SetElectricCurrent; + existingStation.Enabled = station.Enabled; + existingStation.ShieldSiteMechanismStatus = station.ShieldSiteMechanismStatus; + existingStation.GroupCarType = station.GroupCarType; + existingStation.SiteId = station.SiteId; + existingStation.Remarks = station.Remarks; + station = existingStation; + station.ModifiedTime = DateTime.Now; + + + + } + + + + chargeStations[index] = station; + + if (SaveData()) + { + //Diagnosis.Log($"鏇存柊鍏呯數妗╂垚鍔: {station}", "ChargeStation", true); + errorMessage = string.Empty; + return true; + } + else + { + chargeStations[index] = existingStation; + errorMessage = "淇濆瓨鏁版嵁澶辫触"; + return false; + } + } + } + + /// + /// 鍒犻櫎鍏呯數妗 + /// + public bool DeleteStation(string stationId, out string errorMessage) + { + lock (lockObj) + { + var station = chargeStations.FirstOrDefault(s => s.StationId == stationId); + if (station == null) + { + errorMessage = $"鍏呯數妗╃紪鍙 {stationId} 涓嶅瓨鍦"; + return false; + } + + // 妫鏌ユ槸鍚︽鍦ㄥ厖鐢 + //if (station.Status == ChargeStationStatus.Charging) + //{ + // errorMessage = $"鍏呯數妗 {station.Name} 姝e湪鍏呯數涓紝鏃犳硶鍒犻櫎"; + // return false; + //} + + chargeStations.Remove(station); + + if (SaveData()) + { + Diagnosis.Log($"鍒犻櫎鍏呯數妗╂垚鍔: {station}", "ChargeStation", true); + errorMessage = string.Empty; + return true; + } + else + { + chargeStations.Add(station); + errorMessage = "淇濆瓨鏁版嵁澶辫触"; + return false; + } + } + } + + /// + /// 鏇存柊鍏呯數妗╃姸鎬 + /// + public bool UpdateStationStatus(string stationId, ChargeStationStatus status) + { + lock (lockObj) + { + var station = chargeStations.FirstOrDefault(s => s.StationId == stationId); + if (station == null) + { + return false; + } + + station.Status = status; + station.ModifiedTime = DateTime.Now; + + return SaveData(); + } + } + + /// + /// 鑾峰彇绌洪棽鐨勫厖鐢垫々 + /// + public List GetIdleStations() + { + lock (lockObj) + { + return chargeStations + .Where(s => s.Enabled && s.Status == ChargeStationStatus.Idle) + .ToList(); + } + } + + /// + /// 鑾峰彇鍏呯數涓殑鍏呯數妗╂暟閲 + /// + public int GetChargingCount() + { + lock (lockObj) + { + return chargeStations.Count(s => s.Status == ChargeStationStatus.Charging); + } + } + + /// + /// 閲嶆柊鍔犺浇鏁版嵁 + /// + public void Reload() + { + LoadData(); + } + } +} + + + diff --git a/StandardScene.Core/Charge/ChargeStationHelper.cs b/StandardScene.Core/Charge/ChargeStationHelper.cs new file mode 100644 index 0000000..1d91eb4 --- /dev/null +++ b/StandardScene.Core/Charge/ChargeStationHelper.cs @@ -0,0 +1,448 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Windows.Forms; +using SimpleCore; +using SimpleCore.Library; + +namespace StandardScene.Charge +{ + /// + /// 鍏呯數妗╃鐞嗚緟鍔╃被 + /// 鎻愪緵绠鍖栫殑闈欐佹柟娉曠敤浜庡揩閫熻闂厖鐢垫々鍔熻兘 + /// + public static class ChargeStationHelper + { + private static ChargeStationManagementForm _managementForm; + + /// + /// 鎵撳紑鍏呯數妗╃鐞嗙獥鍙o紙鍗曚緥妯″紡锛 + /// + public static void OpenManagementWindow() + { + if (_managementForm == null || _managementForm.IsDisposed) + { + _managementForm = new ChargeStationManagementForm(); + _managementForm.FormClosed += (s, e) => _managementForm = null; + _managementForm.Show(); + } + else + { + _managementForm.BringToFront(); + _managementForm.Activate(); + } + } + + /// + /// 鎵撳紑鍏呯數妗╃鐞嗙獥鍙o紙瀵硅瘽妗嗘ā寮忥級 + /// + public static DialogResult OpenManagementDialog() + { + using (var form = new ChargeStationManagementForm()) + { + return form.ShowDialog(); + } + } + + /// + /// 鑾峰彇鎸囧畾绔欑偣鐨勫厖鐢垫々 + /// + /// 绔欑偣ID + /// 鍏呯數妗╁璞★紝濡傛灉涓嶅瓨鍦ㄥ垯杩斿洖null + public static ChargeStation GetStationBySiteId(int siteId) + { + var dataService = ChargeStationDataService.Instance; + return dataService.GetAllStations() + .FirstOrDefault(s => s.SiteId == siteId); + } + + /// + /// 鑾峰彇鎸囧畾IP鐨勫厖鐢垫々 + /// + /// IP鍦板潃 + /// 鍏呯數妗╁璞★紝濡傛灉涓嶅瓨鍦ㄥ垯杩斿洖null + public static ChargeStation GetStationByIp(string ipAddress) + { + var dataService = ChargeStationDataService.Instance; + return dataService.GetAllStations() + .FirstOrDefault(s => s.IpAddress == ipAddress); + } + + /// + /// 鑾峰彇鎵鏈夊厖鐢垫々閰嶇疆 + /// + /// 鎵鏈夊厖鐢垫々閰嶇疆鍒楄〃 + public static List GetAllStationConfigs() + { + var dataService = ChargeStationDataService.Instance; + return dataService.GetAllStations(); + } + + /// + /// 鑾峰彇褰撳墠鍏呯數绛栫暐閰嶇疆 + /// + /// 鍏呯數绛栫暐閰嶇疆瀵硅薄 + public static ChargeStrategyConfig GetChargeStrategyConfig() + { + var configService = ChargeStrategyConfigService.Instance; + return configService.LoadConfig(); + } + + /// + /// 淇濆瓨鍏呯數绛栫暐閰嶇疆 + /// + /// 鍏呯數绛栫暐閰嶇疆瀵硅薄 + public static void SaveChargeStrategyConfig(ChargeStrategyConfig config) + { + var configService = ChargeStrategyConfigService.Instance; + configService.SaveConfig(config); + } + + /// + /// 妫鏌ユ寚瀹氱珯鐐规槸鍚︽湁鍙敤鐨勫厖鐢垫々 + /// + /// 绔欑偣ID + /// true琛ㄧず鏈夊彲鐢ㄥ厖鐢垫々锛宖alse琛ㄧず娌℃湁 + public static bool IsSiteHasAvailableChargeStation(int siteId) + { + var station = GetStationBySiteId(siteId); + return station != null && + station.Enabled && + station.Status == ChargeStationStatus.Idle; + } + + /// + /// 鏍囪鍏呯數妗╁紑濮嬪厖鐢 + /// + /// 鍏呯數妗╃紪鍙 + /// 杞﹁締ID + /// 鎴愬姛杩斿洖true锛屽け璐ヨ繑鍥瀎alse + 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; + } + } + + /// + /// 鏍囪鍏呯數妗╁仠姝㈠厖鐢 + /// + /// 鍏呯數妗╃紪鍙 + /// 杞﹁締ID + /// 鎴愬姛杩斿洖true锛屽け璐ヨ繑鍥瀎alse + 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; + } + } + + /// + /// 鏍囪鍏呯數妗╀负鏁呴殰鐘舵 + /// + /// 鍏呯數妗╃紪鍙 + /// 鏁呴殰鍘熷洜 + /// 鎴愬姛杩斿洖true锛屽け璐ヨ繑鍥瀎alse + 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; + } + } + + /// + /// 鑾峰彇鍏呯數妗╃姸鎬佹憳瑕佷俊鎭 + /// + /// 鏍煎紡鍖栫殑鐘舵佸瓧绗︿覆 + 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}"; + } + + /// + /// 鑾峰彇鏈杩戠殑绌洪棽鍏呯數妗╋紙鍩轰簬绔欑偣ID锛 + /// + /// 褰撳墠绔欑偣ID + /// 鏈杩戠殑鍏呯數妗╋紝濡傛灉娌℃湁鍒欒繑鍥瀗ull + 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]; + } + + /// + /// 蹇熷垱寤烘祴璇曞厖鐢垫々锛堢敤浜庢祴璇曪級 + /// + /// 鍚嶇О + /// IP鍦板潃 + /// 绔欑偣ID + /// 鍒涘缓鎴愬姛杩斿洖true + 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; + } + + /// + /// 鏄剧ず鍏呯數妗╅夋嫨瀵硅瘽妗 + /// + /// 鎸夌姸鎬佽繃婊わ紙null琛ㄧず鏄剧ず鍏ㄩ儴锛 + /// 閫変腑鐨勫厖鐢垫々锛屽彇娑堝垯杩斿洖null + public static ChargeStation ShowStationSelectionDialog(ChargeStationStatus? filterByStatus = null) + { + var dataService = ChargeStationDataService.Instance; + var stations = dataService.GetAllStations(); + + if (filterByStatus.HasValue) + { + stations = stations.Where(s => s.Status == filterByStatus.Value).ToList(); + } + + if (stations.Count == 0) + { + MessageBox.Show("娌℃湁绗﹀悎鏉′欢鐨勫厖鐢垫々", "鎻愮ず", + MessageBoxButtons.OK, MessageBoxIcon.Information); + return null; + } + + // 鍒涘缓绠鍗曠殑閫夋嫨瀵硅瘽妗 + using (var dialog = new Form()) + { + dialog.Text = "閫夋嫨鍏呯數妗"; + dialog.Size = new System.Drawing.Size(500, 400); + dialog.StartPosition = FormStartPosition.CenterParent; + + var listBox = new ListBox + { + Dock = DockStyle.Fill, + Font = new System.Drawing.Font("寰蒋闆呴粦", 10F) + }; + + foreach (var station in stations) + { + listBox.Items.Add($"[{station.StationId}] {station.Name} - {station.IpAddress}:{station.Port} - {GetStatusText(station.Status)}"); + } + + var btnOK = new Button + { + Text = "纭畾", + DialogResult = DialogResult.OK, + Dock = DockStyle.Bottom, + Height = 40 + }; + + dialog.Controls.Add(listBox); + dialog.Controls.Add(btnOK); + dialog.AcceptButton = btnOK; + + if (dialog.ShowDialog() == DialogResult.OK && listBox.SelectedIndex >= 0) + { + return stations[listBox.SelectedIndex]; + } + + return null; + } + } + + /// + /// 鑾峰彇鐘舵佹枃鏈 + /// + 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 "鏈煡"; + } + } + + /// + /// 鎵归噺鏇存柊鍏呯數妗╁湪绾跨姸鎬侊紙鐢ㄤ簬瀹氭湡鐩戞帶锛 + /// + /// 瓒呮椂鏃堕棿锛堟绉掞級 + /// 鏇存柊鐨勫厖鐢垫々鏁伴噺 + 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; + } + + /// + /// Ping 鍏呯數妗╋紙妫鏌ヨ繛閫氭э級 + /// + 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; + } + } + } +} + + + diff --git a/StandardScene.Core/Charge/ChargeStationManagementForm.Designer.cs b/StandardScene.Core/Charge/ChargeStationManagementForm.Designer.cs new file mode 100644 index 0000000..0e77df8 --- /dev/null +++ b/StandardScene.Core/Charge/ChargeStationManagementForm.Designer.cs @@ -0,0 +1,1335 @@ +namespace StandardScene.Charge +{ + partial class ChargeStationManagementForm + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle1 = new System.Windows.Forms.DataGridViewCellStyle(); + System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle19 = new System.Windows.Forms.DataGridViewCellStyle(); + System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle2 = new System.Windows.Forms.DataGridViewCellStyle(); + System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle3 = new System.Windows.Forms.DataGridViewCellStyle(); + System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle4 = new System.Windows.Forms.DataGridViewCellStyle(); + System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle5 = new System.Windows.Forms.DataGridViewCellStyle(); + System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle6 = new System.Windows.Forms.DataGridViewCellStyle(); + System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle7 = new System.Windows.Forms.DataGridViewCellStyle(); + System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle8 = new System.Windows.Forms.DataGridViewCellStyle(); + System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle9 = new System.Windows.Forms.DataGridViewCellStyle(); + System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle10 = new System.Windows.Forms.DataGridViewCellStyle(); + System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle11 = new System.Windows.Forms.DataGridViewCellStyle(); + System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle12 = new System.Windows.Forms.DataGridViewCellStyle(); + System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle13 = new System.Windows.Forms.DataGridViewCellStyle(); + System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle14 = new System.Windows.Forms.DataGridViewCellStyle(); + System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle15 = new System.Windows.Forms.DataGridViewCellStyle(); + System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle16 = new System.Windows.Forms.DataGridViewCellStyle(); + System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle17 = new System.Windows.Forms.DataGridViewCellStyle(); + System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle18 = new System.Windows.Forms.DataGridViewCellStyle(); + this.splitContainer = new System.Windows.Forms.SplitContainer(); + this.pnlList = new System.Windows.Forms.Panel(); + this.dgvStations = new System.Windows.Forms.DataGridView(); + this.pnlListButtons = new System.Windows.Forms.Panel(); + this.lblStatistics = new System.Windows.Forms.Label(); + this.btnStrategyConfig = new System.Windows.Forms.Button(); + this.btnCommMonitor = new System.Windows.Forms.Button(); + this.btnAlarmConfig = new System.Windows.Forms.Button(); + this.btnExport = new System.Windows.Forms.Button(); + this.btnRefresh = new System.Windows.Forms.Button(); + this.pnlSearch = new System.Windows.Forms.Panel(); + this.cmbStatusFilter = new System.Windows.Forms.ComboBox(); + this.lblStatusFilter = new System.Windows.Forms.Label(); + this.txtSearch = new System.Windows.Forms.TextBox(); + this.lblSearch = new System.Windows.Forms.Label(); + this.pnlEdit = new System.Windows.Forms.Panel(); + this.grpRealTimeInfo = new System.Windows.Forms.GroupBox(); + this.lblAlarmValue = new System.Windows.Forms.Label(); + this.lblAlarm = new System.Windows.Forms.Label(); + this.lblMechanismStatusValue = new System.Windows.Forms.Label(); + this.lblMechanismStatus = new System.Windows.Forms.Label(); + this.lblChargeCommandStatusValue = new System.Windows.Forms.Label(); + this.lblChargeCommandStatus = new System.Windows.Forms.Label(); + this.lblCommStatusValue = new System.Windows.Forms.Label(); + this.lblCommStatus = new System.Windows.Forms.Label(); + this.lblRealTimeCurrentValue = new System.Windows.Forms.Label(); + this.lblRealTimeCurrent = new System.Windows.Forms.Label(); + this.lblRealTimeVoltageValue = new System.Windows.Forms.Label(); + this.lblRealTimeVoltage = new System.Windows.Forms.Label(); + this.lblBatteryLevelValue = new System.Windows.Forms.Label(); + this.lblBatteryLevel = new System.Windows.Forms.Label(); + this.lblCurrentVehicleValue = new System.Windows.Forms.Label(); + this.lblCurrentVehicle = new System.Windows.Forms.Label(); + this.grpEditInfo = new System.Windows.Forms.GroupBox(); + this.chargeCarType = new System.Windows.Forms.ComboBox(); + this.label1 = new System.Windows.Forms.Label(); + this.txtRemarks = new System.Windows.Forms.TextBox(); + this.lblRemarks = new System.Windows.Forms.Label(); + this.numSiteId = new System.Windows.Forms.NumericUpDown(); + this.lblSiteId = new System.Windows.Forms.Label(); + this.chkEnabled = new System.Windows.Forms.CheckBox(); + this.chkShieldSiteMechanismStatus = new System.Windows.Forms.CheckBox(); + this.numCurrent = new System.Windows.Forms.NumericUpDown(); + this.lblCurrent = new System.Windows.Forms.Label(); + this.numVoltage = new System.Windows.Forms.NumericUpDown(); + this.lblVoltage = new System.Windows.Forms.Label(); + this.numPort = new System.Windows.Forms.NumericUpDown(); + this.lblPort = new System.Windows.Forms.Label(); + this.txtIpAddress = new System.Windows.Forms.TextBox(); + this.lblIpAddress = new System.Windows.Forms.Label(); + this.cmbChargeMethod = new System.Windows.Forms.ComboBox(); + this.lblChargeMethod = new System.Windows.Forms.Label(); + this.cmbType = new System.Windows.Forms.ComboBox(); + this.lblType = new System.Windows.Forms.Label(); + this.txtName = new System.Windows.Forms.TextBox(); + this.lblName = new System.Windows.Forms.Label(); + this.txtStationId = new System.Windows.Forms.TextBox(); + this.lblStationId = new System.Windows.Forms.Label(); + this.pnlEditButtons = new System.Windows.Forms.Panel(); + this.btnCancel = new System.Windows.Forms.Button(); + this.btnDelete = new System.Windows.Forms.Button(); + this.btnSave = new System.Windows.Forms.Button(); + this.colStationId = new System.Windows.Forms.DataGridViewTextBoxColumn(); + this.colName = new System.Windows.Forms.DataGridViewTextBoxColumn(); + this.colType = new System.Windows.Forms.DataGridViewTextBoxColumn(); + this.colChargeMethod = new System.Windows.Forms.DataGridViewTextBoxColumn(); + this.colSiteId = new System.Windows.Forms.DataGridViewTextBoxColumn(); + this.colLastSendTime = new System.Windows.Forms.DataGridViewTextBoxColumn(); + this.colLastReceiveTime = new System.Windows.Forms.DataGridViewTextBoxColumn(); + this.colCommStatus = new System.Windows.Forms.DataGridViewTextBoxColumn(); + this.colChargeCommandStatus = new System.Windows.Forms.DataGridViewTextBoxColumn(); + this.colMechanismStatus = new System.Windows.Forms.DataGridViewTextBoxColumn(); + this.colCurrentVehicle = new System.Windows.Forms.DataGridViewTextBoxColumn(); + this.colBatteryLevel = new System.Windows.Forms.DataGridViewTextBoxColumn(); + this.colAlarm = new System.Windows.Forms.DataGridViewTextBoxColumn(); + this.colRealTimeVoltage = new System.Windows.Forms.DataGridViewTextBoxColumn(); + this.colRealTimeCurrent = new System.Windows.Forms.DataGridViewTextBoxColumn(); + this.colStatus = new System.Windows.Forms.DataGridViewTextBoxColumn(); + this.colEnabled = new System.Windows.Forms.DataGridViewTextBoxColumn(); + ((System.ComponentModel.ISupportInitialize)(this.splitContainer)).BeginInit(); + this.splitContainer.Panel1.SuspendLayout(); + this.splitContainer.Panel2.SuspendLayout(); + this.splitContainer.SuspendLayout(); + this.pnlList.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.dgvStations)).BeginInit(); + this.pnlListButtons.SuspendLayout(); + this.pnlSearch.SuspendLayout(); + this.pnlEdit.SuspendLayout(); + this.grpRealTimeInfo.SuspendLayout(); + this.grpEditInfo.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.numSiteId)).BeginInit(); + ((System.ComponentModel.ISupportInitialize)(this.numCurrent)).BeginInit(); + ((System.ComponentModel.ISupportInitialize)(this.numVoltage)).BeginInit(); + ((System.ComponentModel.ISupportInitialize)(this.numPort)).BeginInit(); + this.pnlEditButtons.SuspendLayout(); + this.SuspendLayout(); + // + // splitContainer + // + this.splitContainer.Dock = System.Windows.Forms.DockStyle.Fill; + this.splitContainer.Location = new System.Drawing.Point(0, 0); + this.splitContainer.Margin = new System.Windows.Forms.Padding(4); + this.splitContainer.Name = "splitContainer"; + // + // splitContainer.Panel1 + // + this.splitContainer.Panel1.Controls.Add(this.pnlList); + // + // splitContainer.Panel2 + // + this.splitContainer.Panel2.Controls.Add(this.pnlEdit); + this.splitContainer.Size = new System.Drawing.Size(1731, 875); + this.splitContainer.SplitterDistance = 1081; + this.splitContainer.SplitterWidth = 5; + this.splitContainer.TabIndex = 0; + // + // pnlList + // + this.pnlList.Controls.Add(this.dgvStations); + this.pnlList.Controls.Add(this.pnlListButtons); + this.pnlList.Controls.Add(this.pnlSearch); + this.pnlList.Dock = System.Windows.Forms.DockStyle.Fill; + this.pnlList.Location = new System.Drawing.Point(0, 0); + this.pnlList.Margin = new System.Windows.Forms.Padding(4); + this.pnlList.Name = "pnlList"; + this.pnlList.Size = new System.Drawing.Size(1081, 875); + this.pnlList.TabIndex = 0; + // + // dgvStations + // + this.dgvStations.AllowUserToAddRows = false; + this.dgvStations.AllowUserToDeleteRows = false; + this.dgvStations.AllowUserToResizeRows = false; + this.dgvStations.AutoSizeColumnsMode = System.Windows.Forms.DataGridViewAutoSizeColumnsMode.Fill; + this.dgvStations.BackgroundColor = System.Drawing.Color.White; + this.dgvStations.BorderStyle = System.Windows.Forms.BorderStyle.None; + this.dgvStations.CellBorderStyle = System.Windows.Forms.DataGridViewCellBorderStyle.SingleHorizontal; + dataGridViewCellStyle1.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleLeft; + dataGridViewCellStyle1.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(63)))), ((int)(((byte)(81)))), ((int)(((byte)(181))))); + dataGridViewCellStyle1.Font = new System.Drawing.Font("寰蒋闆呴粦", 9F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + dataGridViewCellStyle1.ForeColor = System.Drawing.Color.White; + dataGridViewCellStyle1.SelectionBackColor = System.Drawing.Color.FromArgb(((int)(((byte)(63)))), ((int)(((byte)(81)))), ((int)(((byte)(181))))); + dataGridViewCellStyle1.SelectionForeColor = System.Drawing.SystemColors.HighlightText; + dataGridViewCellStyle1.WrapMode = System.Windows.Forms.DataGridViewTriState.True; + this.dgvStations.ColumnHeadersDefaultCellStyle = dataGridViewCellStyle1; + this.dgvStations.ColumnHeadersHeight = 100; + this.dgvStations.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.DisableResizing; + this.dgvStations.Columns.AddRange(new System.Windows.Forms.DataGridViewColumn[] { + this.colStationId, + this.colName, + this.colType, + this.colChargeMethod, + this.colSiteId, + this.colLastSendTime, + this.colLastReceiveTime, + this.colCommStatus, + this.colChargeCommandStatus, + this.colMechanismStatus, + this.colCurrentVehicle, + this.colBatteryLevel, + this.colAlarm, + this.colRealTimeVoltage, + this.colRealTimeCurrent, + this.colStatus, + this.colEnabled}); + dataGridViewCellStyle19.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleLeft; + dataGridViewCellStyle19.BackColor = System.Drawing.Color.White; + dataGridViewCellStyle19.Font = new System.Drawing.Font("寰蒋闆呴粦", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + dataGridViewCellStyle19.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(64)))), ((int)(((byte)(64)))), ((int)(((byte)(64))))); + dataGridViewCellStyle19.SelectionBackColor = System.Drawing.Color.FromArgb(((int)(((byte)(197)))), ((int)(((byte)(202)))), ((int)(((byte)(233))))); + dataGridViewCellStyle19.SelectionForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(33)))), ((int)(((byte)(33)))), ((int)(((byte)(33))))); + dataGridViewCellStyle19.WrapMode = System.Windows.Forms.DataGridViewTriState.False; + this.dgvStations.DefaultCellStyle = dataGridViewCellStyle19; + this.dgvStations.Dock = System.Windows.Forms.DockStyle.Fill; + this.dgvStations.EnableHeadersVisualStyles = false; + this.dgvStations.GridColor = System.Drawing.Color.FromArgb(((int)(((byte)(224)))), ((int)(((byte)(224)))), ((int)(((byte)(224))))); + this.dgvStations.Location = new System.Drawing.Point(0, 62); + this.dgvStations.Margin = new System.Windows.Forms.Padding(4); + this.dgvStations.MultiSelect = false; + this.dgvStations.Name = "dgvStations"; + this.dgvStations.ReadOnly = true; + this.dgvStations.RowHeadersVisible = false; + this.dgvStations.RowHeadersWidth = 30; + this.dgvStations.RowHeadersWidthSizeMode = System.Windows.Forms.DataGridViewRowHeadersWidthSizeMode.DisableResizing; + this.dgvStations.RowTemplate.Height = 35; + this.dgvStations.SelectionMode = System.Windows.Forms.DataGridViewSelectionMode.FullRowSelect; + this.dgvStations.Size = new System.Drawing.Size(1081, 725); + this.dgvStations.TabIndex = 2; + this.dgvStations.CellDoubleClick += new System.Windows.Forms.DataGridViewCellEventHandler(this.dgvStations_CellDoubleClick); + // + // pnlListButtons + // + this.pnlListButtons.Controls.Add(this.lblStatistics); + this.pnlListButtons.Controls.Add(this.btnStrategyConfig); + this.pnlListButtons.Controls.Add(this.btnCommMonitor); + this.pnlListButtons.Controls.Add(this.btnAlarmConfig); + this.pnlListButtons.Controls.Add(this.btnExport); + this.pnlListButtons.Controls.Add(this.btnRefresh); + this.pnlListButtons.Dock = System.Windows.Forms.DockStyle.Bottom; + this.pnlListButtons.Location = new System.Drawing.Point(0, 749); + this.pnlListButtons.Margin = new System.Windows.Forms.Padding(4); + this.pnlListButtons.Name = "pnlListButtons"; + this.pnlListButtons.Padding = new System.Windows.Forms.Padding(13, 12, 13, 12); + this.pnlListButtons.Size = new System.Drawing.Size(1081, 126); + this.pnlListButtons.TabIndex = 1; + // + // lblStatistics + // + this.lblStatistics.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.lblStatistics.AutoSize = false; + this.lblStatistics.Font = new System.Drawing.Font("寰蒋闆呴粦", 10F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + this.lblStatistics.Location = new System.Drawing.Point(20, 12); + this.lblStatistics.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); + this.lblStatistics.Name = "lblStatistics"; + this.lblStatistics.Size = new System.Drawing.Size(1034, 24); + this.lblStatistics.TabIndex = 2; + this.lblStatistics.Text = "鎬绘暟: 0 | 绌洪棽: 0 | 鍏呯數涓: 0 | 鏁呴殰: 0"; + // + // btnStrategyConfig + // + this.btnStrategyConfig.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); + this.btnStrategyConfig.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(76)))), ((int)(((byte)(175)))), ((int)(((byte)(80))))); + this.btnStrategyConfig.Font = new System.Drawing.Font("寰蒋闆呴粦", 10F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + this.btnStrategyConfig.ForeColor = System.Drawing.Color.White; + this.btnStrategyConfig.Location = new System.Drawing.Point(411, 52); + this.btnStrategyConfig.Margin = new System.Windows.Forms.Padding(4); + this.btnStrategyConfig.Name = "btnStrategyConfig"; + this.btnStrategyConfig.Size = new System.Drawing.Size(120, 50); + this.btnStrategyConfig.TabIndex = 5; + this.btnStrategyConfig.Text = "绛栫暐閰嶇疆"; + this.btnStrategyConfig.UseVisualStyleBackColor = false; + this.btnStrategyConfig.Click += new System.EventHandler(this.btnStrategyConfig_Click); + // + // btnCommMonitor + // + this.btnCommMonitor.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); + this.btnCommMonitor.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(33)))), ((int)(((byte)(150)))), ((int)(((byte)(243))))); + this.btnCommMonitor.Font = new System.Drawing.Font("寰蒋闆呴粦", 10F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + this.btnCommMonitor.ForeColor = System.Drawing.Color.White; + this.btnCommMonitor.Location = new System.Drawing.Point(541, 52); + this.btnCommMonitor.Margin = new System.Windows.Forms.Padding(4); + this.btnCommMonitor.Name = "btnCommMonitor"; + this.btnCommMonitor.Size = new System.Drawing.Size(120, 50); + this.btnCommMonitor.TabIndex = 4; + this.btnCommMonitor.Text = "閫氳鐩戞帶"; + this.btnCommMonitor.UseVisualStyleBackColor = false; + this.btnCommMonitor.Click += new System.EventHandler(this.btnCommMonitor_Click); + // + // btnAlarmConfig + // + this.btnAlarmConfig.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); + this.btnAlarmConfig.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(255)))), ((int)(((byte)(193)))), ((int)(((byte)(7))))); + this.btnAlarmConfig.Font = new System.Drawing.Font("寰蒋闆呴粦", 10F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + this.btnAlarmConfig.ForeColor = System.Drawing.Color.White; + this.btnAlarmConfig.Location = new System.Drawing.Point(671, 52); + this.btnAlarmConfig.Margin = new System.Windows.Forms.Padding(4); + this.btnAlarmConfig.Name = "btnAlarmConfig"; + this.btnAlarmConfig.Size = new System.Drawing.Size(120, 50); + this.btnAlarmConfig.TabIndex = 3; + this.btnAlarmConfig.Text = "鎶ヨ閰嶇疆"; + this.btnAlarmConfig.UseVisualStyleBackColor = false; + this.btnAlarmConfig.Click += new System.EventHandler(this.btnAlarmConfig_Click); + // + // btnExport + // + this.btnExport.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); + this.btnExport.Font = new System.Drawing.Font("寰蒋闆呴粦", 10F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + this.btnExport.Location = new System.Drawing.Point(934, 52); + this.btnExport.Margin = new System.Windows.Forms.Padding(4); + this.btnExport.Name = "btnExport"; + this.btnExport.Size = new System.Drawing.Size(120, 50); + this.btnExport.TabIndex = 1; + this.btnExport.Text = "瀵煎嚭"; + this.btnExport.UseVisualStyleBackColor = true; + this.btnExport.Click += new System.EventHandler(this.btnExport_Click); + // + // btnRefresh + // + this.btnRefresh.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); + this.btnRefresh.Font = new System.Drawing.Font("寰蒋闆呴粦", 10F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + this.btnRefresh.Location = new System.Drawing.Point(801, 52); + this.btnRefresh.Margin = new System.Windows.Forms.Padding(4); + this.btnRefresh.Name = "btnRefresh"; + this.btnRefresh.Size = new System.Drawing.Size(120, 50); + this.btnRefresh.TabIndex = 0; + this.btnRefresh.Text = "鍒锋柊"; + this.btnRefresh.UseVisualStyleBackColor = true; + this.btnRefresh.Click += new System.EventHandler(this.btnRefresh_Click); + // + // pnlSearch + // + this.pnlSearch.Controls.Add(this.cmbStatusFilter); + this.pnlSearch.Controls.Add(this.lblStatusFilter); + this.pnlSearch.Controls.Add(this.txtSearch); + this.pnlSearch.Controls.Add(this.lblSearch); + this.pnlSearch.Dock = System.Windows.Forms.DockStyle.Top; + this.pnlSearch.Location = new System.Drawing.Point(0, 0); + this.pnlSearch.Margin = new System.Windows.Forms.Padding(4); + this.pnlSearch.Name = "pnlSearch"; + this.pnlSearch.Padding = new System.Windows.Forms.Padding(13, 12, 13, 12); + this.pnlSearch.Size = new System.Drawing.Size(1081, 62); + this.pnlSearch.TabIndex = 0; + // + // cmbStatusFilter + // + this.cmbStatusFilter.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.cmbStatusFilter.Font = new System.Drawing.Font("寰蒋闆呴粦", 10F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + this.cmbStatusFilter.FormattingEnabled = true; + this.cmbStatusFilter.Location = new System.Drawing.Point(680, 16); + this.cmbStatusFilter.Margin = new System.Windows.Forms.Padding(4); + this.cmbStatusFilter.Name = "cmbStatusFilter"; + this.cmbStatusFilter.Size = new System.Drawing.Size(199, 31); + this.cmbStatusFilter.TabIndex = 3; + this.cmbStatusFilter.SelectedIndexChanged += new System.EventHandler(this.cmbStatusFilter_SelectedIndexChanged); + // + // lblStatusFilter + // + this.lblStatusFilter.AutoSize = true; + this.lblStatusFilter.Font = new System.Drawing.Font("寰蒋闆呴粦", 10F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + this.lblStatusFilter.Location = new System.Drawing.Point(593, 21); + this.lblStatusFilter.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); + this.lblStatusFilter.Name = "lblStatusFilter"; + this.lblStatusFilter.Size = new System.Drawing.Size(61, 23); + this.lblStatusFilter.TabIndex = 2; + this.lblStatusFilter.Text = "鐘舵侊細"; + // + // txtSearch + // + this.txtSearch.Font = new System.Drawing.Font("寰蒋闆呴粦", 10F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + this.txtSearch.Location = new System.Drawing.Point(100, 16); + this.txtSearch.Margin = new System.Windows.Forms.Padding(4); + this.txtSearch.Name = "txtSearch"; + this.txtSearch.Size = new System.Drawing.Size(399, 29); + this.txtSearch.TabIndex = 1; + this.txtSearch.TextChanged += new System.EventHandler(this.txtSearch_TextChanged); + // + // lblSearch + // + this.lblSearch.AutoSize = true; + this.lblSearch.Font = new System.Drawing.Font("寰蒋闆呴粦", 10F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + this.lblSearch.Location = new System.Drawing.Point(13, 21); + this.lblSearch.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); + this.lblSearch.Name = "lblSearch"; + this.lblSearch.Size = new System.Drawing.Size(61, 23); + this.lblSearch.TabIndex = 0; + this.lblSearch.Text = "鎼滅储锛"; + // + // pnlEdit + // + this.pnlEdit.Controls.Add(this.grpRealTimeInfo); + this.pnlEdit.Controls.Add(this.grpEditInfo); + this.pnlEdit.Controls.Add(this.pnlEditButtons); + this.pnlEdit.Dock = System.Windows.Forms.DockStyle.Fill; + this.pnlEdit.Location = new System.Drawing.Point(0, 0); + this.pnlEdit.Margin = new System.Windows.Forms.Padding(4); + this.pnlEdit.Name = "pnlEdit"; + this.pnlEdit.Size = new System.Drawing.Size(645, 875); + this.pnlEdit.TabIndex = 0; + // + // grpRealTimeInfo + // + this.grpRealTimeInfo.Controls.Add(this.lblAlarmValue); + this.grpRealTimeInfo.Controls.Add(this.lblAlarm); + this.grpRealTimeInfo.Controls.Add(this.lblMechanismStatusValue); + this.grpRealTimeInfo.Controls.Add(this.lblMechanismStatus); + this.grpRealTimeInfo.Controls.Add(this.lblChargeCommandStatusValue); + this.grpRealTimeInfo.Controls.Add(this.lblChargeCommandStatus); + this.grpRealTimeInfo.Controls.Add(this.lblCommStatusValue); + this.grpRealTimeInfo.Controls.Add(this.lblCommStatus); + this.grpRealTimeInfo.Controls.Add(this.lblRealTimeCurrentValue); + this.grpRealTimeInfo.Controls.Add(this.lblRealTimeCurrent); + this.grpRealTimeInfo.Controls.Add(this.lblRealTimeVoltageValue); + this.grpRealTimeInfo.Controls.Add(this.lblRealTimeVoltage); + this.grpRealTimeInfo.Controls.Add(this.lblBatteryLevelValue); + this.grpRealTimeInfo.Controls.Add(this.lblBatteryLevel); + this.grpRealTimeInfo.Controls.Add(this.lblCurrentVehicleValue); + this.grpRealTimeInfo.Controls.Add(this.lblCurrentVehicle); + this.grpRealTimeInfo.Dock = System.Windows.Forms.DockStyle.Fill; + this.grpRealTimeInfo.Font = new System.Drawing.Font("寰蒋闆呴粦", 10F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + this.grpRealTimeInfo.Location = new System.Drawing.Point(0, 500); + this.grpRealTimeInfo.Margin = new System.Windows.Forms.Padding(4); + this.grpRealTimeInfo.Name = "grpRealTimeInfo"; + this.grpRealTimeInfo.Padding = new System.Windows.Forms.Padding(20, 10, 20, 10); + this.grpRealTimeInfo.Size = new System.Drawing.Size(645, 250); + this.grpRealTimeInfo.TabIndex = 2; + this.grpRealTimeInfo.TabStop = false; + this.grpRealTimeInfo.Text = "瀹炴椂鐘舵侊紙鍙锛"; + // + // lblAlarmValue + // + this.lblAlarmValue.Font = new System.Drawing.Font("寰蒋闆呴粦", 8F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + this.lblAlarmValue.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(76)))), ((int)(((byte)(175)))), ((int)(((byte)(80))))); + this.lblAlarmValue.Location = new System.Drawing.Point(451, 96); + this.lblAlarmValue.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); + this.lblAlarmValue.Name = "lblAlarmValue"; + this.lblAlarmValue.Size = new System.Drawing.Size(76, 20); + this.lblAlarmValue.TabIndex = 15; + this.lblAlarmValue.Text = "姝e父"; + this.lblAlarmValue.TextAlign = System.Drawing.ContentAlignment.MiddleLeft; + // + // lblAlarm + // + this.lblAlarm.Font = new System.Drawing.Font("寰蒋闆呴粦", 9F); + this.lblAlarm.Location = new System.Drawing.Point(343, 97); + this.lblAlarm.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); + this.lblAlarm.Name = "lblAlarm"; + this.lblAlarm.Size = new System.Drawing.Size(100, 18); + this.lblAlarm.TabIndex = 14; + this.lblAlarm.Text = "鎶ヨ鐘舵侊細"; + this.lblAlarm.TextAlign = System.Drawing.ContentAlignment.MiddleRight; + // + // lblMechanismStatusValue + // + this.lblMechanismStatusValue.Font = new System.Drawing.Font("寰蒋闆呴粦", 8F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + this.lblMechanismStatusValue.Location = new System.Drawing.Point(155, 98); + this.lblMechanismStatusValue.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); + this.lblMechanismStatusValue.Name = "lblMechanismStatusValue"; + this.lblMechanismStatusValue.Size = new System.Drawing.Size(81, 18); + this.lblMechanismStatusValue.TabIndex = 13; + this.lblMechanismStatusValue.Text = "? 鏈煡"; + this.lblMechanismStatusValue.TextAlign = System.Drawing.ContentAlignment.MiddleLeft; + // + // lblMechanismStatus + // + this.lblMechanismStatus.Font = new System.Drawing.Font("寰蒋闆呴粦", 9F); + this.lblMechanismStatus.Location = new System.Drawing.Point(50, 95); + this.lblMechanismStatus.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); + this.lblMechanismStatus.Name = "lblMechanismStatus"; + this.lblMechanismStatus.Size = new System.Drawing.Size(97, 30); + this.lblMechanismStatus.TabIndex = 12; + this.lblMechanismStatus.Text = "鏈烘瀯鐘舵侊細"; + this.lblMechanismStatus.TextAlign = System.Drawing.ContentAlignment.MiddleRight; + // + // lblChargeCommandStatusValue + // + this.lblChargeCommandStatusValue.Font = new System.Drawing.Font("寰蒋闆呴粦", 9F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + this.lblChargeCommandStatusValue.Location = new System.Drawing.Point(451, 75); + this.lblChargeCommandStatusValue.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); + this.lblChargeCommandStatusValue.Name = "lblChargeCommandStatusValue"; + this.lblChargeCommandStatusValue.Size = new System.Drawing.Size(150, 20); + this.lblChargeCommandStatusValue.TabIndex = 11; + this.lblChargeCommandStatusValue.Text = "鈼 鍋滄"; + this.lblChargeCommandStatusValue.TextAlign = System.Drawing.ContentAlignment.MiddleLeft; + // + // lblChargeCommandStatus + // + this.lblChargeCommandStatus.Font = new System.Drawing.Font("寰蒋闆呴粦", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + this.lblChargeCommandStatus.Location = new System.Drawing.Point(343, 75); + this.lblChargeCommandStatus.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); + this.lblChargeCommandStatus.Name = "lblChargeCommandStatus"; + this.lblChargeCommandStatus.Size = new System.Drawing.Size(100, 20); + this.lblChargeCommandStatus.TabIndex = 10; + this.lblChargeCommandStatus.Text = "鍏呯數鎸囦护锛"; + this.lblChargeCommandStatus.TextAlign = System.Drawing.ContentAlignment.MiddleRight; + // + // lblCommStatusValue + // + this.lblCommStatusValue.Font = new System.Drawing.Font("寰蒋闆呴粦", 9F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + this.lblCommStatusValue.Location = new System.Drawing.Point(155, 75); + this.lblCommStatusValue.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); + this.lblCommStatusValue.Name = "lblCommStatusValue"; + this.lblCommStatusValue.Size = new System.Drawing.Size(200, 20); + this.lblCommStatusValue.TabIndex = 9; + this.lblCommStatusValue.Text = "? 鏈煡"; + this.lblCommStatusValue.TextAlign = System.Drawing.ContentAlignment.MiddleLeft; + // + // lblCommStatus + // + this.lblCommStatus.Font = new System.Drawing.Font("寰蒋闆呴粦", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + this.lblCommStatus.Location = new System.Drawing.Point(27, 75); + this.lblCommStatus.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); + this.lblCommStatus.Name = "lblCommStatus"; + this.lblCommStatus.Size = new System.Drawing.Size(120, 20); + this.lblCommStatus.TabIndex = 8; + this.lblCommStatus.Text = "閫氳鐘舵侊細"; + this.lblCommStatus.TextAlign = System.Drawing.ContentAlignment.MiddleRight; + // + // lblRealTimeCurrentValue + // + this.lblRealTimeCurrentValue.Font = new System.Drawing.Font("寰蒋闆呴粦", 9F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + this.lblRealTimeCurrentValue.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(33)))), ((int)(((byte)(150)))), ((int)(((byte)(243))))); + this.lblRealTimeCurrentValue.Location = new System.Drawing.Point(451, 50); + this.lblRealTimeCurrentValue.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); + this.lblRealTimeCurrentValue.Name = "lblRealTimeCurrentValue"; + this.lblRealTimeCurrentValue.Size = new System.Drawing.Size(150, 25); + this.lblRealTimeCurrentValue.TabIndex = 7; + this.lblRealTimeCurrentValue.Text = "0.0 A"; + this.lblRealTimeCurrentValue.TextAlign = System.Drawing.ContentAlignment.MiddleLeft; + // + // lblRealTimeCurrent + // + this.lblRealTimeCurrent.Font = new System.Drawing.Font("寰蒋闆呴粦", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + this.lblRealTimeCurrent.Location = new System.Drawing.Point(327, 50); + this.lblRealTimeCurrent.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); + this.lblRealTimeCurrent.Name = "lblRealTimeCurrent"; + this.lblRealTimeCurrent.Size = new System.Drawing.Size(116, 25); + this.lblRealTimeCurrent.TabIndex = 6; + this.lblRealTimeCurrent.Text = "瀹炴椂鐢垫祦锛"; + this.lblRealTimeCurrent.TextAlign = System.Drawing.ContentAlignment.MiddleRight; + // + // lblRealTimeVoltageValue + // + this.lblRealTimeVoltageValue.Font = new System.Drawing.Font("寰蒋闆呴粦", 9F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + this.lblRealTimeVoltageValue.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(33)))), ((int)(((byte)(150)))), ((int)(((byte)(243))))); + this.lblRealTimeVoltageValue.Location = new System.Drawing.Point(155, 50); + this.lblRealTimeVoltageValue.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); + this.lblRealTimeVoltageValue.Name = "lblRealTimeVoltageValue"; + this.lblRealTimeVoltageValue.Size = new System.Drawing.Size(200, 25); + this.lblRealTimeVoltageValue.TabIndex = 5; + this.lblRealTimeVoltageValue.Text = "0.0 V"; + this.lblRealTimeVoltageValue.TextAlign = System.Drawing.ContentAlignment.MiddleLeft; + // + // lblRealTimeVoltage + // + this.lblRealTimeVoltage.Font = new System.Drawing.Font("寰蒋闆呴粦", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + this.lblRealTimeVoltage.Location = new System.Drawing.Point(27, 50); + this.lblRealTimeVoltage.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); + this.lblRealTimeVoltage.Name = "lblRealTimeVoltage"; + this.lblRealTimeVoltage.Size = new System.Drawing.Size(120, 25); + this.lblRealTimeVoltage.TabIndex = 4; + this.lblRealTimeVoltage.Text = "瀹炴椂鐢靛帇锛"; + this.lblRealTimeVoltage.TextAlign = System.Drawing.ContentAlignment.MiddleRight; + // + // lblBatteryLevelValue + // + this.lblBatteryLevelValue.Font = new System.Drawing.Font("寰蒋闆呴粦", 9F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + this.lblBatteryLevelValue.Location = new System.Drawing.Point(451, 25); + this.lblBatteryLevelValue.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); + this.lblBatteryLevelValue.Name = "lblBatteryLevelValue"; + this.lblBatteryLevelValue.Size = new System.Drawing.Size(150, 25); + this.lblBatteryLevelValue.TabIndex = 3; + this.lblBatteryLevelValue.Text = "-"; + this.lblBatteryLevelValue.TextAlign = System.Drawing.ContentAlignment.MiddleLeft; + // + // lblBatteryLevel + // + this.lblBatteryLevel.Font = new System.Drawing.Font("寰蒋闆呴粦", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + this.lblBatteryLevel.Location = new System.Drawing.Point(350, 25); + this.lblBatteryLevel.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); + this.lblBatteryLevel.Name = "lblBatteryLevel"; + this.lblBatteryLevel.Size = new System.Drawing.Size(93, 25); + this.lblBatteryLevel.TabIndex = 2; + this.lblBatteryLevel.Text = "瀹炴椂鐢甸噺锛"; + this.lblBatteryLevel.TextAlign = System.Drawing.ContentAlignment.MiddleRight; + // + // lblCurrentVehicleValue + // + this.lblCurrentVehicleValue.Font = new System.Drawing.Font("寰蒋闆呴粦", 9F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + this.lblCurrentVehicleValue.Location = new System.Drawing.Point(155, 25); + this.lblCurrentVehicleValue.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); + this.lblCurrentVehicleValue.Name = "lblCurrentVehicleValue"; + this.lblCurrentVehicleValue.Size = new System.Drawing.Size(200, 25); + this.lblCurrentVehicleValue.TabIndex = 1; + this.lblCurrentVehicleValue.Text = "-"; + this.lblCurrentVehicleValue.TextAlign = System.Drawing.ContentAlignment.MiddleLeft; + // + // lblCurrentVehicle + // + this.lblCurrentVehicle.Font = new System.Drawing.Font("寰蒋闆呴粦", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + this.lblCurrentVehicle.Location = new System.Drawing.Point(27, 25); + this.lblCurrentVehicle.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); + this.lblCurrentVehicle.Name = "lblCurrentVehicle"; + this.lblCurrentVehicle.Size = new System.Drawing.Size(120, 25); + this.lblCurrentVehicle.TabIndex = 0; + this.lblCurrentVehicle.Text = "褰撳墠杞﹁締锛"; + this.lblCurrentVehicle.TextAlign = System.Drawing.ContentAlignment.MiddleRight; + // + // grpEditInfo + // + this.grpEditInfo.Controls.Add(this.chargeCarType); + this.grpEditInfo.Controls.Add(this.label1); + this.grpEditInfo.Controls.Add(this.txtRemarks); + this.grpEditInfo.Controls.Add(this.lblRemarks); + this.grpEditInfo.Controls.Add(this.numSiteId); + this.grpEditInfo.Controls.Add(this.lblSiteId); + this.grpEditInfo.Controls.Add(this.chkEnabled); + this.grpEditInfo.Controls.Add(this.chkShieldSiteMechanismStatus); + this.grpEditInfo.Controls.Add(this.numCurrent); + this.grpEditInfo.Controls.Add(this.lblCurrent); + this.grpEditInfo.Controls.Add(this.numVoltage); + this.grpEditInfo.Controls.Add(this.lblVoltage); + this.grpEditInfo.Controls.Add(this.numPort); + this.grpEditInfo.Controls.Add(this.lblPort); + this.grpEditInfo.Controls.Add(this.txtIpAddress); + this.grpEditInfo.Controls.Add(this.lblIpAddress); + this.grpEditInfo.Controls.Add(this.cmbChargeMethod); + this.grpEditInfo.Controls.Add(this.lblChargeMethod); + this.grpEditInfo.Controls.Add(this.cmbType); + this.grpEditInfo.Controls.Add(this.lblType); + this.grpEditInfo.Controls.Add(this.txtName); + this.grpEditInfo.Controls.Add(this.lblName); + this.grpEditInfo.Controls.Add(this.txtStationId); + this.grpEditInfo.Controls.Add(this.lblStationId); + this.grpEditInfo.Dock = System.Windows.Forms.DockStyle.Top; + this.grpEditInfo.Font = new System.Drawing.Font("寰蒋闆呴粦", 10F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + this.grpEditInfo.Location = new System.Drawing.Point(0, 0); + this.grpEditInfo.Margin = new System.Windows.Forms.Padding(4); + this.grpEditInfo.Name = "grpEditInfo"; + this.grpEditInfo.Padding = new System.Windows.Forms.Padding(20, 19, 20, 19); + this.grpEditInfo.Size = new System.Drawing.Size(645, 500); + this.grpEditInfo.TabIndex = 1; + this.grpEditInfo.TabStop = false; + this.grpEditInfo.Text = "鍩烘湰淇℃伅锛堝彲缂栬緫锛"; + // + // chargeCarType + // + this.chargeCarType.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.chargeCarType.Font = new System.Drawing.Font("寰蒋闆呴粦", 10F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + this.chargeCarType.FormattingEnabled = true; + this.chargeCarType.Items.AddRange(new object[] { + "FRLD", + "MuXing"}); + this.chargeCarType.Location = new System.Drawing.Point(426, 332); + this.chargeCarType.Margin = new System.Windows.Forms.Padding(4); + this.chargeCarType.Name = "chargeCarType"; + this.chargeCarType.Size = new System.Drawing.Size(140, 31); + this.chargeCarType.TabIndex = 25; + // + // label1 + // + this.label1.Font = new System.Drawing.Font("寰蒋闆呴粦", 10F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + this.label1.Location = new System.Drawing.Point(292, 332); + this.label1.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(137, 31); + this.label1.TabIndex = 24; + this.label1.Text = "鍋滈潬杞﹁締绫诲瀷锛"; + this.label1.TextAlign = System.Drawing.ContentAlignment.MiddleRight; + // + // txtRemarks + // + this.txtRemarks.Font = new System.Drawing.Font("寰蒋闆呴粦", 10F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + this.txtRemarks.Location = new System.Drawing.Point(167, 408); + this.txtRemarks.Margin = new System.Windows.Forms.Padding(4); + this.txtRemarks.Multiline = true; + this.txtRemarks.Name = "txtRemarks"; + this.txtRemarks.ScrollBars = System.Windows.Forms.ScrollBars.Vertical; + this.txtRemarks.Size = new System.Drawing.Size(399, 62); + this.txtRemarks.TabIndex = 21; + // + // lblRemarks + // + this.lblRemarks.Font = new System.Drawing.Font("寰蒋闆呴粦", 10F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + this.lblRemarks.Location = new System.Drawing.Point(27, 408); + this.lblRemarks.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); + this.lblRemarks.Name = "lblRemarks"; + this.lblRemarks.Size = new System.Drawing.Size(133, 31); + this.lblRemarks.TabIndex = 20; + this.lblRemarks.Text = "澶囨敞锛"; + this.lblRemarks.TextAlign = System.Drawing.ContentAlignment.TopRight; + // + // numSiteId + // + this.numSiteId.Font = new System.Drawing.Font("寰蒋闆呴粦", 10F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + this.numSiteId.Location = new System.Drawing.Point(167, 370); + this.numSiteId.Margin = new System.Windows.Forms.Padding(4); + this.numSiteId.Maximum = new decimal(new int[] { + 99999, + 0, + 0, + 0}); + this.numSiteId.Name = "numSiteId"; + this.numSiteId.Size = new System.Drawing.Size(400, 29); + this.numSiteId.TabIndex = 19; + // + // lblSiteId + // + this.lblSiteId.Font = new System.Drawing.Font("寰蒋闆呴粦", 10F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + this.lblSiteId.Location = new System.Drawing.Point(27, 370); + this.lblSiteId.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); + this.lblSiteId.Name = "lblSiteId"; + this.lblSiteId.Size = new System.Drawing.Size(133, 31); + this.lblSiteId.TabIndex = 18; + this.lblSiteId.Text = "绔欑偣ID锛"; + this.lblSiteId.TextAlign = System.Drawing.ContentAlignment.MiddleRight; + // + // chkEnabled + // + this.chkEnabled.AutoSize = true; + this.chkEnabled.Checked = true; + this.chkEnabled.CheckState = System.Windows.Forms.CheckState.Checked; + this.chkEnabled.Font = new System.Drawing.Font("寰蒋闆呴粦", 10F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + this.chkEnabled.Location = new System.Drawing.Point(167, 334); + this.chkEnabled.Margin = new System.Windows.Forms.Padding(4); + this.chkEnabled.Name = "chkEnabled"; + this.chkEnabled.Size = new System.Drawing.Size(117, 27); + this.chkEnabled.TabIndex = 17; + this.chkEnabled.Text = "鍚敤鍏呯數妗"; + this.chkEnabled.UseVisualStyleBackColor = true; + // + // chkShieldSiteMechanismStatus + // + this.chkShieldSiteMechanismStatus.AutoSize = true; + this.chkShieldSiteMechanismStatus.Font = new System.Drawing.Font("寰蒋闆呴粦", 10F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + this.chkShieldSiteMechanismStatus.Location = new System.Drawing.Point(167, 473); + this.chkShieldSiteMechanismStatus.Margin = new System.Windows.Forms.Padding(4); + this.chkShieldSiteMechanismStatus.Name = "chkShieldSiteMechanismStatus"; + this.chkShieldSiteMechanismStatus.Size = new System.Drawing.Size(168, 27); + this.chkShieldSiteMechanismStatus.TabIndex = 18; + this.chkShieldSiteMechanismStatus.Text = "灞忚斀鏈烘瀯鐘舵佷氦浜"; + this.chkShieldSiteMechanismStatus.UseVisualStyleBackColor = true; + // + // numCurrent + // + this.numCurrent.DecimalPlaces = 1; + this.numCurrent.Font = new System.Drawing.Font("寰蒋闆呴粦", 10F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + this.numCurrent.Increment = new decimal(new int[] { + 1, + 0, + 0, + 65536}); + this.numCurrent.Location = new System.Drawing.Point(167, 296); + this.numCurrent.Margin = new System.Windows.Forms.Padding(4); + this.numCurrent.Maximum = new decimal(new int[] { + 110, + 0, + 0, + 0}); + this.numCurrent.Name = "numCurrent"; + this.numCurrent.Size = new System.Drawing.Size(400, 29); + this.numCurrent.TabIndex = 13; + this.numCurrent.Value = new decimal(new int[] { + 10, + 0, + 0, + 0}); + // + // lblCurrent + // + this.lblCurrent.Font = new System.Drawing.Font("寰蒋闆呴粦", 10F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + this.lblCurrent.Location = new System.Drawing.Point(27, 296); + this.lblCurrent.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); + this.lblCurrent.Name = "lblCurrent"; + this.lblCurrent.Size = new System.Drawing.Size(133, 31); + this.lblCurrent.TabIndex = 12; + this.lblCurrent.Text = "鐢垫祦(A)锛"; + this.lblCurrent.TextAlign = System.Drawing.ContentAlignment.MiddleRight; + // + // numVoltage + // + this.numVoltage.DecimalPlaces = 1; + this.numVoltage.Font = new System.Drawing.Font("寰蒋闆呴粦", 10F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + this.numVoltage.Increment = new decimal(new int[] { + 1, + 0, + 0, + 65536}); + this.numVoltage.Location = new System.Drawing.Point(167, 258); + this.numVoltage.Margin = new System.Windows.Forms.Padding(4); + this.numVoltage.Maximum = new decimal(new int[] { + 68, + 0, + 0, + 0}); + this.numVoltage.Name = "numVoltage"; + this.numVoltage.Size = new System.Drawing.Size(400, 29); + this.numVoltage.TabIndex = 11; + // + // lblVoltage + // + this.lblVoltage.Font = new System.Drawing.Font("寰蒋闆呴粦", 10F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + this.lblVoltage.Location = new System.Drawing.Point(27, 258); + this.lblVoltage.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); + this.lblVoltage.Name = "lblVoltage"; + this.lblVoltage.Size = new System.Drawing.Size(133, 31); + this.lblVoltage.TabIndex = 10; + this.lblVoltage.Text = "鐢靛帇(V)锛"; + this.lblVoltage.TextAlign = System.Drawing.ContentAlignment.MiddleRight; + // + // numPort + // + this.numPort.Font = new System.Drawing.Font("寰蒋闆呴粦", 10F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + this.numPort.Location = new System.Drawing.Point(167, 220); + this.numPort.Margin = new System.Windows.Forms.Padding(4); + this.numPort.Maximum = new decimal(new int[] { + 65535, + 0, + 0, + 0}); + this.numPort.Minimum = new decimal(new int[] { + 1, + 0, + 0, + 0}); + this.numPort.Name = "numPort"; + this.numPort.Size = new System.Drawing.Size(400, 29); + this.numPort.TabIndex = 9; + this.numPort.Value = new decimal(new int[] { + 1, + 0, + 0, + 0}); + // + // lblPort + // + this.lblPort.Font = new System.Drawing.Font("寰蒋闆呴粦", 10F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + this.lblPort.Location = new System.Drawing.Point(27, 220); + this.lblPort.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); + this.lblPort.Name = "lblPort"; + this.lblPort.Size = new System.Drawing.Size(133, 31); + this.lblPort.TabIndex = 8; + this.lblPort.Text = "绔彛锛"; + this.lblPort.TextAlign = System.Drawing.ContentAlignment.MiddleRight; + // + // txtIpAddress + // + this.txtIpAddress.Font = new System.Drawing.Font("寰蒋闆呴粦", 10F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + this.txtIpAddress.Location = new System.Drawing.Point(167, 182); + this.txtIpAddress.Margin = new System.Windows.Forms.Padding(4); + this.txtIpAddress.Name = "txtIpAddress"; + this.txtIpAddress.Size = new System.Drawing.Size(399, 29); + this.txtIpAddress.TabIndex = 7; + // + // lblIpAddress + // + this.lblIpAddress.Font = new System.Drawing.Font("寰蒋闆呴粦", 10F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + this.lblIpAddress.Location = new System.Drawing.Point(27, 182); + this.lblIpAddress.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); + this.lblIpAddress.Name = "lblIpAddress"; + this.lblIpAddress.Size = new System.Drawing.Size(133, 31); + this.lblIpAddress.TabIndex = 6; + this.lblIpAddress.Text = "IP鍦板潃锛"; + this.lblIpAddress.TextAlign = System.Drawing.ContentAlignment.MiddleRight; + // + // cmbChargeMethod + // + this.cmbChargeMethod.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.cmbChargeMethod.Font = new System.Drawing.Font("寰蒋闆呴粦", 10F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + this.cmbChargeMethod.FormattingEnabled = true; + this.cmbChargeMethod.Items.AddRange(new object[] { + "鍦板厖", + "灏惧厖", + "渚у厖"}); + this.cmbChargeMethod.Location = new System.Drawing.Point(167, 144); + this.cmbChargeMethod.Margin = new System.Windows.Forms.Padding(4); + this.cmbChargeMethod.Name = "cmbChargeMethod"; + this.cmbChargeMethod.Size = new System.Drawing.Size(399, 31); + this.cmbChargeMethod.TabIndex = 23; + this.cmbChargeMethod.SelectedIndexChanged += new System.EventHandler(this.cmbChargeMethod_SelectedIndexChanged); + // + // lblChargeMethod + // + this.lblChargeMethod.Font = new System.Drawing.Font("寰蒋闆呴粦", 10F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + this.lblChargeMethod.Location = new System.Drawing.Point(27, 144); + this.lblChargeMethod.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); + this.lblChargeMethod.Name = "lblChargeMethod"; + this.lblChargeMethod.Size = new System.Drawing.Size(133, 31); + this.lblChargeMethod.TabIndex = 22; + this.lblChargeMethod.Text = "鍏呯數鏂瑰紡锛"; + this.lblChargeMethod.TextAlign = System.Drawing.ContentAlignment.MiddleRight; + // + // cmbType + // + this.cmbType.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.cmbType.Font = new System.Drawing.Font("寰蒋闆呴粦", 10F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + this.cmbType.FormattingEnabled = true; + this.cmbType.Items.AddRange(new object[] { + "FRLD楂樻鍏呯數妗", + "FRLD鐭鍏呯數妗", + "鐗ф槦鍏呯數妗"}); + this.cmbType.Location = new System.Drawing.Point(167, 106); + this.cmbType.Margin = new System.Windows.Forms.Padding(4); + this.cmbType.Name = "cmbType"; + this.cmbType.Size = new System.Drawing.Size(399, 31); + this.cmbType.TabIndex = 5; + // + // lblType + // + this.lblType.Font = new System.Drawing.Font("寰蒋闆呴粦", 10F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + this.lblType.Location = new System.Drawing.Point(27, 106); + this.lblType.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); + this.lblType.Name = "lblType"; + this.lblType.Size = new System.Drawing.Size(133, 31); + this.lblType.TabIndex = 4; + this.lblType.Text = "绫诲瀷锛"; + this.lblType.TextAlign = System.Drawing.ContentAlignment.MiddleRight; + // + // txtName + // + this.txtName.Font = new System.Drawing.Font("寰蒋闆呴粦", 10F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + this.txtName.Location = new System.Drawing.Point(167, 64); + this.txtName.Margin = new System.Windows.Forms.Padding(4); + this.txtName.Name = "txtName"; + this.txtName.Size = new System.Drawing.Size(192, 29); + this.txtName.TabIndex = 3; + // + // lblName + // + this.lblName.Font = new System.Drawing.Font("寰蒋闆呴粦", 10F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + this.lblName.Location = new System.Drawing.Point(26, 62); + this.lblName.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); + this.lblName.Name = "lblName"; + this.lblName.Size = new System.Drawing.Size(133, 31); + this.lblName.TabIndex = 2; + this.lblName.Text = "鍏呯數妗╁悕绉帮細"; + this.lblName.TextAlign = System.Drawing.ContentAlignment.MiddleRight; + // + // txtStationId + // + this.txtStationId.Font = new System.Drawing.Font("寰蒋闆呴粦", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + this.txtStationId.Location = new System.Drawing.Point(167, 29); + this.txtStationId.Margin = new System.Windows.Forms.Padding(4); + this.txtStationId.Name = "txtStationId"; + this.txtStationId.Size = new System.Drawing.Size(192, 27); + this.txtStationId.TabIndex = 1; + // + // lblStationId + // + this.lblStationId.Font = new System.Drawing.Font("寰蒋闆呴粦", 10F); + this.lblStationId.Location = new System.Drawing.Point(49, 31); + this.lblStationId.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); + this.lblStationId.Name = "lblStationId"; + this.lblStationId.Size = new System.Drawing.Size(127, 25); + this.lblStationId.TabIndex = 0; + this.lblStationId.Text = "鍏呯數妗╃紪鍙:"; + this.lblStationId.TextAlign = System.Drawing.ContentAlignment.BottomLeft; + // + // pnlEditButtons + // + this.pnlEditButtons.Controls.Add(this.btnCancel); + this.pnlEditButtons.Controls.Add(this.btnDelete); + this.pnlEditButtons.Controls.Add(this.btnSave); + this.pnlEditButtons.Dock = System.Windows.Forms.DockStyle.Bottom; + this.pnlEditButtons.Location = new System.Drawing.Point(0, 750); + this.pnlEditButtons.Margin = new System.Windows.Forms.Padding(4); + this.pnlEditButtons.Name = "pnlEditButtons"; + this.pnlEditButtons.Padding = new System.Windows.Forms.Padding(13, 12, 13, 12); + this.pnlEditButtons.Size = new System.Drawing.Size(645, 125); + this.pnlEditButtons.TabIndex = 0; + // + // btnCancel + // + this.btnCancel.Font = new System.Drawing.Font("寰蒋闆呴粦", 11F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + this.btnCancel.Location = new System.Drawing.Point(373, 25); + this.btnCancel.Margin = new System.Windows.Forms.Padding(4); + this.btnCancel.Name = "btnCancel"; + this.btnCancel.Size = new System.Drawing.Size(133, 62); + this.btnCancel.TabIndex = 2; + this.btnCancel.Text = "鍙栨秷"; + this.btnCancel.UseVisualStyleBackColor = true; + this.btnCancel.Click += new System.EventHandler(this.btnCancel_Click); + // + // btnDelete + // + this.btnDelete.BackColor = System.Drawing.Color.LightCoral; + this.btnDelete.Font = new System.Drawing.Font("寰蒋闆呴粦", 11F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + this.btnDelete.Location = new System.Drawing.Point(200, 25); + this.btnDelete.Margin = new System.Windows.Forms.Padding(4); + this.btnDelete.Name = "btnDelete"; + this.btnDelete.Size = new System.Drawing.Size(133, 62); + this.btnDelete.TabIndex = 1; + this.btnDelete.Text = "鍒犻櫎"; + this.btnDelete.UseVisualStyleBackColor = false; + this.btnDelete.Click += new System.EventHandler(this.btnDelete_Click); + // + // btnSave + // + this.btnSave.BackColor = System.Drawing.Color.LightBlue; + this.btnSave.Font = new System.Drawing.Font("寰蒋闆呴粦", 11F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + this.btnSave.Location = new System.Drawing.Point(27, 25); + this.btnSave.Margin = new System.Windows.Forms.Padding(4); + this.btnSave.Name = "btnSave"; + this.btnSave.Size = new System.Drawing.Size(133, 62); + this.btnSave.TabIndex = 0; + this.btnSave.Text = "淇濆瓨"; + this.btnSave.UseVisualStyleBackColor = false; + this.btnSave.Click += new System.EventHandler(this.btnSave_Click); + // + // colStationId + // + this.colStationId.AutoSizeMode = System.Windows.Forms.DataGridViewAutoSizeColumnMode.None; + dataGridViewCellStyle2.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleCenter; + this.colStationId.DefaultCellStyle = dataGridViewCellStyle2; + this.colStationId.HeaderText = "缂栧彿"; + this.colStationId.MinimumWidth = 6; + this.colStationId.Name = "colStationId"; + this.colStationId.ReadOnly = true; + this.colStationId.Resizable = System.Windows.Forms.DataGridViewTriState.False; + this.colStationId.Width = 30; + // + // colName + // + this.colName.AutoSizeMode = System.Windows.Forms.DataGridViewAutoSizeColumnMode.None; + dataGridViewCellStyle3.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleCenter; + this.colName.DefaultCellStyle = dataGridViewCellStyle3; + this.colName.HeaderText = "鍚嶇О"; + this.colName.MinimumWidth = 6; + this.colName.Name = "colName"; + this.colName.ReadOnly = true; + this.colName.Resizable = System.Windows.Forms.DataGridViewTriState.False; + this.colName.Width = 30; + // + // colType + // + this.colType.AutoSizeMode = System.Windows.Forms.DataGridViewAutoSizeColumnMode.None; + dataGridViewCellStyle4.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleCenter; + this.colType.DefaultCellStyle = dataGridViewCellStyle4; + this.colType.FillWeight = 189.1357F; + this.colType.HeaderText = "绫诲瀷"; + this.colType.MinimumWidth = 6; + this.colType.Name = "colType"; + this.colType.ReadOnly = true; + this.colType.Resizable = System.Windows.Forms.DataGridViewTriState.False; + this.colType.Width = 110; + // + // colChargeMethod + // + this.colChargeMethod.AutoSizeMode = System.Windows.Forms.DataGridViewAutoSizeColumnMode.None; + dataGridViewCellStyle5.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleCenter; + this.colChargeMethod.DefaultCellStyle = dataGridViewCellStyle5; + this.colChargeMethod.FillWeight = 98.90109F; + this.colChargeMethod.HeaderText = "鍏呯數鏂瑰紡"; + this.colChargeMethod.MinimumWidth = 6; + this.colChargeMethod.Name = "colChargeMethod"; + this.colChargeMethod.ReadOnly = true; + this.colChargeMethod.Resizable = System.Windows.Forms.DataGridViewTriState.False; + this.colChargeMethod.Width = 50; + // + // colSiteId + // + dataGridViewCellStyle6.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleCenter; + this.colSiteId.DefaultCellStyle = dataGridViewCellStyle6; + this.colSiteId.FillWeight = 59.72706F; + this.colSiteId.HeaderText = "绔欑偣缂栧彿"; + this.colSiteId.MinimumWidth = 6; + this.colSiteId.Name = "colSiteId"; + this.colSiteId.ReadOnly = true; + this.colSiteId.Resizable = System.Windows.Forms.DataGridViewTriState.False; + // + // colLastSendTime + // + dataGridViewCellStyle7.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleCenter; + this.colLastSendTime.DefaultCellStyle = dataGridViewCellStyle7; + this.colLastSendTime.FillWeight = 59.72706F; + this.colLastSendTime.HeaderText = "鍙戦佹椂闂"; + this.colLastSendTime.MinimumWidth = 6; + this.colLastSendTime.Name = "colLastSendTime"; + this.colLastSendTime.ReadOnly = true; + this.colLastSendTime.Resizable = System.Windows.Forms.DataGridViewTriState.False; + // + // colLastReceiveTime + // + dataGridViewCellStyle8.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleCenter; + this.colLastReceiveTime.DefaultCellStyle = dataGridViewCellStyle8; + this.colLastReceiveTime.FillWeight = 59.72706F; + this.colLastReceiveTime.HeaderText = "鎺ユ敹鏃堕棿"; + this.colLastReceiveTime.MinimumWidth = 6; + this.colLastReceiveTime.Name = "colLastReceiveTime"; + this.colLastReceiveTime.ReadOnly = true; + this.colLastReceiveTime.Resizable = System.Windows.Forms.DataGridViewTriState.False; + // + // colCommStatus + // + dataGridViewCellStyle9.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleCenter; + this.colCommStatus.DefaultCellStyle = dataGridViewCellStyle9; + this.colCommStatus.FillWeight = 59.72706F; + this.colCommStatus.HeaderText = "閫氳"; + this.colCommStatus.MinimumWidth = 6; + this.colCommStatus.Name = "colCommStatus"; + this.colCommStatus.ReadOnly = true; + this.colCommStatus.Resizable = System.Windows.Forms.DataGridViewTriState.False; + // + // colChargeCommandStatus + // + dataGridViewCellStyle10.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleCenter; + this.colChargeCommandStatus.DefaultCellStyle = dataGridViewCellStyle10; + this.colChargeCommandStatus.FillWeight = 59.72706F; + this.colChargeCommandStatus.HeaderText = "鍏呯數鎸囦护"; + this.colChargeCommandStatus.MinimumWidth = 6; + this.colChargeCommandStatus.Name = "colChargeCommandStatus"; + this.colChargeCommandStatus.ReadOnly = true; + this.colChargeCommandStatus.Resizable = System.Windows.Forms.DataGridViewTriState.False; + // + // colMechanismStatus + // + dataGridViewCellStyle11.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleCenter; + this.colMechanismStatus.DefaultCellStyle = dataGridViewCellStyle11; + this.colMechanismStatus.FillWeight = 59.72706F; + this.colMechanismStatus.HeaderText = "鏈烘瀯鐘舵"; + this.colMechanismStatus.MinimumWidth = 6; + this.colMechanismStatus.Name = "colMechanismStatus"; + this.colMechanismStatus.ReadOnly = true; + this.colMechanismStatus.Resizable = System.Windows.Forms.DataGridViewTriState.False; + // + // colCurrentVehicle + // + dataGridViewCellStyle12.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleCenter; + this.colCurrentVehicle.DefaultCellStyle = dataGridViewCellStyle12; + this.colCurrentVehicle.FillWeight = 59.72706F; + this.colCurrentVehicle.HeaderText = "褰撳墠杞﹁締"; + this.colCurrentVehicle.MinimumWidth = 6; + this.colCurrentVehicle.Name = "colCurrentVehicle"; + this.colCurrentVehicle.ReadOnly = true; + this.colCurrentVehicle.Resizable = System.Windows.Forms.DataGridViewTriState.False; + // + // colBatteryLevel + // + dataGridViewCellStyle13.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleCenter; + dataGridViewCellStyle13.Format = "%"; + dataGridViewCellStyle13.NullValue = "0%"; + this.colBatteryLevel.DefaultCellStyle = dataGridViewCellStyle13; + this.colBatteryLevel.FillWeight = 59.72706F; + this.colBatteryLevel.HeaderText = "鐢甸噺"; + this.colBatteryLevel.MinimumWidth = 6; + this.colBatteryLevel.Name = "colBatteryLevel"; + this.colBatteryLevel.ReadOnly = true; + this.colBatteryLevel.Resizable = System.Windows.Forms.DataGridViewTriState.False; + // + // colAlarm + // + this.colAlarm.AutoSizeMode = System.Windows.Forms.DataGridViewAutoSizeColumnMode.None; + dataGridViewCellStyle14.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleCenter; + this.colAlarm.DefaultCellStyle = dataGridViewCellStyle14; + this.colAlarm.FillWeight = 120F; + this.colAlarm.HeaderText = "鎶ヨ "; + this.colAlarm.MinimumWidth = 6; + this.colAlarm.Name = "colAlarm"; + this.colAlarm.ReadOnly = true; + this.colAlarm.Resizable = System.Windows.Forms.DataGridViewTriState.False; + this.colAlarm.Width = 110; + // + // colRealTimeVoltage + // + dataGridViewCellStyle15.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleCenter; + dataGridViewCellStyle15.Format = "V"; + dataGridViewCellStyle15.NullValue = "V"; + this.colRealTimeVoltage.DefaultCellStyle = dataGridViewCellStyle15; + this.colRealTimeVoltage.FillWeight = 59.72706F; + this.colRealTimeVoltage.HeaderText = "瀹炴椂鐢靛帇"; + this.colRealTimeVoltage.MinimumWidth = 6; + this.colRealTimeVoltage.Name = "colRealTimeVoltage"; + this.colRealTimeVoltage.ReadOnly = true; + this.colRealTimeVoltage.Resizable = System.Windows.Forms.DataGridViewTriState.False; + // + // colRealTimeCurrent + // + dataGridViewCellStyle16.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleCenter; + this.colRealTimeCurrent.DefaultCellStyle = dataGridViewCellStyle16; + this.colRealTimeCurrent.FillWeight = 59.72706F; + this.colRealTimeCurrent.HeaderText = "瀹炴椂鐢垫祦"; + this.colRealTimeCurrent.MinimumWidth = 6; + this.colRealTimeCurrent.Name = "colRealTimeCurrent"; + this.colRealTimeCurrent.ReadOnly = true; + this.colRealTimeCurrent.Resizable = System.Windows.Forms.DataGridViewTriState.False; + // + // colStatus + // + dataGridViewCellStyle17.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleCenter; + this.colStatus.DefaultCellStyle = dataGridViewCellStyle17; + this.colStatus.FillWeight = 59.72706F; + this.colStatus.HeaderText = "鍏呯數鐘舵"; + this.colStatus.MinimumWidth = 6; + this.colStatus.Name = "colStatus"; + this.colStatus.ReadOnly = true; + this.colStatus.Resizable = System.Windows.Forms.DataGridViewTriState.False; + // + // colEnabled + // + dataGridViewCellStyle18.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleCenter; + this.colEnabled.DefaultCellStyle = dataGridViewCellStyle18; + this.colEnabled.FillWeight = 59.72706F; + this.colEnabled.HeaderText = "鍚敤"; + this.colEnabled.MinimumWidth = 6; + this.colEnabled.Name = "colEnabled"; + this.colEnabled.ReadOnly = true; + this.colEnabled.Resizable = System.Windows.Forms.DataGridViewTriState.False; + // + // ChargeStationManagementForm + // + this.AutoScaleDimensions = new System.Drawing.SizeF(8F, 15F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(1731, 875); + this.Controls.Add(this.splitContainer); + this.Margin = new System.Windows.Forms.Padding(4); + this.MinimumSize = new System.Drawing.Size(1327, 738); + this.Name = "ChargeStationManagementForm"; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen; + this.Text = "鍏呯數妗╃鐞嗙郴缁"; + this.splitContainer.Panel1.ResumeLayout(false); + this.splitContainer.Panel2.ResumeLayout(false); + ((System.ComponentModel.ISupportInitialize)(this.splitContainer)).EndInit(); + this.splitContainer.ResumeLayout(false); + this.pnlList.ResumeLayout(false); + ((System.ComponentModel.ISupportInitialize)(this.dgvStations)).EndInit(); + this.pnlListButtons.ResumeLayout(false); + this.pnlListButtons.PerformLayout(); + this.pnlSearch.ResumeLayout(false); + this.pnlSearch.PerformLayout(); + this.pnlEdit.ResumeLayout(false); + this.grpRealTimeInfo.ResumeLayout(false); + this.grpEditInfo.ResumeLayout(false); + this.grpEditInfo.PerformLayout(); + ((System.ComponentModel.ISupportInitialize)(this.numSiteId)).EndInit(); + ((System.ComponentModel.ISupportInitialize)(this.numCurrent)).EndInit(); + ((System.ComponentModel.ISupportInitialize)(this.numVoltage)).EndInit(); + ((System.ComponentModel.ISupportInitialize)(this.numPort)).EndInit(); + this.pnlEditButtons.ResumeLayout(false); + this.ResumeLayout(false); + + } + + #endregion + + private System.Windows.Forms.SplitContainer splitContainer; + private System.Windows.Forms.Panel pnlList; + private System.Windows.Forms.DataGridView dgvStations; + private System.Windows.Forms.Panel pnlListButtons; + private System.Windows.Forms.Label lblStatistics; + private System.Windows.Forms.Button btnStrategyConfig; + private System.Windows.Forms.Button btnCommMonitor; + private System.Windows.Forms.Button btnAlarmConfig; + private System.Windows.Forms.Button btnExport; + private System.Windows.Forms.Button btnRefresh; + private System.Windows.Forms.Panel pnlSearch; + private System.Windows.Forms.ComboBox cmbStatusFilter; + private System.Windows.Forms.Label lblStatusFilter; + private System.Windows.Forms.TextBox txtSearch; + private System.Windows.Forms.Label lblSearch; + private System.Windows.Forms.Panel pnlEdit; + private System.Windows.Forms.GroupBox grpEditInfo; + private System.Windows.Forms.TextBox txtRemarks; + private System.Windows.Forms.Label lblRemarks; + private System.Windows.Forms.NumericUpDown numSiteId; + private System.Windows.Forms.Label lblSiteId; + private System.Windows.Forms.CheckBox chkEnabled; + private System.Windows.Forms.CheckBox chkShieldSiteMechanismStatus; + private System.Windows.Forms.NumericUpDown numCurrent; + private System.Windows.Forms.Label lblCurrent; + private System.Windows.Forms.NumericUpDown numVoltage; + private System.Windows.Forms.Label lblVoltage; + private System.Windows.Forms.NumericUpDown numPort; + private System.Windows.Forms.Label lblPort; + private System.Windows.Forms.TextBox txtIpAddress; + private System.Windows.Forms.Label lblIpAddress; + private System.Windows.Forms.ComboBox cmbType; + private System.Windows.Forms.Label lblType; + private System.Windows.Forms.ComboBox cmbChargeMethod; + private System.Windows.Forms.Label lblChargeMethod; + private System.Windows.Forms.TextBox txtName; + private System.Windows.Forms.Label lblName; + private System.Windows.Forms.TextBox txtStationId; + private System.Windows.Forms.Label lblStationId; + private System.Windows.Forms.Panel pnlEditButtons; + private System.Windows.Forms.Button btnCancel; + private System.Windows.Forms.Button btnDelete; + private System.Windows.Forms.Button btnSave; + private System.Windows.Forms.GroupBox grpRealTimeInfo; + private System.Windows.Forms.Label lblCurrentVehicleValue; + private System.Windows.Forms.Label lblCurrentVehicle; + private System.Windows.Forms.Label lblBatteryLevelValue; + private System.Windows.Forms.Label lblBatteryLevel; + private System.Windows.Forms.Label lblRealTimeVoltageValue; + private System.Windows.Forms.Label lblRealTimeVoltage; + private System.Windows.Forms.Label lblRealTimeCurrentValue; + private System.Windows.Forms.Label lblRealTimeCurrent; + private System.Windows.Forms.Label lblCommStatusValue; + private System.Windows.Forms.Label lblCommStatus; + private System.Windows.Forms.Label lblChargeCommandStatusValue; + private System.Windows.Forms.Label lblChargeCommandStatus; + private System.Windows.Forms.Label lblMechanismStatusValue; + private System.Windows.Forms.Label lblMechanismStatus; + private System.Windows.Forms.Label lblAlarmValue; + private System.Windows.Forms.Label lblAlarm; + private System.Windows.Forms.ComboBox chargeCarType; + private System.Windows.Forms.Label label1; + private System.Windows.Forms.DataGridViewTextBoxColumn colStationId; + private System.Windows.Forms.DataGridViewTextBoxColumn colName; + private System.Windows.Forms.DataGridViewTextBoxColumn colType; + private System.Windows.Forms.DataGridViewTextBoxColumn colChargeMethod; + private System.Windows.Forms.DataGridViewTextBoxColumn colSiteId; + private System.Windows.Forms.DataGridViewTextBoxColumn colLastSendTime; + private System.Windows.Forms.DataGridViewTextBoxColumn colLastReceiveTime; + private System.Windows.Forms.DataGridViewTextBoxColumn colCommStatus; + private System.Windows.Forms.DataGridViewTextBoxColumn colChargeCommandStatus; + private System.Windows.Forms.DataGridViewTextBoxColumn colMechanismStatus; + private System.Windows.Forms.DataGridViewTextBoxColumn colCurrentVehicle; + private System.Windows.Forms.DataGridViewTextBoxColumn colBatteryLevel; + private System.Windows.Forms.DataGridViewTextBoxColumn colAlarm; + private System.Windows.Forms.DataGridViewTextBoxColumn colRealTimeVoltage; + private System.Windows.Forms.DataGridViewTextBoxColumn colRealTimeCurrent; + private System.Windows.Forms.DataGridViewTextBoxColumn colStatus; + private System.Windows.Forms.DataGridViewTextBoxColumn colEnabled; + } +} + diff --git a/StandardScene.Core/Charge/ChargeStationManagementForm.cs b/StandardScene.Core/Charge/ChargeStationManagementForm.cs new file mode 100644 index 0000000..bd1b27d --- /dev/null +++ b/StandardScene.Core/Charge/ChargeStationManagementForm.cs @@ -0,0 +1,1389 @@ +using SimpleCore; +using System; +using System.Drawing; +using System.Linq; +using System.Net.NetworkInformation; +using System.Windows.Forms; + +namespace StandardScene.Charge +{ + /// + /// 鍏呯數妗╃鐞嗙獥鍙 + /// + public partial class ChargeStationManagementForm : Form + { + private ChargeStationDataService dataService; + private ChargeStation selectedStation; + private System.Windows.Forms.Timer autoRefreshTimer; + private Ping Ping = new Ping(); + private CommunicationMonitorForm communicationMonitorForm; + + public ChargeStationManagementForm() + { + InitializeComponent(); + dataService = ChargeStationDataService.Instance; + InitializeForm(); + InitializeAutoRefresh(); + } + + /// + /// 鍒濆鍖栬嚜鍔ㄥ埛鏂板畾鏃跺櫒 + /// + private void InitializeAutoRefresh() + { + autoRefreshTimer = new System.Windows.Forms.Timer(); + autoRefreshTimer.Interval = 3000; // 姣3绉掑埛鏂颁竴娆 + autoRefreshTimer.Tick += AutoRefreshTimer_Tick; + autoRefreshTimer.Start(); + } + + /// + /// 鑷姩鍒锋柊浜嬩欢 + /// + private void AutoRefreshTimer_Tick(object sender, EventArgs e) + { + // 淇濆瓨褰撳墠閫変腑鐨勫厖鐢垫々ID + string selectedStationId = null; + if (dgvStations.SelectedRows.Count > 0) + { + selectedStationId = dgvStations.SelectedRows[0].Cells[0].Value?.ToString(); + } + + // 鍒锋柊鍒楄〃 + LoadStations(); + + // 鎭㈠閫変腑鐘舵 + if (!string.IsNullOrEmpty(selectedStationId)) + { + foreach (DataGridViewRow row in dgvStations.Rows) + { + if (row.Cells[0].Value?.ToString() == selectedStationId) + { + row.Selected = true; + dgvStations.CurrentCell = row.Cells[0]; + break; + } + } + } + } + + /// + /// 绐椾綋鍏抽棴鏃跺仠姝㈠畾鏃跺櫒 + /// + protected override void OnFormClosing(FormClosingEventArgs e) + { + if (autoRefreshTimer != null) + { + autoRefreshTimer.Stop(); + autoRefreshTimer.Dispose(); + } + base.OnFormClosing(e); + } + + private void InitializeForm() + { + // 璁剧疆绐楀彛灞炴 + this.Text = "鍏呯數妗╃鐞嗙郴缁"; + this.Size = new Size(1200, 700); + this.StartPosition = FormStartPosition.CenterScreen; + this.MinimumSize = new Size(1000, 600); + + // 璁剧疆琛ㄥご鏂囧瓧鍨傜洿鎺掑垪 + SetupVerticalHeaderText(); + + // 鍒濆鍖栫姸鎬佺瓫閫変笅鎷夋 + InitializeStatusFilter(); + + // 鍔犺浇鏁版嵁 + LoadStations(); + + // 璁剧疆榛樿鐘舵佷负鏂板妯″紡 + ClearEditFields(); + } + + /// + /// 璁剧疆琛ㄥご鏂囧瓧鍨傜洿鎺掑垪 + /// + private void SetupVerticalHeaderText() + { + if (dgvStations == null) + return; + + // 澧炲姞鍒楁爣棰橀珮搴︿互瀹圭撼鍨傜洿鏂囧瓧 + dgvStations.ColumnHeadersHeight = 100; + + // 璁㈤槄鍒楁爣棰樼粯鍒朵簨浠 + dgvStations.ColumnHeadersHeightSizeMode = DataGridViewColumnHeadersHeightSizeMode.DisableResizing; + dgvStations.CellPainting += DgvStations_CellPainting; + + // 鍥哄畾琛岄珮搴 + dgvStations.RowTemplate.Height = 35; + dgvStations.AllowUserToResizeRows = false; + dgvStations.RowHeadersWidthSizeMode = DataGridViewRowHeadersWidthSizeMode.DisableResizing; + dgvStations.AutoSizeRowsMode = DataGridViewAutoSizeRowsMode.None; + } + + /// + /// 鑷畾涔夌粯鍒跺垪鏍囬锛堝瀭鐩存枃瀛楋級 + /// + private void DgvStations_CellPainting(object sender, DataGridViewCellPaintingEventArgs e) + { + // 鍙鐞嗗垪鏍囬琛 + if (e.RowIndex == -1 && e.ColumnIndex >= 0) + { + try + { + // 缁樺埗鑳屾櫙 + e.PaintBackground(e.CellBounds, true); + + // 鑾峰彇鍒楁爣棰樻枃鏈 + string headerText = dgvStations.Columns[e.ColumnIndex].HeaderText; + + // 璁剧疆鏂囧瓧鏍煎紡锛堝瀭鐩存帓鍒楋紝浠庝笂鍒颁笅锛 + using (var brush = new SolidBrush(dgvStations.ColumnHeadersDefaultCellStyle.ForeColor)) + using (var format = new StringFormat()) + { + format.Alignment = StringAlignment.Center; + format.LineAlignment = StringAlignment.Near; + format.FormatFlags = StringFormatFlags.DirectionVertical; // 鍨傜洿鏂囧瓧 + + // 璁$畻缁樺埗浣嶇疆锛堝眳涓級 + float x = e.CellBounds.Left + (e.CellBounds.Width - e.Graphics.MeasureString("娴", dgvStations.ColumnHeadersDefaultCellStyle.Font).Width) / 2; + float y = e.CellBounds.Top + 5; + + // 缁樺埗鍨傜洿鏂囧瓧 + e.Graphics.DrawString( + headerText, + dgvStations.ColumnHeadersDefaultCellStyle.Font, + brush, + new RectangleF(x, y, e.CellBounds.Width, e.CellBounds.Height - 10), + format); + } + + // 缁樺埗杈规 + e.Paint(e.CellBounds, DataGridViewPaintParts.Border); + + // 鏍囪涓哄凡澶勭悊 + e.Handled = true; + } + catch (Exception ex) + { + System.Diagnostics.Debug.WriteLine($"缁樺埗鍒楁爣棰樺け璐: {ex.Message}"); + } + } + } + + /// + /// 鍒濆鍖栫姸鎬佺瓫閫変笅鎷夋 + /// + private void InitializeStatusFilter() + { + if (cmbStatusFilter != null) + { + cmbStatusFilter.Items.Clear(); + cmbStatusFilter.Items.Add("鍏ㄩ儴鐘舵"); + cmbStatusFilter.Items.Add("绌洪棽"); + cmbStatusFilter.Items.Add("鍏呯數涓"); + cmbStatusFilter.Items.Add("鏁呴殰"); + cmbStatusFilter.Items.Add("绂荤嚎"); + cmbStatusFilter.SelectedIndex = 0; // 榛樿鏄剧ず鍏ㄩ儴 + } + } + + /// + /// 鍔犺浇鍏呯數妗╁垪琛 + /// + private void LoadStations() + { + try + { + var stations = dataService.GetAllStations(); + + // 鏍规嵁鐘舵佺瓫閫 + if (cmbStatusFilter != null && cmbStatusFilter.SelectedIndex > 0) + { + var filterStatus = GetStatusFromFilterIndex(cmbStatusFilter.SelectedIndex); + stations = stations.Where(s => s.Status == filterStatus).ToList(); + } + + dgvStations.Rows.Clear(); + + foreach (var station in stations) + { + + try + { + if (Ping.Send(station.IpAddress, 1000).Status == IPStatus.Success) + { + station.CommStatus = CommunicationStatus.Normal; + } + else + { + station.CommStatus = CommunicationStatus.Error; + } + } + catch (Exception) + { + + station.CommStatus = CommunicationStatus.Error; + } + + + var index = dgvStations.Rows.Add( + station.StationId, + station.Name, + GetTypeText(station.Type), + GetChargeMethodText(station.ChargeMethod), + //station.IpAddress, + //station.Port, + station.SiteId?.ToString() ?? "", + FormatTimeToMinuteSecond(station.LastSendTime), + FormatTimeToMinuteSecond(station.LastReceiveTime), + FormatCommStatusDisplay(station.CommStatus), + FormatChargeCommandStatusDisplay(station.ChargeCommandStatus), + FormatMechanismStatusDisplay(station.MechanismStatus), + string.IsNullOrWhiteSpace(station.CurrentVehicle) ? "-" : station.CurrentVehicle, + station.BatteryLevel > 0 ? $"{station.BatteryLevel:F1}%" : "-", + FormatAlarmDisplay(station), + //station.SetVoltage, + //station.SetElectricCurrent, + station.RealTimeVoltage.ToString("F1"), + station.RealTimeCurrent.ToString("F1"), + GetStatusText(station.Status), + station.Enabled ? "鏄" : "鍚", + station.Remarks + ); + + // 鏍规嵁鐘舵佽缃棰滆壊锛堟墎骞冲寲璁捐锛 + var row = dgvStations.Rows[index]; + + // 閫氳鐘舵佸垪鏍峰紡璁剧疆锛堢9鍒楋紝鍥犱负澧炲姞浜嗗厖鐢垫柟寮忓垪锛 + var commCell = row.Cells[9]; + switch (station.CommStatus) + { + case CommunicationStatus.Normal: + commCell.Style.ForeColor = Color.FromArgb(76, 175, 80); // 缁胯壊 + // commCell.Style.Font = new Font(commCell.Style.Font ?? row.DefaultCellStyle.Font, FontStyle.Bold); + break; + case CommunicationStatus.Delayed: + commCell.Style.ForeColor = Color.FromArgb(255, 152, 0); // 姗欒壊 + // commCell.Style.Font = new Font(commCell.Style.Font ?? row.DefaultCellStyle.Font, FontStyle.Bold); + break; + case CommunicationStatus.Timeout: + case CommunicationStatus.Disconnected: + case CommunicationStatus.Error: + commCell.Style.ForeColor = Color.FromArgb(244, 67, 54); // 绾㈣壊 + // commCell.Style.Font = new Font(commCell.Style.Font ?? row.DefaultCellStyle.Font, FontStyle.Bold); + break; + case CommunicationStatus.Unknown: + commCell.Style.ForeColor = Color.FromArgb(158, 158, 158); // 鐏拌壊 + break; + } + + // 鍏呯數鎸囦护鐘舵佸垪鏍峰紡璁剧疆锛堢10鍒楋級 + var chargeCommandCell = row.Cells[10]; + switch (station.ChargeCommandStatus) + { + case ChargeCommandStatus.Stopped: + chargeCommandCell.Style.ForeColor = Color.FromArgb(158, 158, 158); // 鐏拌壊 + break; + case ChargeCommandStatus.Started: + chargeCommandCell.Style.ForeColor = Color.FromArgb(76, 175, 80); // 缁胯壊 + // chargeCommandCell.Style.Font = new Font(chargeCommandCell.Style.Font ?? row.DefaultCellStyle.Font, FontStyle.Bold); + break; + } + + // 鏈烘瀯鐘舵佸垪鏍峰紡璁剧疆锛堢11鍒楋級 + var mechanismCell = row.Cells[11]; + switch (station.MechanismStatus) + { + + case MechanismStatus.Retracted: + mechanismCell.Style.ForeColor = Color.FromArgb(76, 175, 80); // 缁胯壊 + //mechanismCell.Style.Font = new Font(mechanismCell.Style.Font ?? row.DefaultCellStyle.Font, FontStyle.Bold); + break; + case MechanismStatus.Extending: + mechanismCell.Style.ForeColor = Color.FromArgb(33, 150, 243); // 钃濊壊 + //mechanismCell.Style.Font = new Font(mechanismCell.Style.Font ?? row.DefaultCellStyle.Font, FontStyle.Bold); + break; + case MechanismStatus.Extended: + mechanismCell.Style.ForeColor = Color.FromArgb(255, 0, 0); // + //mechanismCell.Style.Font = new Font(mechanismCell.Style.Font ?? row.DefaultCellStyle.Font, FontStyle.Bold); + break; + break; + } + + // 鐢甸噺鍒楁牱寮忚缃紙绗13鍒楋級 + if (station.BatteryLevel > 0) + { + var batteryCell = row.Cells[13]; + if (station.BatteryLevel >= 80) + { + batteryCell.Style.ForeColor = Color.FromArgb(76, 175, 80); // 缁胯壊 - 鐢甸噺鍏呰冻 + //batteryCell.Style.Font = new Font(batteryCell.Style.Font ?? row.DefaultCellStyle.Font, FontStyle.Bold); + } + else if (station.BatteryLevel >= 50) + { + batteryCell.Style.ForeColor = Color.FromArgb(33, 150, 243); // 钃濊壊 - 鐢甸噺涓瓑 + //batteryCell.Style.Font = new Font(batteryCell.Style.Font ?? row.DefaultCellStyle.Font, FontStyle.Bold); + } + else if (station.BatteryLevel >= 20) + { + batteryCell.Style.ForeColor = Color.FromArgb(255, 152, 0); // 姗欒壊 - 鐢甸噺鍋忎綆 + //batteryCell.Style.Font = new Font(batteryCell.Style.Font ?? row.DefaultCellStyle.Font, FontStyle.Bold); + } + else + { + batteryCell.Style.ForeColor = Color.FromArgb(244, 67, 54); // 绾㈣壊 - 鐢甸噺浣 + //batteryCell.Style.Font = new Font(batteryCell.Style.Font ?? row.DefaultCellStyle.Font, FontStyle.Bold); + } + } + + // 濡傛灉鏈夋姤璀︼紝鏁磋鏄剧ず绾㈣壊 + if (station.HasAlarm) + { + row.DefaultCellStyle.BackColor = Color.FromArgb(255, 205, 210); // 娴呯孩鑹茶儗鏅 #FFCDD2 + row.DefaultCellStyle.ForeColor = Color.FromArgb(198, 40, 40); // 娣辩孩鑹叉枃瀛 + var baseFont = row.DefaultCellStyle.Font ?? dgvStations.DefaultCellStyle.Font ?? new Font("寰蒋闆呴粦", 9F); + row.DefaultCellStyle.Font = new Font(baseFont, FontStyle.Bold); + } + else + { + // 鏍规嵁鐘舵佽缃鑹 + switch (station.Status) + { + case ChargeStationStatus.Idle: + row.DefaultCellStyle.BackColor = Color.FromArgb(232, 245, 233); // 娴呯豢鑹 #E8F5E9 + row.DefaultCellStyle.ForeColor = Color.FromArgb(46, 125, 50); // 娣辩豢鑹叉枃瀛 + break; + case ChargeStationStatus.Charging: + row.DefaultCellStyle.BackColor = Color.FromArgb(200, 230, 201); // 浜豢鑹 #C8E6C9 + row.DefaultCellStyle.ForeColor = Color.FromArgb(27, 94, 32); // 娣辩豢鑹叉枃瀛 + break; + case ChargeStationStatus.Fault: + row.DefaultCellStyle.BackColor = Color.FromArgb(255, 205, 210); // 娴呯孩鑹 #FFCDD2 + row.DefaultCellStyle.ForeColor = Color.FromArgb(198, 40, 40); // 娣辩孩鑹叉枃瀛 + break; + case ChargeStationStatus.Battery: + row.DefaultCellStyle.BackColor = Color.FromArgb(238, 238, 238); // 娴呯伆鑹 #EEEEEE + row.DefaultCellStyle.ForeColor = Color.FromArgb(97, 97, 97); // 娣辩伆鑹叉枃瀛 + break; + } + } + } + + // 鏇存柊缁熻淇℃伅 + UpdateStatistics(); + + // 鏇存柊鏍囬鏄剧ず绛涢夌姸鎬 + UpdateTitleWithFilter(stations.Count); + } + catch (Exception ex) + { + MessageBox.Show($"鍔犺浇鏁版嵁澶辫触: {ex.Message}\n\n鍫嗘爤:\n{ex.StackTrace}", "閿欒", + MessageBoxButtons.OK, MessageBoxIcon.Error); + } + } + + /// + /// 鏍规嵁绛涢夊櫒绱㈠紩鑾峰彇鐘舵 + /// + private ChargeStationStatus GetStatusFromFilterIndex(int index) + { + switch (index) + { + case 1: return ChargeStationStatus.Idle; // 绌洪棽 + case 2: return ChargeStationStatus.Charging; // 鍏呯數涓 + case 3: return ChargeStationStatus.Fault; // 鏁呴殰 + case 4: return ChargeStationStatus.Battery; // 绂荤嚎 + default: return ChargeStationStatus.Idle; + } + } + + /// + /// 鏇存柊鏍囬鏄剧ず绛涢変俊鎭 + /// + private void UpdateTitleWithFilter(int displayCount) + { + var totalCount = dataService.GetAllStations().Count; + if (cmbStatusFilter != null && cmbStatusFilter.SelectedIndex > 0) + { + this.Text = $"鍏呯數妗╃鐞嗙郴缁 - 鏄剧ず: {displayCount}/{totalCount} ({cmbStatusFilter.Text})"; + } + else + { + this.Text = $"鍏呯數妗╃鐞嗙郴缁 - 鎬绘暟: {totalCount}"; + } + } + + /// + /// 鏇存柊缁熻淇℃伅 + /// + private void UpdateStatistics() + { + var stations = dataService.GetAllStations(); + var total = stations.Count; + var idle = stations.Count(s => s.Status == ChargeStationStatus.Idle); + 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); + + lblStatistics.Text = $"鎬绘暟: {total} | 绌洪棽: {idle} | 鍏呯數涓: {charging} | 鏁呴殰: {fault} | AGV鐢垫睜宸叉帴鍏: {offline}"; + } + + /// + /// 鑾峰彇鐘舵佹枃鏈 + /// + private string GetStatusText(ChargeStationStatus status) + { + switch (status) + { + case ChargeStationStatus.Idle: return "绌洪棽"; + case ChargeStationStatus.Charging: return "鍏呯數涓"; + case ChargeStationStatus.Fault: return "鏁呴殰"; + case ChargeStationStatus.Battery: return "AGV鐢垫睜宸叉帴鍏"; + default: return "鏈煡"; + } + } + + /// + /// 鏍煎紡鍖栨椂闂翠负 mm:ss 鏍煎紡 + /// + private string FormatTimeToMinuteSecond(DateTime? dateTime) + { + if (dateTime == null) + { + return "--:--"; + } + return dateTime.Value.ToString("mm:ss"); + } + + /// + /// 鏍煎紡鍖栨満鏋勪几缂╃姸鎬佹樉绀 + /// + private string FormatMechanismStatusDisplay(MechanismStatus status) + { + switch (status) + { + case MechanismStatus.Extended: + return "鈼 浼稿嚭"; + case MechanismStatus.Retracted: + return "鈼 缂╁洖"; + case MechanismStatus.Extending: + return "鈻 杩愬姩涓"; + default: + return "? 鏈煡"; + } + } + + /// + /// 鏍煎紡鍖栧厖鐢垫寚浠ょ姸鎬佹樉绀 + /// + private string FormatChargeCommandStatusDisplay(ChargeCommandStatus status) + { + switch (status) + { + case ChargeCommandStatus.Stopped: + return "鈼 鍋滄"; + case ChargeCommandStatus.Started: + return "鈻 鍚姩"; + default: + return "鈼 鍋滄"; + } + } + + /// + /// 鏍煎紡鍖栭氳鐘舵佹樉绀 + /// + private string FormatCommStatusDisplay(CommunicationStatus status) + { + switch (status) + { + case CommunicationStatus.Normal: + return "鉁 姝e父"; + case CommunicationStatus.Delayed: + return "鈿 寤惰繜"; + case CommunicationStatus.Timeout: + return "鉁 瓒呮椂"; + case CommunicationStatus.Disconnected: + return "鉁 鏂紑"; + case CommunicationStatus.Error: + return "鉁 閿欒"; + case CommunicationStatus.Unknown: + default: + return "? 鏈煡"; + } + } + + /// + /// 鏍煎紡鍖栨姤璀︿俊鎭樉绀 + /// + private string FormatAlarmDisplay(ChargeStation station) + { + if (!station.HasAlarm) + { + return "姝e父"; + } + + string levelText = GetAlarmLevelText(station.AlarmLevel); + if (string.IsNullOrWhiteSpace(station.AlarmMessage)) + { + return $"銆恵levelText}銆"; + } + + return $"{station.AlarmMessage}"; + } + + /// + /// 鑾峰彇鎶ヨ绾у埆鏂囨湰 + /// + private string GetAlarmLevelText(AlarmLevel level) + { + switch (level) + { + case AlarmLevel.None: return "鏃"; + case AlarmLevel.Low: return "浣"; + case AlarmLevel.Medium: return "涓"; + case AlarmLevel.High: return "楂"; + case AlarmLevel.Critical: return "涓ラ噸"; + default: return "鏈煡"; + } + } + + /// + /// 鑾峰彇绫诲瀷鏂囨湰 + /// + private string GetTypeText(ChargeStationType type) + { + switch (type) + { + case ChargeStationType.FRLDTall: return "FRLD楂樻鍏呯數妗"; + case ChargeStationType.FRLDShort: return "FRLD鐭鍏呯數妗"; + case ChargeStationType.MuXing: return "鐗ф槦鍏呯數妗"; + default: return "鏈煡"; + } + } + + /// + /// 鑾峰彇鍏呯數鏂瑰紡鏂囨湰 + /// + private string GetChargeMethodText(ChargeMethodType method) + { + switch (method) + { + case ChargeMethodType.Ground: return "鍦板厖"; + case ChargeMethodType.Rear: return "灏惧厖"; + case ChargeMethodType.Side: return "渚у厖"; + default: return "鏈煡"; + } + } + + /// + /// 璁剧疆缂栬緫妯″紡 + /// + /// + /// 娓呯┖缂栬緫鍖 + /// + private void ClearEditFields() + { + selectedStation = null; + txtStationId.Text = ""; // 鎵嬪姩杈撳叆缂栧彿 + txtName.Text = ""; + cmbType.SelectedIndex = 1; + cmbChargeMethod.SelectedIndex = 0; // 榛樿鍦板厖 + chargeCarType.SelectedIndex = 0; + // 瑙﹀彂鍏呯數鏂瑰紡鏀瑰彉浜嬩欢锛屾洿鏂"灞忚斀鏈烘瀯鐘舵佷氦浜"鐨勫彲瑙佹 + cmbChargeMethod_SelectedIndexChanged(null, null); + + txtIpAddress.Text = "192.168."; + numPort.Value = 2000; + numVoltage.Value = 48; + numCurrent.Value = 32; + chkEnabled.Checked = true; + chkShieldSiteMechanismStatus.Checked = false; + numSiteId.Value = 0; + txtRemarks.Text = ""; + + // 娓呯┖瀹炴椂鐘舵佹樉绀 + ClearRealTimeInfo(); + + // 鏂板妯″紡锛氭墍鏈夊瓧娈靛彲缂栬緫 + SetEditMode(true); + btnSave.Text = "淇濆瓨"; + btnDelete.Enabled = false; + } + + /// + /// 娓呯┖瀹炴椂鐘舵佹樉绀哄尯鍩 + /// + private void ClearRealTimeInfo() + { + lblCurrentVehicleValue.Text = "-"; + lblCurrentVehicleValue.ForeColor = Color.Gray; + + lblBatteryLevelValue.Text = "-"; + lblBatteryLevelValue.ForeColor = Color.Gray; + + lblRealTimeVoltageValue.Text = "0.0 V"; + lblRealTimeVoltageValue.ForeColor = Color.Gray; + + lblRealTimeCurrentValue.Text = "0.0 A"; + lblRealTimeCurrentValue.ForeColor = Color.Gray; + + lblCommStatusValue.Text = "? 鏈煡"; + lblCommStatusValue.ForeColor = Color.Gray; + + lblChargeCommandStatusValue.Text = "鈼 鍋滄"; + lblChargeCommandStatusValue.ForeColor = Color.Gray; + + lblMechanismStatusValue.Text = "? 鏈煡"; + lblMechanismStatusValue.ForeColor = Color.Gray; + + lblAlarmValue.Text = "-"; + lblAlarmValue.ForeColor = Color.Gray; + } + + /// + /// 璁剧疆缂栬緫妯″紡 + /// + /// true=鍙紪杈戯紝false=鍙 + private void SetEditMode(bool editable, bool isList = false) + { + // 缂栧彿鍦ㄦ柊澧炴椂鍙紪杈戯紝缂栬緫鏃跺彧璇 + if (selectedStation == null) + { + // 鏂板妯″紡锛氱紪鍙峰彲缂栬緫 + txtStationId.ReadOnly = false; + txtStationId.BackColor = Color.White; + } + else + { + // 缂栬緫妯″紡锛氱紪鍙峰彧璇 + txtStationId.ReadOnly = true; + txtStationId.BackColor = Color.LightGray; + } + + // 鍏朵粬瀛楁鏍规嵁鍙傛暟璁剧疆 + txtName.ReadOnly = !editable; + cmbType.Enabled = isList ? false : editable; + cmbChargeMethod.Enabled = editable; + txtIpAddress.ReadOnly = !editable; + numPort.ReadOnly = !editable; + numVoltage.ReadOnly = !editable; + numCurrent.ReadOnly = !editable; + chkEnabled.Enabled = editable; + chkShieldSiteMechanismStatus.Enabled = editable; + numSiteId.ReadOnly = !editable; + txtRemarks.ReadOnly = !editable; + + // 璁剧疆鑳屾櫙棰滆壊 + if (!editable) + { + txtName.BackColor = Color.WhiteSmoke; + txtIpAddress.BackColor = Color.WhiteSmoke; + txtRemarks.BackColor = Color.WhiteSmoke; + } + else + { + txtName.BackColor = Color.White; + txtIpAddress.BackColor = Color.White; + txtRemarks.BackColor = Color.White; + } + + // 鎺у埗鎸夐挳鐘舵 + btnSave.Enabled = editable; + } + + /// + /// 浠庣紪杈戝尯鍒涘缓鍏呯數妗╁璞 + /// + private ChargeStation CreateStationFromFields() + { + //var station = selectedStation ?? new ChargeStation(); + var station = new ChargeStation(); + station.StationId = txtStationId.Text.Trim(); + station.Name = txtName.Text.Trim(); + station.Type = (ChargeStationType)cmbType.SelectedIndex; + station.ChargeMethod = (ChargeMethodType)cmbChargeMethod.SelectedIndex; + station.IpAddress = txtIpAddress.Text.Trim(); + station.Port = (int)numPort.Value; + station.SetVoltage = (double)numVoltage.Value; + station.SetElectricCurrent = (double)numCurrent.Value; + station.Enabled = chkEnabled.Checked; + station.ShieldSiteMechanismStatus = chkShieldSiteMechanismStatus.Checked; + station.GroupCarType = (ChargeStationCarType)chargeCarType.SelectedIndex; + station.SiteId = numSiteId.Value > 0 ? (int?)numSiteId.Value : null; + station.Remarks = txtRemarks.Text.Trim(); + + return station; + } + + /// + /// 鍔犺浇鍏呯數妗╁埌缂栬緫鍖 + /// + private void LoadStationToFields(ChargeStation station) + { + selectedStation = station; + + // 鍔犺浇鍩烘湰淇℃伅锛堝彲缂栬緫閮ㄥ垎锛 + txtStationId.Text = station.StationId; + txtName.Text = station.Name; + cmbType.SelectedIndex = (int)station.Type; + cmbChargeMethod.SelectedIndex = (int)station.ChargeMethod; + + // 瑙﹀彂鍏呯數鏂瑰紡鏀瑰彉浜嬩欢锛屾洿鏂"灞忚斀鏈烘瀯鐘舵佷氦浜"鐨勫彲瑙佹 + cmbChargeMethod_SelectedIndexChanged(null, null); + + txtIpAddress.Text = station.IpAddress; + numPort.Value = station.Port; + numVoltage.Value = (decimal)station.SetVoltage; + numCurrent.Value = (decimal)station.SetElectricCurrent; + chkEnabled.Checked = station.Enabled; + chkShieldSiteMechanismStatus.Checked = station.ShieldSiteMechanismStatus; + chargeCarType.SelectedIndex = (int)station.GroupCarType; + numSiteId.Value = station.SiteId ?? 0; + txtRemarks.Text = station.Remarks ?? ""; + + // 鍔犺浇瀹炴椂鐘舵佷俊鎭紙鍙閮ㄥ垎锛 + LoadRealTimeInfo(station); + + // 鏌ョ湅妯″紡锛氭墍鏈夊瓧娈靛彧璇 + SetEditMode(false); + btnSave.Text = "淇敼"; + btnDelete.Enabled = true; + } + + /// + /// 鍔犺浇瀹炴椂鐘舵佷俊鎭埌鏄剧ず鍖哄煙 + /// + private void LoadRealTimeInfo(ChargeStation station) + { + // 褰撳墠杞﹁締 + lblCurrentVehicleValue.Text = string.IsNullOrWhiteSpace(station.CurrentVehicle) ? "-" : station.CurrentVehicle; + + // 鐢甸噺 + if (station.BatteryLevel > 0) + { + lblBatteryLevelValue.Text = $"{station.BatteryLevel:F1}%"; + if (station.BatteryLevel >= 80) + { + lblBatteryLevelValue.ForeColor = Color.FromArgb(76, 175, 80); // 缁胯壊 + } + else if (station.BatteryLevel >= 50) + { + lblBatteryLevelValue.ForeColor = Color.FromArgb(33, 150, 243); // 钃濊壊 + } + else if (station.BatteryLevel >= 20) + { + lblBatteryLevelValue.ForeColor = Color.FromArgb(255, 152, 0); // 姗欒壊 + } + else + { + lblBatteryLevelValue.ForeColor = Color.FromArgb(244, 67, 54); // 绾㈣壊 + } + } + else + { + lblBatteryLevelValue.Text = "-"; + lblBatteryLevelValue.ForeColor = Color.Gray; + } + + // 瀹炴椂鐢靛帇 + lblRealTimeVoltageValue.Text = $"{station.RealTimeVoltage:F1} V"; + lblRealTimeVoltageValue.ForeColor = station.RealTimeVoltage > 0 + ? Color.FromArgb(33, 150, 243) // 钃濊壊 + : Color.Gray; + + // 瀹炴椂鐢垫祦 + lblRealTimeCurrentValue.Text = $"{station.RealTimeCurrent:F1} A"; + lblRealTimeCurrentValue.ForeColor = station.RealTimeCurrent > 0 + ? Color.FromArgb(33, 150, 243) // 钃濊壊 + : Color.Gray; + + // 閫氳鐘舵 + lblCommStatusValue.Text = FormatCommStatusDisplay(station.CommStatus); + switch (station.CommStatus) + { + case CommunicationStatus.Normal: + lblCommStatusValue.ForeColor = Color.FromArgb(76, 175, 80); // 缁胯壊 + break; + case CommunicationStatus.Delayed: + lblCommStatusValue.ForeColor = Color.FromArgb(255, 193, 7); // 榛勮壊 + break; + case CommunicationStatus.Timeout: + case CommunicationStatus.Disconnected: + case CommunicationStatus.Error: + lblCommStatusValue.ForeColor = Color.FromArgb(244, 67, 54); // 绾㈣壊 + break; + default: + lblCommStatusValue.ForeColor = Color.Gray; + break; + } + + // 鍏呯數鎸囦护鐘舵 + lblChargeCommandStatusValue.Text = FormatChargeCommandStatusDisplay(station.ChargeCommandStatus); + lblChargeCommandStatusValue.ForeColor = station.ChargeCommandStatus == ChargeCommandStatus.Started + ? Color.FromArgb(76, 175, 80) // 缁胯壊 + : Color.Gray; + + // 鏈烘瀯鐘舵 + lblMechanismStatusValue.Text = FormatMechanismStatusDisplay(station.MechanismStatus); + switch (station.MechanismStatus) + { + + case MechanismStatus.Retracted: + lblMechanismStatusValue.ForeColor = Color.FromArgb(76, 175, 80); // 缁胯壊 + break; + case MechanismStatus.Extending: + lblMechanismStatusValue.ForeColor = Color.FromArgb(33, 150, 243); // 钃濊壊 + break; + case MechanismStatus.Extended: + lblMechanismStatusValue.ForeColor = Color.FromArgb(255, 0, 0); // 钃濊壊 + break; + default: + lblMechanismStatusValue.ForeColor = Color.Gray; + break; + } + + // 鎶ヨ淇℃伅 + if (station.HasAlarm) + { + string levelText = ""; + switch (station.AlarmLevel) + { + case AlarmLevel.Critical: + levelText = "涓ラ噸"; + lblAlarmValue.ForeColor = Color.FromArgb(183, 28, 28); // 娣辩孩鑹 + break; + case AlarmLevel.High: + levelText = "楂"; + lblAlarmValue.ForeColor = Color.FromArgb(244, 67, 54); // 绾㈣壊 + break; + case AlarmLevel.Medium: + levelText = "涓"; + lblAlarmValue.ForeColor = Color.FromArgb(255, 152, 0); // 姗欒壊 + break; + case AlarmLevel.Low: + levelText = "浣"; + lblAlarmValue.ForeColor = Color.FromArgb(255, 193, 7); // 榛勮壊 + break; + default: + levelText = "鏈煡"; + lblAlarmValue.ForeColor = Color.Gray; + break; + } + lblAlarmValue.Text = $"銆恵levelText}銆憑station.AlarmMessage}"; + } + else + { + lblAlarmValue.Text = "姝e父"; + lblAlarmValue.ForeColor = Color.FromArgb(76, 175, 80); // 缁胯壊 + } + } + + + // ==================== 浜嬩欢澶勭悊 ==================== + + /// + /// 鍏呯數鏂瑰紡鏀瑰彉浜嬩欢 - 鎺у埗"灞忚斀鏈烘瀯鐘舵佷氦浜"閫夐」鐨勬樉绀 + /// + private void cmbChargeMethod_SelectedIndexChanged(object sender, EventArgs e) + { + // 鍙湁渚у厖(Side=2)鏃舵墠鏄剧ず"灞忚斀鏈烘瀯鐘舵佷氦浜"閫夐」 + bool isSideCharge = cmbChargeMethod.SelectedIndex == (int)ChargeMethodType.Side; + chkShieldSiteMechanismStatus.Visible = isSideCharge; + + // 濡傛灉涓嶆槸渚у厖锛岃嚜鍔ㄥ彇娑堝嬀閫 + if (!isSideCharge) + { + chkShieldSiteMechanismStatus.Checked = false; + } + } + + private void btnSave_Click(object sender, EventArgs e) + { + try + { + // 濡傛灉褰撳墠鏄彧璇绘ā寮忥紙鏌ョ湅妯″紡锛夛紝鐐瑰嚮"淇敼"鎸夐挳鍒囨崲鍒扮紪杈戞ā寮 + if (btnSave.Text == "淇敼") + { + SetEditMode(true); + btnSave.Text = "淇濆瓨"; + return; + } + + // 浠ヤ笅鏄繚瀛橀昏緫 + // 鍏呯數妗╃紪鍙烽獙璇侊細1-99涔嬮棿鐨勬暟瀛 + string stationId = txtStationId.Text.Trim(); + if (string.IsNullOrEmpty(stationId)) + { + MessageBox.Show("璇疯緭鍏ュ厖鐢垫々缂栧彿锛1-99锛", "楠岃瘉澶辫触", + MessageBoxButtons.OK, MessageBoxIcon.Warning); + txtStationId.Focus(); + return; + } + + // 楠岃瘉鏄惁涓烘暟瀛椾笖鍦1-99鑼冨洿鍐 + if (!int.TryParse(stationId, out int stationNumber) || stationNumber < 1 || stationNumber > 99) + { + MessageBox.Show("鍏呯數妗╃紪鍙峰繀椤绘槸1-99涔嬮棿鐨勬暟瀛", "楠岃瘉澶辫触", + MessageBoxButtons.OK, MessageBoxIcon.Warning); + txtStationId.Focus(); + return; + } + + // 妫鏌ョ紪鍙锋槸鍚﹂噸澶嶏紙鏂板鏃讹級 + if (selectedStation == null) + { + var existingStations = dataService.GetAllStations(); + if (existingStations.Any(s => s.StationId == stationId)) + { + MessageBox.Show($"鍏呯數妗╃紪鍙 {stationId} 宸插瓨鍦紝璇疯緭鍏ュ叾浠栫紪鍙", "楠岃瘉澶辫触", + MessageBoxButtons.OK, MessageBoxIcon.Warning); + txtStationId.Focus(); + return; + } + } + + + var Site = SimpleLib.GetSite((int)numSiteId.Value); + if (Site == null) //鍒ゆ柇绔欑偣鏄惁鍦⊿閲屻 + { + MessageBox.Show($"绔欑偣ID{numSiteId.Value} 鏈湪璋冨害绯荤粺涓", "楠岃瘉澶辫触", + MessageBoxButtons.OK, MessageBoxIcon.Warning); + numSiteId.Focus(); + return; + } + + var station = CreateStationFromFields(); + string errorMessage; + + bool success; + if (selectedStation == null) + { + // 鏂板 + success = dataService.AddStation(station, out errorMessage); + } + else + { + // 鏇存柊 + success = dataService.UpdateStation(station, out errorMessage, true); + } + + if (success) + { + Site.name = station.Name; + //Site.fields["chargeNum"] = station.StationId; + //Site.fields["stationIP"] = station.IpAddress; + //Site.fields["stationPort"] = station.Port.ToString(); + //switch (station.Type) + //{ + // case ChargeStationType.FRLDTall: + // Site.fields["Charge"] = "FLChargeStation"; + // break; + // case ChargeStationType.FRLDShort: + // Site.fields["Charge"] = "PCBChargeStation"; + // Site.fields["CommunicationType"] = "UDP"; + // break; + // case ChargeStationType.MuXing: + // Site.fields["Charge"] = "MuXingChargeStation"; + // break; + // default: + // break; + //} + Site.fields["setVoltage"] = station.SetVoltage.ToString(); + Site.fields["setElectricCurrent"] = station.SetElectricCurrent.ToString(); + if (!station.Enabled) + { + Site.fields["group"] = "绂佺敤"; + } + else + { + Site.fields["group"] = station.GroupCarType.ToString(); + } + + LoadStations(); + ClearEditFields(); + MessageBox.Show("淇濆瓨鎴愬姛", "鎻愮ず", + MessageBoxButtons.OK, MessageBoxIcon.Information); + } + else + { + MessageBox.Show($"淇濆瓨澶辫触: {errorMessage}", "閿欒", + MessageBoxButtons.OK, MessageBoxIcon.Error); + } + } + catch (Exception ex) + { + MessageBox.Show($"淇濆瓨澶辫触: {ex.Message}", "閿欒", + MessageBoxButtons.OK, MessageBoxIcon.Error); + } + } + + private void btnDelete_Click(object sender, EventArgs e) + { + if (dgvStations.SelectedRows.Count == 0) + { + MessageBox.Show("璇峰厛閫夋嫨瑕佸垹闄ょ殑鍏呯數妗", "鎻愮ず", + MessageBoxButtons.OK, MessageBoxIcon.Warning); + return; + } + + var stationId = dgvStations.SelectedRows[0].Cells[0].Value.ToString(); + var stationName = dgvStations.SelectedRows[0].Cells[1].Value.ToString(); + + var result = MessageBox.Show( + $"纭畾瑕佸垹闄ゅ厖鐢垫々 [{stationId}] {stationName} 鍚楋紵", + "纭鍒犻櫎", + MessageBoxButtons.YesNo, + MessageBoxIcon.Question); + + if (result == DialogResult.Yes) + { + if (dataService.DeleteStation(stationId, out string errorMessage)) + { + + var Site = SimpleLib.GetSite((int)numSiteId.Value); + if (Site != null) //鍒ゆ柇绔欑偣鏄惁鍦⊿閲屻 + { + Site.name = "NoName"; + Site.fields.Remove("setVoltage"); + Site.fields.Remove("setElectricCurrent"); + Site.fields.Remove("Charge"); + Site.fields.Remove("group"); + + + } + MessageBox.Show("鍒犻櫎鎴愬姛锛", "鎻愮ず", + MessageBoxButtons.OK, MessageBoxIcon.Information); + LoadStations(); + ClearEditFields(); + } + else + { + MessageBox.Show($"鍒犻櫎澶辫触: {errorMessage}", "閿欒", + MessageBoxButtons.OK, MessageBoxIcon.Error); + } + + } + } + + private void btnCancel_Click(object sender, EventArgs e) + { + // 濡傛灉鏈夐変腑鐨勫厖鐢垫々锛屾仮澶嶅埌鍙妯″紡 + //if (selectedStation != null) + //{ + // LoadStationToFields(selectedStation); + //} + //else + { + // 鍚﹀垯娓呯┖涓烘柊澧炴ā寮 + ClearEditFields(); + } + } + + private void btnRefresh_Click(object sender, EventArgs e) + { + dataService.Reload(); + LoadStations(); + MessageBox.Show("鍒锋柊鎴愬姛锛", "鎻愮ず", + MessageBoxButtons.OK, MessageBoxIcon.Information); + } + + private void dgvStations_CellDoubleClick(object sender, DataGridViewCellEventArgs e) + { + if (e.RowIndex >= 0) + { + var stationId = dgvStations.Rows[e.RowIndex].Cells[0].Value.ToString(); + var station = dataService.GetStationById(stationId); + if (station != null) + { + LoadStationToFields(station); + SetEditMode(true, true); + } + } + } + + + + private void txtSearch_TextChanged(object sender, EventArgs e) + { + ApplyFilters(); + } + + private void cmbStatusFilter_SelectedIndexChanged(object sender, EventArgs e) + { + ApplyFilters(); + } + + /// + /// 搴旂敤鎼滅储鍜岀姸鎬佺瓫閫 + /// + private void ApplyFilters() + { + var stations = dataService.GetAllStations(); + + // 搴旂敤鎼滅储绛涢 + var searchText = txtSearch.Text.Trim().ToLower(); + if (!string.IsNullOrEmpty(searchText)) + { + stations = stations + .Where(s => s.StationId.ToLower().Contains(searchText) || + s.Name.ToLower().Contains(searchText) || + s.IpAddress.Contains(searchText) || + GetTypeText(s.Type).Contains(searchText)) + .ToList(); + } + + // 搴旂敤鐘舵佺瓫閫 + if (cmbStatusFilter != null && cmbStatusFilter.SelectedIndex > 0) + { + var filterStatus = GetStatusFromFilterIndex(cmbStatusFilter.SelectedIndex); + stations = stations.Where(s => s.Status == filterStatus).ToList(); + } + + // 鏄剧ず缁撴灉 + dgvStations.Rows.Clear(); + foreach (var station in stations) + { + var index = dgvStations.Rows.Add( + station.StationId, + station.Name, + GetTypeText(station.Type), + GetChargeMethodText(station.ChargeMethod), + //station.IpAddress, + //station.Port, + station.SiteId?.ToString() ?? "", + FormatTimeToMinuteSecond(station.LastSendTime), + FormatTimeToMinuteSecond(station.LastReceiveTime), + FormatCommStatusDisplay(station.CommStatus), + FormatChargeCommandStatusDisplay(station.ChargeCommandStatus), + FormatMechanismStatusDisplay(station.MechanismStatus), + string.IsNullOrWhiteSpace(station.CurrentVehicle) ? "-" : station.CurrentVehicle, + station.BatteryLevel > 0 ? $"{station.BatteryLevel:F1}%" : "-", + FormatAlarmDisplay(station), + //station.SetVoltage, + //station.SetElectricCurrent, + station.RealTimeVoltage.ToString("F1"), + station.RealTimeCurrent.ToString("F1"), + GetStatusText(station.Status), + station.Enabled ? "鏄" : "鍚", + station.Remarks + ); + + // 鏍规嵁鐘舵佽缃棰滆壊锛堟墎骞冲寲璁捐锛 + var row = dgvStations.Rows[index]; + + // 閫氳鐘舵佸垪鏍峰紡璁剧疆锛堢9鍒楋級 + var commCell = row.Cells[9]; + switch (station.CommStatus) + { + case CommunicationStatus.Normal: + commCell.Style.ForeColor = Color.FromArgb(76, 175, 80); // 缁胯壊 + // commCell.Style.Font = new Font(commCell.Style.Font ?? row.DefaultCellStyle.Font, FontStyle.Bold); + break; + case CommunicationStatus.Delayed: + commCell.Style.ForeColor = Color.FromArgb(255, 152, 0); // 姗欒壊 + // commCell.Style.Font = new Font(commCell.Style.Font ?? row.DefaultCellStyle.Font, FontStyle.Bold); + break; + case CommunicationStatus.Timeout: + case CommunicationStatus.Disconnected: + commCell.Style.ForeColor = Color.FromArgb(244, 67, 54); // 绾㈣壊 + // commCell.Style.Font = new Font(commCell.Style.Font ?? row.DefaultCellStyle.Font, FontStyle.Bold); + break; + case CommunicationStatus.Unknown: + commCell.Style.ForeColor = Color.FromArgb(158, 158, 158); // 鐏拌壊 + break; + } + + // 鍏呯數鎸囦护鐘舵佸垪鏍峰紡璁剧疆锛堢10鍒楋級 + var chargeCommandCell = row.Cells[10]; + switch (station.ChargeCommandStatus) + { + case ChargeCommandStatus.Stopped: + chargeCommandCell.Style.ForeColor = Color.FromArgb(158, 158, 158); // 鐏拌壊 + break; + case ChargeCommandStatus.Started: + chargeCommandCell.Style.ForeColor = Color.FromArgb(76, 175, 80); // 缁胯壊 + //chargeCommandCell.Style.Font = new Font(chargeCommandCell.Style.Font ?? row.DefaultCellStyle.Font, FontStyle.Bold); + break; + } + + // 鏈烘瀯鐘舵佸垪鏍峰紡璁剧疆锛堢11鍒楋級 + var mechanismCell = row.Cells[11]; + switch (station.MechanismStatus) + { + + case MechanismStatus.Retracted: + mechanismCell.Style.ForeColor = Color.FromArgb(76, 175, 80); // 缁胯壊 + // mechanismCell.Style.Font = new Font(mechanismCell.Style.Font ?? mechanismCell.Style.Font, FontStyle.Bold); + break; + case MechanismStatus.Extending: + mechanismCell.Style.ForeColor = Color.FromArgb(33, 150, 243); // 钃濊壊 + // mechanismCell.Style.Font = new Font(mechanismCell.Style.Font ?? row.DefaultCellStyle.Font, FontStyle.Bold); + break; + } + + // 鐢甸噺鍒楁牱寮忚缃紙绗13鍒楋級 + if (station.BatteryLevel > 0) + { + var batteryCell = row.Cells[13]; + if (station.BatteryLevel >= 80) + { + batteryCell.Style.ForeColor = Color.FromArgb(76, 175, 80); // 缁胯壊 - 鐢甸噺鍏呰冻 + //batteryCell.Style.Font = new Font(batteryCell.Style.Font ?? batteryCell.Style.Font, FontStyle.Bold); + } + else if (station.BatteryLevel >= 50) + { + batteryCell.Style.ForeColor = Color.FromArgb(33, 150, 243); // 钃濊壊 - 鐢甸噺涓瓑 + //batteryCell.Style.Font = new Font(batteryCell.Style.Font ?? batteryCell.Style.Font, FontStyle.Bold); + } + else if (station.BatteryLevel >= 20) + { + batteryCell.Style.ForeColor = Color.FromArgb(255, 152, 0); // 姗欒壊 - 鐢甸噺鍋忎綆 + //batteryCell.Style.Font = new Font(batteryCell.Style.Font ?? batteryCell.Style.Font, FontStyle.Bold); + } + else + { + batteryCell.Style.ForeColor = Color.FromArgb(244, 67, 54); // 绾㈣壊 - 鐢甸噺浣 + //batteryCell.Style.Font = new Font(batteryCell.Style.Font ?? batteryCell.Style.Font, FontStyle.Bold); + } + } + + // 濡傛灉鏈夋姤璀︼紝鏁磋鏄剧ず绾㈣壊 + if (station.HasAlarm) + { + row.DefaultCellStyle.BackColor = Color.FromArgb(255, 205, 210); // 娴呯孩鑹茶儗鏅 #FFCDD2 + row.DefaultCellStyle.ForeColor = Color.FromArgb(198, 40, 40); // 娣辩孩鑹叉枃瀛 + // row.DefaultCellStyle.Font = new Font(row.DefaultCellStyle.Font, FontStyle.Bold); + } + else + { + // 鏍规嵁鐘舵佽缃鑹 + switch (station.Status) + { + case ChargeStationStatus.Idle: + row.DefaultCellStyle.BackColor = Color.FromArgb(232, 245, 233); // 娴呯豢鑹 #E8F5E9 + // row.DefaultCellStyle.ForeColor = Color.FromArgb(46, 125, 50); // 娣辩豢鑹叉枃瀛 + break; + case ChargeStationStatus.Charging: + row.DefaultCellStyle.BackColor = Color.FromArgb(200, 230, 201); // 浜豢鑹 #C8E6C9 + // row.DefaultCellStyle.ForeColor = Color.FromArgb(27, 94, 32); // 娣辩豢鑹叉枃瀛 + break; + case ChargeStationStatus.Fault: + row.DefaultCellStyle.BackColor = Color.FromArgb(255, 205, 210); // 娴呯孩鑹 #FFCDD2 + // row.DefaultCellStyle.ForeColor = Color.FromArgb(198, 40, 40); // 娣辩孩鑹叉枃瀛 + break; + case ChargeStationStatus.Battery: + row.DefaultCellStyle.BackColor = Color.FromArgb(238, 238, 238); // 娴呯伆鑹 #EEEEEE + // row.DefaultCellStyle.ForeColor = Color.FromArgb(97, 97, 97); // 娣辩伆鑹叉枃瀛 + break; + } + } + } + + // 鏇存柊鏍囬鍜岀粺璁 + UpdateTitleWithFilter(stations.Count); + } + + private void btnStrategyConfig_Click(object sender, EventArgs e) + { + try + { + // 鎵撳紑鍏呯數绛栫暐閰嶇疆鐣岄潰 + var strategyConfigForm = new ChargeStrategyConfigForm(); + strategyConfigForm.ShowDialog(); + } + catch (Exception ex) + { + MessageBox.Show($"鎵撳紑绛栫暐閰嶇疆鐣岄潰澶辫触: {ex.Message}", "閿欒", + MessageBoxButtons.OK, MessageBoxIcon.Error); + } + } + + private void btnCommMonitor_Click(object sender, EventArgs e) + { + try + { + // 閫氳鐩戞帶绐楀彛浠呭厑璁稿崟瀹炰緥 + if (communicationMonitorForm == null || communicationMonitorForm.IsDisposed) + { + communicationMonitorForm = new CommunicationMonitorForm(); + communicationMonitorForm.FormClosed += (s, args) => communicationMonitorForm = null; + communicationMonitorForm.Show(); + } + else + { + if (communicationMonitorForm.WindowState == FormWindowState.Minimized) + { + communicationMonitorForm.WindowState = FormWindowState.Normal; + } + communicationMonitorForm.BringToFront(); + communicationMonitorForm.Activate(); + } + } + catch (Exception ex) + { + MessageBox.Show($"鎵撳紑閫氳鐩戞帶鐣岄潰澶辫触: {ex.Message}", "閿欒", + MessageBoxButtons.OK, MessageBoxIcon.Error); + } + } + + private void btnAlarmConfig_Click(object sender, EventArgs e) + { + try + { + // 鎵撳紑鎶ヨ閰嶇疆鐣岄潰 + var alarmConfigForm = new AlarmConfigManagementForm(); + alarmConfigForm.ShowDialog(); + } + catch (Exception ex) + { + MessageBox.Show($"鎵撳紑鎶ヨ閰嶇疆澶辫触: {ex.Message}", "閿欒", + MessageBoxButtons.OK, MessageBoxIcon.Error); + } + } + + private void btnExport_Click(object sender, EventArgs e) + { + try + { + var saveDialog = new SaveFileDialog + { + Filter = "JSON鏂囦欢|*.json|CSV鏂囦欢|*.csv", + FileName = $"ChargeStations_{DateTime.Now:yyyyMMddHHmmss}" + }; + + if (saveDialog.ShowDialog() == DialogResult.OK) + { + var stations = dataService.GetAllStations(); + if (saveDialog.FilterIndex == 1) // JSON + { + var json = Newtonsoft.Json.JsonConvert.SerializeObject(stations, Newtonsoft.Json.Formatting.Indented); + System.IO.File.WriteAllText(saveDialog.FileName, json); + } + else // CSV + { + var csv = "缂栧彿,鍚嶇О,绫诲瀷,IP鍦板潃,绔彛,鐢靛帇,鐢垫祦,鍔熺巼,鐘舵,鍚敤,鍋滈潬杞︾殑绫诲瀷,绔欑偣ID,澶囨敞\n"; + foreach (var s in stations) + { + csv += $"{s.StationId},{s.Name},{GetTypeText(s.Type)},{s.IpAddress},{s.Port},{s.SetVoltage},{s.SetElectricCurrent},{s.Power},{GetStatusText(s.Status)},{(s.Enabled ? "鏄" : "鍚")},{s.GroupCarType},{s.SiteId},{s.Remarks}\n"; + } + System.IO.File.WriteAllText(saveDialog.FileName, csv, System.Text.Encoding.UTF8); + } + + MessageBox.Show("瀵煎嚭鎴愬姛锛", "鎻愮ず", + MessageBoxButtons.OK, MessageBoxIcon.Information); + } + } + catch (Exception ex) + { + MessageBox.Show($"瀵煎嚭澶辫触: {ex.Message}", "閿欒", + MessageBoxButtons.OK, MessageBoxIcon.Error); + } + } + } +} + + + diff --git a/StandardScene.Core/Charge/ChargeStationManagementForm.resx b/StandardScene.Core/Charge/ChargeStationManagementForm.resx new file mode 100644 index 0000000..1af7de1 --- /dev/null +++ b/StandardScene.Core/Charge/ChargeStationManagementForm.resx @@ -0,0 +1,120 @@ +锘 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/StandardScene.Core/Charge/ChargeStrategyConfig.cs b/StandardScene.Core/Charge/ChargeStrategyConfig.cs new file mode 100644 index 0000000..4730dc2 --- /dev/null +++ b/StandardScene.Core/Charge/ChargeStrategyConfig.cs @@ -0,0 +1,309 @@ +using System; +using System.ComponentModel; + +namespace StandardScene.Charge +{ + /// + /// 鍏呯數绛栫暐閰嶇疆 + /// + public class ChargeStrategyConfig + { + #region SOC 鍙傛暟 + + /// + /// 蹇呭厖鐢甸噺 (%) + /// + [Description("蹇呭厖鐢甸噺")] + [DisplayName("蹇呭厖鐢甸噺(%)")] + public double MustChargeSoc { get; set; } + + /// + /// 绌洪棽鍏呯數鐢甸噺 (%) + /// + [Description("绌洪棽鍏呯數鐢甸噺")] + [DisplayName("绌洪棽鍏呯數鐢甸噺(%)")] + public double IdleChargeSoc { get; set; } + + /// + /// 浠诲姟鍙敤鐢甸噺 (%) + /// + [Description("浠诲姟鍙敤鐢甸噺")] + [DisplayName("浠诲姟鍙敤鐢甸噺(%)")] + public double TaskAvailableSoc { get; set; } + + /// + /// 婊$數鐢甸噺 (%) + /// + [Description("婊$數鐢甸噺")] + [DisplayName("婊$數鐢甸噺(%)")] + public double FullChargeSoc { get; set; } + + /// + /// 鍏佽涓柇鐢甸噺 (%) + /// + [Description("鍏佽涓柇鐢甸噺")] + [DisplayName("鍏佽涓柇鐢甸噺(%)")] + public double AllowInterruptSoc { get; set; } + + #endregion + + #region 鏃堕棿鍙傛暟 + + /// + /// 绌洪棽鍏呯數鏃堕棿 (绉) + /// + [Description("绌洪棽鍏呯數鏃堕棿")] + [DisplayName("绌洪棽鍏呯數鏃堕棿(绉)")] + public double IdleChargeSeconds { get; set; } + + /// + /// 绌洪棽鏃堕棿 (绉) + /// + [Description("绌洪棽鏃堕棿")] + [DisplayName("绌洪棽鏃堕棿(绉)")] + public double IdleSeconds { get; set; } + + /// + /// 蹇呭厖鏃堕棿 (绉) + /// + [Description("蹇呭厖鏃堕棿")] + [DisplayName("蹇呭厖鏃堕棿(绉)")] + public double MustChargeSeconds { get; set; } + + /// + /// 琛ョ數鏃堕棿 (鍒嗛挓) + /// + [Description("琛ョ數鏃堕棿")] + [DisplayName("琛ョ數鏃堕棿(鍒嗛挓)")] + public double TopUpMinutes { get; set; } + + #endregion + + #region 浠诲姟鍙傛暟 + + /// + /// 鍏佽绌洪棽杞﹀厖鐢电殑鏈灏忎换鍔℃暟 + /// + [Description("鍏佽绌洪棽杞﹀厖鐢电殑鏈灏忎换鍔℃暟")] + [DisplayName("鏈灏忎换鍔℃暟")] + public int MinAllowFreeCarToChargeTaskCnt { get; set; } + + #endregion + + #region 寮鍏冲弬鏁 + + /// + /// 鍏佽涓柇鍏呯數浠诲姟 + /// + [Description("鍏佽涓柇鍏呯數浠诲姟")] + [DisplayName("鍏佽涓柇浠诲姟")] + public bool AllowInterruptTask { get; set; } + + /// + /// 浼樺厛浣跨敤浣庣數閲忚溅杈嗗厖鐢 + /// + [Description("浼樺厛浣跨敤浣庣數閲忚溅杈嗗厖鐢")] + [DisplayName("浼樺厛浣庣數閲忓厖鐢")] + public bool UseLowerSocForCharge { get; set; } + + /// + /// 鍚敤鍏呯數閿欒妫娴 + /// + [Description("鍚敤鍏呯數閿欒妫娴")] + [DisplayName("閿欒妫娴")] + public bool EnableErrorChargeDetection { get; set; } + + /// + /// 浣跨敤鍏呯數绔欑偣绛涢 + /// + [Description("浣跨敤鍏呯數绔欑偣绛涢")] + [DisplayName("绔欑偣绛涢")] + public bool UseChargeSiteFilter { get; set; } + + #endregion + + #region 鏋勯犲嚱鏁 + + public ChargeStrategyConfig() + { + // 浣跨敤榛樿鍊煎垵濮嬪寲 + SetDefaults(); + } + + /// + /// 璁剧疆榛樿鍊 + /// + private void SetDefaults() + { + // SOC 鍙傛暟榛樿鍊 + MustChargeSoc = 20; + IdleChargeSoc = 90; + TaskAvailableSoc = 60; + FullChargeSoc = 90; + AllowInterruptSoc = 45; + + // 鏃堕棿鍙傛暟榛樿鍊 + IdleChargeSeconds = 30; + IdleSeconds = 5; + MustChargeSeconds = 60; + TopUpMinutes = 5; + + // 浠诲姟鍙傛暟榛樿鍊 + MinAllowFreeCarToChargeTaskCnt = 0; + + // 寮鍏冲弬鏁伴粯璁ゅ + AllowInterruptTask = false; + UseLowerSocForCharge = true; + EnableErrorChargeDetection = false; + UseChargeSiteFilter = false; + } + + /// + /// 鍒涘缓榛樿閰嶇疆 + /// + public static ChargeStrategyConfig CreateDefault() + { + return new ChargeStrategyConfig(); + } + + #endregion + + #region 楠岃瘉 + + /// + /// 楠岃瘉閰嶇疆鏄惁鏈夋晥 + /// + public bool Validate(out string errorMessage) + { + // 楠岃瘉 SOC 鑼冨洿 + if (MustChargeSoc < 0 || MustChargeSoc > 100) + { + errorMessage = "蹇呭厖鐢甸噺蹇呴』鍦 0-100 涔嬮棿"; + return false; + } + + if (IdleChargeSoc < 0 || IdleChargeSoc > 100) + { + errorMessage = "绌洪棽鍏呯數鐢甸噺蹇呴』鍦 0-100 涔嬮棿"; + return false; + } + + if (TaskAvailableSoc < 0 || TaskAvailableSoc > 100) + { + errorMessage = "浠诲姟鍙敤鐢甸噺蹇呴』鍦 0-100 涔嬮棿"; + return false; + } + + if (FullChargeSoc < 0 || FullChargeSoc > 100) + { + errorMessage = "婊$數鐢甸噺蹇呴』鍦 0-100 涔嬮棿"; + return false; + } + + if (AllowInterruptSoc < 0 || AllowInterruptSoc > 100) + { + errorMessage = "鍏佽涓柇鐢甸噺蹇呴』鍦 0-100 涔嬮棿"; + return false; + } + + // 楠岃瘉 SOC 閫昏緫鍏崇郴 + if (MustChargeSoc >= IdleChargeSoc) + { + errorMessage = "蹇呭厖鐢甸噺蹇呴』灏忎簬绌洪棽鍏呯數鐢甸噺"; + return false; + } + + if (TaskAvailableSoc <= MustChargeSoc) + { + errorMessage = "浠诲姟鍙敤鐢甸噺蹇呴』澶т簬蹇呭厖鐢甸噺"; + return false; + } + + if (FullChargeSoc < IdleChargeSoc) + { + errorMessage = "婊$數鐢甸噺蹇呴』澶т簬绛変簬绌洪棽鍏呯數鐢甸噺"; + return false; + } + + if (AllowInterruptSoc <= MustChargeSoc) + { + errorMessage = "鍏佽涓柇鐢甸噺蹇呴』澶т簬蹇呭厖鐢甸噺"; + return false; + } + + // 楠岃瘉鏃堕棿鍙傛暟 + if (IdleChargeSeconds < 0) + { + errorMessage = "绌洪棽鍏呯數鏃堕棿涓嶈兘涓鸿礋鏁"; + return false; + } + + if (IdleSeconds < 0) + { + errorMessage = "绌洪棽鏃堕棿涓嶈兘涓鸿礋鏁"; + return false; + } + + if (MustChargeSeconds < 0) + { + errorMessage = "蹇呭厖鏃堕棿涓嶈兘涓鸿礋鏁"; + return false; + } + + if (TopUpMinutes < 0) + { + errorMessage = "琛ョ數鏃堕棿涓嶈兘涓鸿礋鏁"; + return false; + } + + // 楠岃瘉浠诲姟鍙傛暟 + if (MinAllowFreeCarToChargeTaskCnt < 0) + { + errorMessage = "鏈灏忎换鍔℃暟涓嶈兘涓鸿礋鏁"; + return false; + } + + errorMessage = string.Empty; + return true; + } + + #endregion + + #region 杈呭姪鏂规硶 + + /// + /// 鍏嬮殕閰嶇疆 + /// + public ChargeStrategyConfig Clone() + { + return new ChargeStrategyConfig + { + MustChargeSoc = this.MustChargeSoc, + IdleChargeSoc = this.IdleChargeSoc, + TaskAvailableSoc = this.TaskAvailableSoc, + FullChargeSoc = this.FullChargeSoc, + AllowInterruptSoc = this.AllowInterruptSoc, + IdleChargeSeconds = this.IdleChargeSeconds, + IdleSeconds = this.IdleSeconds, + MustChargeSeconds = this.MustChargeSeconds, + TopUpMinutes = this.TopUpMinutes, + MinAllowFreeCarToChargeTaskCnt = this.MinAllowFreeCarToChargeTaskCnt, + AllowInterruptTask = this.AllowInterruptTask, + UseLowerSocForCharge = this.UseLowerSocForCharge, + EnableErrorChargeDetection = this.EnableErrorChargeDetection, + UseChargeSiteFilter = this.UseChargeSiteFilter + }; + } + + /// + /// 杞崲涓哄瓧绗︿覆 + /// + public override string ToString() + { + return $"鍏呯數绛栫暐閰嶇疆 [蹇呭厖:{MustChargeSoc}%, 绌洪棽鍏:{IdleChargeSoc}%, 浠诲姟鍙敤:{TaskAvailableSoc}%]"; + } + + #endregion + } +} + diff --git a/StandardScene.Core/Charge/ChargeStrategyConfigForm.Designer.cs b/StandardScene.Core/Charge/ChargeStrategyConfigForm.Designer.cs new file mode 100644 index 0000000..62ad18f --- /dev/null +++ b/StandardScene.Core/Charge/ChargeStrategyConfigForm.Designer.cs @@ -0,0 +1,572 @@ +namespace StandardScene.Charge +{ + partial class ChargeStrategyConfigForm + { + private System.ComponentModel.IContainer components = null; + + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + private void InitializeComponent() + { + // 鍒涘缓鎵鏈夋帶浠跺疄渚 + this.pnlMain = new System.Windows.Forms.Panel(); + this.pnlBottom = new System.Windows.Forms.Panel(); + this.grpSocParams = new System.Windows.Forms.GroupBox(); + this.grpTimeParams = new System.Windows.Forms.GroupBox(); + this.grpTaskParams = new System.Windows.Forms.GroupBox(); + this.grpSwitchParams = new System.Windows.Forms.GroupBox(); + + // SOC 鍙傛暟鎺т欢 + this.lblMustChargeSoc = new System.Windows.Forms.Label(); + this.numMustChargeSoc = new System.Windows.Forms.NumericUpDown(); + this.lblIdleChargeSoc = new System.Windows.Forms.Label(); + this.numIdleChargeSoc = new System.Windows.Forms.NumericUpDown(); + this.lblTaskAvailableSoc = new System.Windows.Forms.Label(); + this.numTaskAvailableSoc = new System.Windows.Forms.NumericUpDown(); + this.lblFullChargeSoc = new System.Windows.Forms.Label(); + this.numFullChargeSoc = new System.Windows.Forms.NumericUpDown(); + this.lblAllowInterruptSoc = new System.Windows.Forms.Label(); + this.numAllowInterruptSoc = new System.Windows.Forms.NumericUpDown(); + + // 鏃堕棿鍙傛暟鎺т欢 + this.lblIdleChargeSeconds = new System.Windows.Forms.Label(); + this.numIdleChargeSeconds = new System.Windows.Forms.NumericUpDown(); + this.lblIdleSeconds = new System.Windows.Forms.Label(); + this.numIdleSeconds = new System.Windows.Forms.NumericUpDown(); + this.lblMustChargeSeconds = new System.Windows.Forms.Label(); + this.numMustChargeSeconds = new System.Windows.Forms.NumericUpDown(); + this.lblTopUpMinutes = new System.Windows.Forms.Label(); + this.numTopUpMinutes = new System.Windows.Forms.NumericUpDown(); + + // 浠诲姟鍙傛暟鎺т欢 + this.lblMinAllowFreeCarToChargeTaskCnt = new System.Windows.Forms.Label(); + this.numMinAllowFreeCarToChargeTaskCnt = new System.Windows.Forms.NumericUpDown(); + + // 寮鍏冲弬鏁版帶浠 + this.chkAllowInterruptTask = new System.Windows.Forms.CheckBox(); + this.chkUseLowerSocForCharge = new System.Windows.Forms.CheckBox(); + this.chkEnableErrorChargeDetection = new System.Windows.Forms.CheckBox(); + this.chkUseChargeSiteFilter = new System.Windows.Forms.CheckBox(); + + // 搴曢儴鎺т欢 + this.lblStatus = new System.Windows.Forms.Label(); + this.btnSave = new System.Windows.Forms.Button(); + this.btnApply = new System.Windows.Forms.Button(); + this.btnRestoreDefaults = new System.Windows.Forms.Button(); + this.btnCancel = new System.Windows.Forms.Button(); + this.pnlMain.SuspendLayout(); + this.grpSwitchParams.SuspendLayout(); + this.grpTaskParams.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.numMinAllowFreeCarToChargeTaskCnt)).BeginInit(); + this.grpTimeParams.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.numTopUpMinutes)).BeginInit(); + ((System.ComponentModel.ISupportInitialize)(this.numMustChargeSeconds)).BeginInit(); + ((System.ComponentModel.ISupportInitialize)(this.numIdleSeconds)).BeginInit(); + ((System.ComponentModel.ISupportInitialize)(this.numIdleChargeSeconds)).BeginInit(); + this.grpSocParams.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.numAllowInterruptSoc)).BeginInit(); + ((System.ComponentModel.ISupportInitialize)(this.numFullChargeSoc)).BeginInit(); + ((System.ComponentModel.ISupportInitialize)(this.numTaskAvailableSoc)).BeginInit(); + ((System.ComponentModel.ISupportInitialize)(this.numIdleChargeSoc)).BeginInit(); + ((System.ComponentModel.ISupportInitialize)(this.numMustChargeSoc)).BeginInit(); + this.pnlBottom.SuspendLayout(); + this.SuspendLayout(); + // + // pnlMain + // + this.pnlMain.AutoScroll = true; + this.pnlMain.Controls.Add(this.grpSwitchParams); + this.pnlMain.Controls.Add(this.grpTaskParams); + this.pnlMain.Controls.Add(this.grpTimeParams); + this.pnlMain.Controls.Add(this.grpSocParams); + this.pnlMain.Dock = System.Windows.Forms.DockStyle.Fill; + this.pnlMain.Location = new System.Drawing.Point(0, 0); + this.pnlMain.Name = "pnlMain"; + this.pnlMain.Padding = new System.Windows.Forms.Padding(10); + this.pnlMain.Size = new System.Drawing.Size(784, 631); + this.pnlMain.TabIndex = 0; + // + // grpSwitchParams + // + this.grpSwitchParams.Controls.Add(this.chkUseChargeSiteFilter); + this.grpSwitchParams.Controls.Add(this.chkEnableErrorChargeDetection); + this.grpSwitchParams.Controls.Add(this.chkUseLowerSocForCharge); + this.grpSwitchParams.Controls.Add(this.chkAllowInterruptTask); + this.grpSwitchParams.Dock = System.Windows.Forms.DockStyle.Top; + this.grpSwitchParams.Font = new System.Drawing.Font("寰蒋闆呴粦", 9F, System.Drawing.FontStyle.Bold); + this.grpSwitchParams.Location = new System.Drawing.Point(10, 460); + this.grpSwitchParams.Name = "grpSwitchParams"; + this.grpSwitchParams.Padding = new System.Windows.Forms.Padding(10); + this.grpSwitchParams.Size = new System.Drawing.Size(764, 150); + this.grpSwitchParams.TabIndex = 3; + this.grpSwitchParams.TabStop = false; + this.grpSwitchParams.Text = "寮鍏冲弬鏁"; + // + // chkUseChargeSiteFilter + // + this.chkUseChargeSiteFilter.AutoSize = true; + this.chkUseChargeSiteFilter.Font = new System.Drawing.Font("寰蒋闆呴粦", 9F); + this.chkUseChargeSiteFilter.Location = new System.Drawing.Point(400, 80); + this.chkUseChargeSiteFilter.Name = "chkUseChargeSiteFilter"; + this.chkUseChargeSiteFilter.Size = new System.Drawing.Size(147, 24); + this.chkUseChargeSiteFilter.TabIndex = 3; + this.chkUseChargeSiteFilter.Text = "浣跨敤鍏呯數绔欑偣绛涢"; + this.chkUseChargeSiteFilter.UseVisualStyleBackColor = true; + // + // chkEnableErrorChargeDetection + // + this.chkEnableErrorChargeDetection.AutoSize = true; + this.chkEnableErrorChargeDetection.Font = new System.Drawing.Font("寰蒋闆呴粦", 9F); + this.chkEnableErrorChargeDetection.Location = new System.Drawing.Point(30, 80); + this.chkEnableErrorChargeDetection.Name = "chkEnableErrorChargeDetection"; + this.chkEnableErrorChargeDetection.Size = new System.Drawing.Size(147, 24); + this.chkEnableErrorChargeDetection.TabIndex = 2; + this.chkEnableErrorChargeDetection.Text = "鍚敤鍏呯數閿欒妫娴"; + this.chkEnableErrorChargeDetection.UseVisualStyleBackColor = true; + // + // chkUseLowerSocForCharge + // + this.chkUseLowerSocForCharge.AutoSize = true; + this.chkUseLowerSocForCharge.Font = new System.Drawing.Font("寰蒋闆呴粦", 9F); + this.chkUseLowerSocForCharge.Location = new System.Drawing.Point(400, 40); + this.chkUseLowerSocForCharge.Name = "chkUseLowerSocForCharge"; + this.chkUseLowerSocForCharge.Size = new System.Drawing.Size(195, 24); + this.chkUseLowerSocForCharge.TabIndex = 1; + this.chkUseLowerSocForCharge.Text = "浼樺厛浣跨敤浣庣數閲忚溅杈嗗厖鐢"; + this.chkUseLowerSocForCharge.UseVisualStyleBackColor = true; + // + // chkAllowInterruptTask + // + this.chkAllowInterruptTask.AutoSize = true; + this.chkAllowInterruptTask.Font = new System.Drawing.Font("寰蒋闆呴粦", 9F); + this.chkAllowInterruptTask.Location = new System.Drawing.Point(30, 40); + this.chkAllowInterruptTask.Name = "chkAllowInterruptTask"; + this.chkAllowInterruptTask.Size = new System.Drawing.Size(147, 24); + this.chkAllowInterruptTask.TabIndex = 0; + this.chkAllowInterruptTask.Text = "鍏佽涓柇鍏呯數浠诲姟"; + this.chkAllowInterruptTask.UseVisualStyleBackColor = true; + // + // grpTaskParams + // + this.grpTaskParams.Controls.Add(this.numMinAllowFreeCarToChargeTaskCnt); + this.grpTaskParams.Controls.Add(this.lblMinAllowFreeCarToChargeTaskCnt); + this.grpTaskParams.Dock = System.Windows.Forms.DockStyle.Top; + this.grpTaskParams.Font = new System.Drawing.Font("寰蒋闆呴粦", 9F, System.Drawing.FontStyle.Bold); + this.grpTaskParams.Location = new System.Drawing.Point(10, 370); + this.grpTaskParams.Name = "grpTaskParams"; + this.grpTaskParams.Padding = new System.Windows.Forms.Padding(10); + this.grpTaskParams.Size = new System.Drawing.Size(764, 90); + this.grpTaskParams.TabIndex = 2; + this.grpTaskParams.TabStop = false; + this.grpTaskParams.Text = "浠诲姟鍙傛暟"; + // + // numMinAllowFreeCarToChargeTaskCnt + // + this.numMinAllowFreeCarToChargeTaskCnt.Font = new System.Drawing.Font("寰蒋闆呴粦", 9F); + this.numMinAllowFreeCarToChargeTaskCnt.Location = new System.Drawing.Point(250, 40); + this.numMinAllowFreeCarToChargeTaskCnt.Maximum = new decimal(new int[] { + 100, + 0, + 0, + 0}); + this.numMinAllowFreeCarToChargeTaskCnt.Name = "numMinAllowFreeCarToChargeTaskCnt"; + this.numMinAllowFreeCarToChargeTaskCnt.Size = new System.Drawing.Size(120, 27); + this.numMinAllowFreeCarToChargeTaskCnt.TabIndex = 1; + // + // lblMinAllowFreeCarToChargeTaskCnt + // + this.lblMinAllowFreeCarToChargeTaskCnt.AutoSize = true; + this.lblMinAllowFreeCarToChargeTaskCnt.Font = new System.Drawing.Font("寰蒋闆呴粦", 9F); + this.lblMinAllowFreeCarToChargeTaskCnt.Location = new System.Drawing.Point(30, 42); + this.lblMinAllowFreeCarToChargeTaskCnt.Name = "lblMinAllowFreeCarToChargeTaskCnt"; + this.lblMinAllowFreeCarToChargeTaskCnt.Size = new System.Drawing.Size(207, 20); + this.lblMinAllowFreeCarToChargeTaskCnt.TabIndex = 0; + this.lblMinAllowFreeCarToChargeTaskCnt.Text = "鍏佽绌洪棽杞﹀厖鐢电殑鏈灏忎换鍔℃暟:"; + // + // grpTimeParams + // + this.grpTimeParams.Controls.Add(this.numTopUpMinutes); + this.grpTimeParams.Controls.Add(this.lblTopUpMinutes); + this.grpTimeParams.Controls.Add(this.numMustChargeSeconds); + this.grpTimeParams.Controls.Add(this.lblMustChargeSeconds); + this.grpTimeParams.Controls.Add(this.numIdleSeconds); + this.grpTimeParams.Controls.Add(this.lblIdleSeconds); + this.grpTimeParams.Controls.Add(this.numIdleChargeSeconds); + this.grpTimeParams.Controls.Add(this.lblIdleChargeSeconds); + this.grpTimeParams.Dock = System.Windows.Forms.DockStyle.Top; + this.grpTimeParams.Font = new System.Drawing.Font("寰蒋闆呴粦", 9F, System.Drawing.FontStyle.Bold); + this.grpTimeParams.Location = new System.Drawing.Point(10, 210); + this.grpTimeParams.Name = "grpTimeParams"; + this.grpTimeParams.Padding = new System.Windows.Forms.Padding(10); + this.grpTimeParams.Size = new System.Drawing.Size(764, 160); + this.grpTimeParams.TabIndex = 1; + this.grpTimeParams.TabStop = false; + this.grpTimeParams.Text = "鏃堕棿鍙傛暟"; + // + // numTopUpMinutes + // + this.numTopUpMinutes.DecimalPlaces = 1; + this.numTopUpMinutes.Font = new System.Drawing.Font("寰蒋闆呴粦", 9F); + this.numTopUpMinutes.Location = new System.Drawing.Point(580, 100); + this.numTopUpMinutes.Maximum = new decimal(new int[] { + 1000, + 0, + 0, + 0}); + this.numTopUpMinutes.Name = "numTopUpMinutes"; + this.numTopUpMinutes.Size = new System.Drawing.Size(120, 27); + this.numTopUpMinutes.TabIndex = 7; + // + // lblTopUpMinutes + // + this.lblTopUpMinutes.AutoSize = true; + this.lblTopUpMinutes.Font = new System.Drawing.Font("寰蒋闆呴粦", 9F); + this.lblTopUpMinutes.Location = new System.Drawing.Point(400, 102); + this.lblTopUpMinutes.Name = "lblTopUpMinutes"; + this.lblTopUpMinutes.Size = new System.Drawing.Size(159, 20); + this.lblTopUpMinutes.TabIndex = 6; + this.lblTopUpMinutes.Text = "琛ョ數鏃堕棿 (鍒嗛挓锛宮in):"; + // + // numMustChargeSeconds + // + this.numMustChargeSeconds.DecimalPlaces = 1; + this.numMustChargeSeconds.Font = new System.Drawing.Font("寰蒋闆呴粦", 9F); + this.numMustChargeSeconds.Location = new System.Drawing.Point(250, 100); + this.numMustChargeSeconds.Maximum = new decimal(new int[] { + 10000, + 0, + 0, + 0}); + this.numMustChargeSeconds.Name = "numMustChargeSeconds"; + this.numMustChargeSeconds.Size = new System.Drawing.Size(120, 27); + this.numMustChargeSeconds.TabIndex = 5; + // + // lblMustChargeSeconds + // + this.lblMustChargeSeconds.AutoSize = true; + this.lblMustChargeSeconds.Font = new System.Drawing.Font("寰蒋闆呴粦", 9F); + this.lblMustChargeSeconds.Location = new System.Drawing.Point(30, 102); + this.lblMustChargeSeconds.Name = "lblMustChargeSeconds"; + this.lblMustChargeSeconds.Size = new System.Drawing.Size(147, 20); + this.lblMustChargeSeconds.TabIndex = 4; + this.lblMustChargeSeconds.Text = "蹇呭厖鏃堕棿 (绉掞紝sec):"; + // + // numIdleSeconds + // + this.numIdleSeconds.DecimalPlaces = 1; + this.numIdleSeconds.Font = new System.Drawing.Font("寰蒋闆呴粦", 9F); + this.numIdleSeconds.Location = new System.Drawing.Point(580, 40); + this.numIdleSeconds.Maximum = new decimal(new int[] { + 10000, + 0, + 0, + 0}); + this.numIdleSeconds.Name = "numIdleSeconds"; + this.numIdleSeconds.Size = new System.Drawing.Size(120, 27); + this.numIdleSeconds.TabIndex = 3; + // + // lblIdleSeconds + // + this.lblIdleSeconds.AutoSize = true; + this.lblIdleSeconds.Font = new System.Drawing.Font("寰蒋闆呴粦", 9F); + this.lblIdleSeconds.Location = new System.Drawing.Point(400, 42); + this.lblIdleSeconds.Name = "lblIdleSeconds"; + this.lblIdleSeconds.Size = new System.Drawing.Size(147, 20); + this.lblIdleSeconds.TabIndex = 2; + this.lblIdleSeconds.Text = "绌洪棽鏃堕棿 (绉掞紝sec):"; + // + // numIdleChargeSeconds + // + this.numIdleChargeSeconds.DecimalPlaces = 1; + this.numIdleChargeSeconds.Font = new System.Drawing.Font("寰蒋闆呴粦", 9F); + this.numIdleChargeSeconds.Location = new System.Drawing.Point(250, 40); + this.numIdleChargeSeconds.Maximum = new decimal(new int[] { + 10000, + 0, + 0, + 0}); + this.numIdleChargeSeconds.Name = "numIdleChargeSeconds"; + this.numIdleChargeSeconds.Size = new System.Drawing.Size(120, 27); + this.numIdleChargeSeconds.TabIndex = 1; + // + // lblIdleChargeSeconds + // + this.lblIdleChargeSeconds.AutoSize = true; + this.lblIdleChargeSeconds.Font = new System.Drawing.Font("寰蒋闆呴粦", 9F); + this.lblIdleChargeSeconds.Location = new System.Drawing.Point(30, 42); + this.lblIdleChargeSeconds.Name = "lblIdleChargeSeconds"; + this.lblIdleChargeSeconds.Size = new System.Drawing.Size(171, 20); + this.lblIdleChargeSeconds.TabIndex = 0; + this.lblIdleChargeSeconds.Text = "绌洪棽鍏呯數鏃堕棿 (绉掞紝sec):"; + // + // grpSocParams + // + this.grpSocParams.Controls.Add(this.numAllowInterruptSoc); + this.grpSocParams.Controls.Add(this.lblAllowInterruptSoc); + this.grpSocParams.Controls.Add(this.numFullChargeSoc); + this.grpSocParams.Controls.Add(this.lblFullChargeSoc); + this.grpSocParams.Controls.Add(this.numTaskAvailableSoc); + this.grpSocParams.Controls.Add(this.lblTaskAvailableSoc); + this.grpSocParams.Controls.Add(this.numIdleChargeSoc); + this.grpSocParams.Controls.Add(this.lblIdleChargeSoc); + this.grpSocParams.Controls.Add(this.numMustChargeSoc); + this.grpSocParams.Controls.Add(this.lblMustChargeSoc); + this.grpSocParams.Dock = System.Windows.Forms.DockStyle.Top; + this.grpSocParams.Font = new System.Drawing.Font("寰蒋闆呴粦", 9F, System.Drawing.FontStyle.Bold); + this.grpSocParams.Location = new System.Drawing.Point(10, 10); + this.grpSocParams.Name = "grpSocParams"; + this.grpSocParams.Padding = new System.Windows.Forms.Padding(10); + this.grpSocParams.Size = new System.Drawing.Size(764, 200); + this.grpSocParams.TabIndex = 0; + this.grpSocParams.TabStop = false; + this.grpSocParams.Text = "SOC 鍙傛暟 (鐢甸噺鐧惧垎姣)"; + // + // numAllowInterruptSoc + // + this.numAllowInterruptSoc.DecimalPlaces = 1; + this.numAllowInterruptSoc.Font = new System.Drawing.Font("寰蒋闆呴粦", 9F); + this.numAllowInterruptSoc.Location = new System.Drawing.Point(250, 150); + this.numAllowInterruptSoc.Name = "numAllowInterruptSoc"; + this.numAllowInterruptSoc.Size = new System.Drawing.Size(120, 27); + this.numAllowInterruptSoc.TabIndex = 9; + // + // lblAllowInterruptSoc + // + this.lblAllowInterruptSoc.AutoSize = true; + this.lblAllowInterruptSoc.Font = new System.Drawing.Font("寰蒋闆呴粦", 9F); + this.lblAllowInterruptSoc.Location = new System.Drawing.Point(30, 152); + this.lblAllowInterruptSoc.Name = "lblAllowInterruptSoc"; + this.lblAllowInterruptSoc.Size = new System.Drawing.Size(135, 20); + this.lblAllowInterruptSoc.TabIndex = 8; + this.lblAllowInterruptSoc.Text = "鍏佽涓柇鐢甸噺 (%):"; + // + // numFullChargeSoc + // + this.numFullChargeSoc.DecimalPlaces = 1; + this.numFullChargeSoc.Font = new System.Drawing.Font("寰蒋闆呴粦", 9F); + this.numFullChargeSoc.Location = new System.Drawing.Point(580, 95); + this.numFullChargeSoc.Name = "numFullChargeSoc"; + this.numFullChargeSoc.Size = new System.Drawing.Size(120, 27); + this.numFullChargeSoc.TabIndex = 7; + // + // lblFullChargeSoc + // + this.lblFullChargeSoc.AutoSize = true; + this.lblFullChargeSoc.Font = new System.Drawing.Font("寰蒋闆呴粦", 9F); + this.lblFullChargeSoc.Location = new System.Drawing.Point(400, 97); + this.lblFullChargeSoc.Name = "lblFullChargeSoc"; + this.lblFullChargeSoc.Size = new System.Drawing.Size(99, 20); + this.lblFullChargeSoc.TabIndex = 6; + this.lblFullChargeSoc.Text = "婊$數鐢甸噺 (%):"; + // + // numTaskAvailableSoc + // + this.numTaskAvailableSoc.DecimalPlaces = 1; + this.numTaskAvailableSoc.Font = new System.Drawing.Font("寰蒋闆呴粦", 9F); + this.numTaskAvailableSoc.Location = new System.Drawing.Point(250, 95); + this.numTaskAvailableSoc.Name = "numTaskAvailableSoc"; + this.numTaskAvailableSoc.Size = new System.Drawing.Size(120, 27); + this.numTaskAvailableSoc.TabIndex = 5; + // + // lblTaskAvailableSoc + // + this.lblTaskAvailableSoc.AutoSize = true; + this.lblTaskAvailableSoc.Font = new System.Drawing.Font("寰蒋闆呴粦", 9F); + this.lblTaskAvailableSoc.Location = new System.Drawing.Point(30, 97); + this.lblTaskAvailableSoc.Name = "lblTaskAvailableSoc"; + this.lblTaskAvailableSoc.Size = new System.Drawing.Size(135, 20); + this.lblTaskAvailableSoc.TabIndex = 4; + this.lblTaskAvailableSoc.Text = "浠诲姟鍙敤鐢甸噺 (%):"; + // + // numIdleChargeSoc + // + this.numIdleChargeSoc.DecimalPlaces = 1; + this.numIdleChargeSoc.Font = new System.Drawing.Font("寰蒋闆呴粦", 9F); + this.numIdleChargeSoc.Location = new System.Drawing.Point(580, 40); + this.numIdleChargeSoc.Name = "numIdleChargeSoc"; + this.numIdleChargeSoc.Size = new System.Drawing.Size(120, 27); + this.numIdleChargeSoc.TabIndex = 3; + // + // lblIdleChargeSoc + // + this.lblIdleChargeSoc.AutoSize = true; + this.lblIdleChargeSoc.Font = new System.Drawing.Font("寰蒋闆呴粦", 9F); + this.lblIdleChargeSoc.Location = new System.Drawing.Point(400, 42); + this.lblIdleChargeSoc.Name = "lblIdleChargeSoc"; + this.lblIdleChargeSoc.Size = new System.Drawing.Size(135, 20); + this.lblIdleChargeSoc.TabIndex = 2; + this.lblIdleChargeSoc.Text = "绌洪棽鍏呯數鐢甸噺 (%):"; + // + // numMustChargeSoc + // + this.numMustChargeSoc.DecimalPlaces = 1; + this.numMustChargeSoc.Font = new System.Drawing.Font("寰蒋闆呴粦", 9F); + this.numMustChargeSoc.Location = new System.Drawing.Point(250, 40); + this.numMustChargeSoc.Name = "numMustChargeSoc"; + this.numMustChargeSoc.Size = new System.Drawing.Size(120, 27); + this.numMustChargeSoc.TabIndex = 1; + // + // lblMustChargeSoc + // + this.lblMustChargeSoc.AutoSize = true; + this.lblMustChargeSoc.Font = new System.Drawing.Font("寰蒋闆呴粦", 9F); + this.lblMustChargeSoc.Location = new System.Drawing.Point(30, 42); + this.lblMustChargeSoc.Name = "lblMustChargeSoc"; + this.lblMustChargeSoc.Size = new System.Drawing.Size(99, 20); + this.lblMustChargeSoc.TabIndex = 0; + this.lblMustChargeSoc.Text = "蹇呭厖鐢甸噺 (%):"; + // + // pnlBottom + // + this.pnlBottom.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(250)))), ((int)(((byte)(250)))), ((int)(((byte)(250))))); + this.pnlBottom.Controls.Add(this.lblStatus); + this.pnlBottom.Controls.Add(this.btnApply); + this.pnlBottom.Controls.Add(this.btnRestoreDefaults); + this.pnlBottom.Controls.Add(this.btnCancel); + this.pnlBottom.Controls.Add(this.btnSave); + this.pnlBottom.Dock = System.Windows.Forms.DockStyle.Bottom; + this.pnlBottom.Location = new System.Drawing.Point(0, 631); + this.pnlBottom.Name = "pnlBottom"; + this.pnlBottom.Size = new System.Drawing.Size(784, 70); + this.pnlBottom.TabIndex = 1; + // + // lblStatus + // + this.lblStatus.AutoSize = true; + this.lblStatus.Font = new System.Drawing.Font("寰蒋闆呴粦", 9F); + this.lblStatus.Location = new System.Drawing.Point(20, 25); + this.lblStatus.Name = "lblStatus"; + this.lblStatus.Size = new System.Drawing.Size(54, 20); + this.lblStatus.TabIndex = 4; + this.lblStatus.Text = "灏辩华..."; + // + // btnApply + // + this.btnApply.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); + this.btnApply.Font = new System.Drawing.Font("寰蒋闆呴粦", 9F); + this.btnApply.Location = new System.Drawing.Point(564, 18); + this.btnApply.Name = "btnApply"; + this.btnApply.Size = new System.Drawing.Size(100, 35); + this.btnApply.TabIndex = 3; + this.btnApply.Text = "搴旂敤"; + this.btnApply.UseVisualStyleBackColor = true; + this.btnApply.Click += new System.EventHandler(this.btnApply_Click); + // + // btnRestoreDefaults + // + this.btnRestoreDefaults.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); + this.btnRestoreDefaults.Font = new System.Drawing.Font("寰蒋闆呴粦", 9F); + this.btnRestoreDefaults.Location = new System.Drawing.Point(344, 18); + this.btnRestoreDefaults.Name = "btnRestoreDefaults"; + this.btnRestoreDefaults.Size = new System.Drawing.Size(100, 35); + this.btnRestoreDefaults.TabIndex = 2; + this.btnRestoreDefaults.Text = "鎭㈠榛樿"; + this.btnRestoreDefaults.UseVisualStyleBackColor = true; + this.btnRestoreDefaults.Click += new System.EventHandler(this.btnRestoreDefaults_Click); + // + // btnCancel + // + this.btnCancel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); + this.btnCancel.Font = new System.Drawing.Font("寰蒋闆呴粦", 9F); + this.btnCancel.Location = new System.Drawing.Point(674, 18); + this.btnCancel.Name = "btnCancel"; + this.btnCancel.Size = new System.Drawing.Size(100, 35); + this.btnCancel.TabIndex = 1; + this.btnCancel.Text = "鍙栨秷"; + this.btnCancel.UseVisualStyleBackColor = true; + this.btnCancel.Click += new System.EventHandler(this.btnCancel_Click); + // + // btnSave + // + this.btnSave.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); + this.btnSave.Font = new System.Drawing.Font("寰蒋闆呴粦", 9F); + this.btnSave.Location = new System.Drawing.Point(454, 18); + this.btnSave.Name = "btnSave"; + this.btnSave.Size = new System.Drawing.Size(100, 35); + this.btnSave.TabIndex = 0; + this.btnSave.Text = "淇濆瓨"; + this.btnSave.UseVisualStyleBackColor = true; + this.btnSave.Click += new System.EventHandler(this.btnSave_Click); + // + // ChargeStrategyConfigForm + // + this.AutoScaleDimensions = new System.Drawing.SizeF(8F, 15F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(784, 701); + this.Controls.Add(this.pnlMain); + this.Controls.Add(this.pnlBottom); + this.Name = "ChargeStrategyConfigForm"; + this.Text = "鍏呯數绛栫暐閰嶇疆"; + this.pnlMain.ResumeLayout(false); + this.grpSwitchParams.ResumeLayout(false); + this.grpSwitchParams.PerformLayout(); + this.grpTaskParams.ResumeLayout(false); + this.grpTaskParams.PerformLayout(); + ((System.ComponentModel.ISupportInitialize)(this.numMinAllowFreeCarToChargeTaskCnt)).EndInit(); + this.grpTimeParams.ResumeLayout(false); + this.grpTimeParams.PerformLayout(); + ((System.ComponentModel.ISupportInitialize)(this.numTopUpMinutes)).EndInit(); + ((System.ComponentModel.ISupportInitialize)(this.numMustChargeSeconds)).EndInit(); + ((System.ComponentModel.ISupportInitialize)(this.numIdleSeconds)).EndInit(); + ((System.ComponentModel.ISupportInitialize)(this.numIdleChargeSeconds)).EndInit(); + this.grpSocParams.ResumeLayout(false); + this.grpSocParams.PerformLayout(); + ((System.ComponentModel.ISupportInitialize)(this.numAllowInterruptSoc)).EndInit(); + ((System.ComponentModel.ISupportInitialize)(this.numFullChargeSoc)).EndInit(); + ((System.ComponentModel.ISupportInitialize)(this.numTaskAvailableSoc)).EndInit(); + ((System.ComponentModel.ISupportInitialize)(this.numIdleChargeSoc)).EndInit(); + ((System.ComponentModel.ISupportInitialize)(this.numMustChargeSoc)).EndInit(); + this.pnlBottom.ResumeLayout(false); + this.pnlBottom.PerformLayout(); + this.ResumeLayout(false); + + } + + #endregion + + private System.Windows.Forms.Panel pnlMain; + private System.Windows.Forms.GroupBox grpSocParams; + private System.Windows.Forms.NumericUpDown numMustChargeSoc; + private System.Windows.Forms.Label lblMustChargeSoc; + private System.Windows.Forms.NumericUpDown numIdleChargeSoc; + private System.Windows.Forms.Label lblIdleChargeSoc; + private System.Windows.Forms.NumericUpDown numTaskAvailableSoc; + private System.Windows.Forms.Label lblTaskAvailableSoc; + private System.Windows.Forms.NumericUpDown numFullChargeSoc; + private System.Windows.Forms.Label lblFullChargeSoc; + private System.Windows.Forms.NumericUpDown numAllowInterruptSoc; + private System.Windows.Forms.Label lblAllowInterruptSoc; + private System.Windows.Forms.GroupBox grpTimeParams; + private System.Windows.Forms.NumericUpDown numIdleChargeSeconds; + private System.Windows.Forms.Label lblIdleChargeSeconds; + private System.Windows.Forms.NumericUpDown numIdleSeconds; + private System.Windows.Forms.Label lblIdleSeconds; + private System.Windows.Forms.NumericUpDown numMustChargeSeconds; + private System.Windows.Forms.Label lblMustChargeSeconds; + private System.Windows.Forms.NumericUpDown numTopUpMinutes; + private System.Windows.Forms.Label lblTopUpMinutes; + private System.Windows.Forms.GroupBox grpTaskParams; + private System.Windows.Forms.NumericUpDown numMinAllowFreeCarToChargeTaskCnt; + private System.Windows.Forms.Label lblMinAllowFreeCarToChargeTaskCnt; + private System.Windows.Forms.GroupBox grpSwitchParams; + private System.Windows.Forms.CheckBox chkAllowInterruptTask; + private System.Windows.Forms.CheckBox chkUseLowerSocForCharge; + private System.Windows.Forms.CheckBox chkEnableErrorChargeDetection; + private System.Windows.Forms.CheckBox chkUseChargeSiteFilter; + private System.Windows.Forms.Panel pnlBottom; + private System.Windows.Forms.Button btnSave; + private System.Windows.Forms.Button btnCancel; + private System.Windows.Forms.Button btnRestoreDefaults; + private System.Windows.Forms.Button btnApply; + private System.Windows.Forms.Label lblStatus; + } +} diff --git a/StandardScene.Core/Charge/ChargeStrategyConfigForm.cs b/StandardScene.Core/Charge/ChargeStrategyConfigForm.cs new file mode 100644 index 0000000..53f84a7 --- /dev/null +++ b/StandardScene.Core/Charge/ChargeStrategyConfigForm.cs @@ -0,0 +1,215 @@ +using System; +using System.Drawing; +using System.Windows.Forms; + +namespace StandardScene.Charge +{ + /// + /// 鍏呯數绛栫暐閰嶇疆绐椾綋 + /// + public partial class ChargeStrategyConfigForm : Form + { + private ChargeStrategyConfig config; + private ChargeStrategyConfigService configService; + + public ChargeStrategyConfigForm() + { + InitializeComponent(); + configService = ChargeStrategyConfigService.Instance; + InitializeForm(); + } + + private void InitializeForm() + { + this.Text = "鍏呯數绛栫暐閰嶇疆"; + this.Size = new Size(800, 700); + this.StartPosition = FormStartPosition.CenterScreen; + this.MinimumSize = new Size(700, 600); + this.FormBorderStyle = FormBorderStyle.FixedDialog; + this.MaximizeBox = false; + + // 鍔犺浇閰嶇疆 + LoadConfig(); + } + + /// + /// 鍔犺浇閰嶇疆鍒扮晫闈 + /// + private void LoadConfig(bool isDef = false) + { + try + { + if (!isDef) + { + config = configService.LoadConfig(); + } + + + // SOC 鐩稿叧鍙傛暟 + numMustChargeSoc.Value = (decimal)config.MustChargeSoc; + numIdleChargeSoc.Value = (decimal)config.IdleChargeSoc; + numTaskAvailableSoc.Value = (decimal)config.TaskAvailableSoc; + numFullChargeSoc.Value = (decimal)config.FullChargeSoc; + numAllowInterruptSoc.Value = (decimal)config.AllowInterruptSoc; + + // 鏃堕棿鐩稿叧鍙傛暟 + numIdleChargeSeconds.Value = (decimal)config.IdleChargeSeconds; + numIdleSeconds.Value = (decimal)config.IdleSeconds; + numMustChargeSeconds.Value = (decimal)config.MustChargeSeconds; + numTopUpMinutes.Value = (decimal)config.TopUpMinutes; + + // 浠诲姟鐩稿叧鍙傛暟 + numMinAllowFreeCarToChargeTaskCnt.Value = config.MinAllowFreeCarToChargeTaskCnt; + + // 寮鍏冲弬鏁 + chkAllowInterruptTask.Checked = config.AllowInterruptTask; + chkUseLowerSocForCharge.Checked = config.UseLowerSocForCharge; + chkEnableErrorChargeDetection.Checked = config.EnableErrorChargeDetection; + chkUseChargeSiteFilter.Checked = config.UseChargeSiteFilter; + + lblStatus.Text = "閰嶇疆鍔犺浇鎴愬姛"; + lblStatus.ForeColor = Color.Green; + } + catch (Exception ex) + { + MessageBox.Show($"鍔犺浇閰嶇疆澶辫触: {ex.Message}", "閿欒", + MessageBoxButtons.OK, MessageBoxIcon.Error); + lblStatus.Text = "閰嶇疆鍔犺浇澶辫触"; + lblStatus.ForeColor = Color.Red; + } + } + + /// + /// 浠庣晫闈繚瀛橀厤缃 + /// + private void SaveConfig() + { + try + { + // SOC 鐩稿叧鍙傛暟 + config.MustChargeSoc = (double)numMustChargeSoc.Value; + config.IdleChargeSoc = (double)numIdleChargeSoc.Value; + config.TaskAvailableSoc = (double)numTaskAvailableSoc.Value; + config.FullChargeSoc = (double)numFullChargeSoc.Value; + config.AllowInterruptSoc = (double)numAllowInterruptSoc.Value; + + // 鏃堕棿鐩稿叧鍙傛暟 + config.IdleChargeSeconds = (double)numIdleChargeSeconds.Value; + config.IdleSeconds = (double)numIdleSeconds.Value; + config.MustChargeSeconds = (double)numMustChargeSeconds.Value; + config.TopUpMinutes = (double)numTopUpMinutes.Value; + + // 浠诲姟鐩稿叧鍙傛暟 + config.MinAllowFreeCarToChargeTaskCnt = (int)numMinAllowFreeCarToChargeTaskCnt.Value; + + // 寮鍏冲弬鏁 + config.AllowInterruptTask = chkAllowInterruptTask.Checked; + config.UseLowerSocForCharge = chkUseLowerSocForCharge.Checked; + config.EnableErrorChargeDetection = chkEnableErrorChargeDetection.Checked; + config.UseChargeSiteFilter = chkUseChargeSiteFilter.Checked; + + // 淇濆瓨鍒版枃浠 + configService.SaveConfig(config); + + lblStatus.Text = "閰嶇疆淇濆瓨鎴愬姛"; + lblStatus.ForeColor = Color.Green; + + MessageBox.Show("鍏呯數绛栫暐閰嶇疆淇濆瓨鎴愬姛锛", "鎴愬姛", + MessageBoxButtons.OK, MessageBoxIcon.Information); + } + catch (Exception ex) + { + MessageBox.Show($"淇濆瓨閰嶇疆澶辫触: {ex.Message}", "閿欒", + MessageBoxButtons.OK, MessageBoxIcon.Error); + lblStatus.Text = "閰嶇疆淇濆瓨澶辫触"; + lblStatus.ForeColor = Color.Red; + } + } + + /// + /// 鎭㈠榛樿閰嶇疆 + /// + private void RestoreDefaults() + { + var result = MessageBox.Show( + "纭畾瑕佹仮澶嶉粯璁ら厤缃悧锛熷綋鍓嶉厤缃皢琚鐩栥", + "纭鎭㈠", + MessageBoxButtons.YesNo, + MessageBoxIcon.Question); + + if (result == DialogResult.Yes) + { + config = ChargeStrategyConfig.CreateDefault(); + LoadConfig(true); + lblStatus.Text = "宸叉仮澶嶉粯璁ら厤缃紙鏈繚瀛橈級"; + lblStatus.ForeColor = Color.Blue; + } + } + + /// + /// 楠岃瘉閰嶇疆鍙傛暟 + /// + private bool ValidateConfig() + { + // 楠岃瘉 SOC 鑼冨洿 + if (numMustChargeSoc.Value >= numIdleChargeSoc.Value) + { + MessageBox.Show("蹇呭厖鐢甸噺蹇呴』灏忎簬绌洪棽鍏呯數鐢甸噺", "楠岃瘉澶辫触", + MessageBoxButtons.OK, MessageBoxIcon.Warning); + return false; + } + + if (numTaskAvailableSoc.Value <= numMustChargeSoc.Value) + { + MessageBox.Show("浠诲姟鍙敤鐢甸噺蹇呴』澶т簬蹇呭厖鐢甸噺", "楠岃瘉澶辫触", + MessageBoxButtons.OK, MessageBoxIcon.Warning); + return false; + } + + if (numFullChargeSoc.Value < numIdleChargeSoc.Value) + { + MessageBox.Show("婊$數鐢甸噺蹇呴』澶т簬绛変簬绌洪棽鍏呯數鐢甸噺", "楠岃瘉澶辫触", + MessageBoxButtons.OK, MessageBoxIcon.Warning); + return false; + } + + if (numAllowInterruptSoc.Value <= numMustChargeSoc.Value) + { + MessageBox.Show("鍏佽涓柇鐢甸噺蹇呴』澶т簬蹇呭厖鐢甸噺", "楠岃瘉澶辫触", + MessageBoxButtons.OK, MessageBoxIcon.Warning); + return false; + } + + return true; + } + + // ==================== 浜嬩欢澶勭悊 ==================== + + private void btnSave_Click(object sender, EventArgs e) + { + if (ValidateConfig()) + { + SaveConfig(); + } + } + + private void btnCancel_Click(object sender, EventArgs e) + { + this.Close(); + } + + private void btnRestoreDefaults_Click(object sender, EventArgs e) + { + RestoreDefaults(); + } + + private void btnApply_Click(object sender, EventArgs e) + { + if (ValidateConfig()) + { + SaveConfig(); + } + } + } +} + diff --git a/StandardScene.Core/Charge/ChargeStrategyConfigService.cs b/StandardScene.Core/Charge/ChargeStrategyConfigService.cs new file mode 100644 index 0000000..aa975cc --- /dev/null +++ b/StandardScene.Core/Charge/ChargeStrategyConfigService.cs @@ -0,0 +1,203 @@ +using System; +using System.IO; +using Newtonsoft.Json; + +namespace StandardScene.Charge +{ + /// + /// 鍏呯數绛栫暐閰嶇疆鏈嶅姟锛堝崟渚嬫ā寮忥級 + /// + public class ChargeStrategyConfigService + { + private static ChargeStrategyConfigService _instance; + private static readonly object _lock = new object(); + private readonly string configFilePath; + private const string ConfigFileName = "ChargeStrategyConfig.json"; + + /// + /// 鑾峰彇鍗曚緥瀹炰緥 + /// + public static ChargeStrategyConfigService Instance + { + get + { + if (_instance == null) + { + lock (_lock) + { + if (_instance == null) + { + _instance = new ChargeStrategyConfigService(); + } + } + } + return _instance; + } + } + + private ChargeStrategyConfigService() + { + // 閰嶇疆鏂囦欢淇濆瓨鍦ㄥ簲鐢ㄧ▼搴忕洰褰曚笅鐨 Config 鏂囦欢澶 + string configDir = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Config"); + + // 纭繚鐩綍瀛樺湪 + if (!Directory.Exists(configDir)) + { + Directory.CreateDirectory(configDir); + } + + configFilePath = Path.Combine(configDir, ConfigFileName); + } + + /// + /// 鍔犺浇閰嶇疆 + /// + public ChargeStrategyConfig LoadConfig() + { + try + { + if (File.Exists(configFilePath)) + { + string json = File.ReadAllText(configFilePath); + var config = JsonConvert.DeserializeObject(json); + + // 楠岃瘉閰嶇疆 + if (config.Validate(out string errorMessage)) + { + return config; + } + else + { + // 閰嶇疆鏃犳晥锛岃繑鍥為粯璁ら厤缃 + System.Diagnostics.Debug.WriteLine($"閰嶇疆楠岃瘉澶辫触: {errorMessage}锛屼娇鐢ㄩ粯璁ら厤缃"); + return ChargeStrategyConfig.CreateDefault(); + } + } + else + { + // 鏂囦欢涓嶅瓨鍦紝鍒涘缓榛樿閰嶇疆骞朵繚瀛 + var defaultConfig = ChargeStrategyConfig.CreateDefault(); + SaveConfig(defaultConfig); + return defaultConfig; + } + } + catch (Exception ex) + { + System.Diagnostics.Debug.WriteLine($"鍔犺浇閰嶇疆澶辫触: {ex.Message}"); + // 鍔犺浇澶辫触锛岃繑鍥為粯璁ら厤缃 + return ChargeStrategyConfig.CreateDefault(); + } + } + + /// + /// 淇濆瓨閰嶇疆 + /// + public void SaveConfig(ChargeStrategyConfig config) + { + try + { + // 楠岃瘉閰嶇疆 + if (!config.Validate(out string errorMessage)) + { + throw new InvalidOperationException($"閰嶇疆楠岃瘉澶辫触: {errorMessage}"); + } + + // 搴忓垪鍖栦负 JSON + string json = JsonConvert.SerializeObject(config, Formatting.Indented); + + // 淇濆瓨鍒版枃浠 + File.WriteAllText(configFilePath, json); + + System.Diagnostics.Debug.WriteLine($"閰嶇疆淇濆瓨鎴愬姛: {configFilePath}"); + } + catch (Exception ex) + { + System.Diagnostics.Debug.WriteLine($"淇濆瓨閰嶇疆澶辫触: {ex.Message}"); + throw new Exception($"淇濆瓨閰嶇疆澶辫触: {ex.Message}", ex); + } + } + + /// + /// 鑾峰彇閰嶇疆鏂囦欢璺緞 + /// + public string GetConfigFilePath() + { + return configFilePath; + } + + /// + /// 妫鏌ラ厤缃枃浠舵槸鍚﹀瓨鍦 + /// + public bool ConfigFileExists() + { + return File.Exists(configFilePath); + } + + /// + /// 鍒犻櫎閰嶇疆鏂囦欢 + /// + public void DeleteConfig() + { + try + { + if (File.Exists(configFilePath)) + { + File.Delete(configFilePath); + System.Diagnostics.Debug.WriteLine($"閰嶇疆鏂囦欢宸插垹闄: {configFilePath}"); + } + } + catch (Exception ex) + { + System.Diagnostics.Debug.WriteLine($"鍒犻櫎閰嶇疆鏂囦欢澶辫触: {ex.Message}"); + throw new Exception($"鍒犻櫎閰嶇疆鏂囦欢澶辫触: {ex.Message}", ex); + } + } + + /// + /// 瀵煎嚭閰嶇疆鍒版寚瀹氳矾寰 + /// + public void ExportConfig(string exportPath, ChargeStrategyConfig config) + { + try + { + string json = JsonConvert.SerializeObject(config, Formatting.Indented); + File.WriteAllText(exportPath, json); + System.Diagnostics.Debug.WriteLine($"閰嶇疆瀵煎嚭鎴愬姛: {exportPath}"); + } + catch (Exception ex) + { + throw new Exception($"瀵煎嚭閰嶇疆澶辫触: {ex.Message}", ex); + } + } + + /// + /// 浠庢寚瀹氳矾寰勫鍏ラ厤缃 + /// + public ChargeStrategyConfig ImportConfig(string importPath) + { + try + { + if (!File.Exists(importPath)) + { + throw new FileNotFoundException($"閰嶇疆鏂囦欢涓嶅瓨鍦: {importPath}"); + } + + string json = File.ReadAllText(importPath); + var config = JsonConvert.DeserializeObject(json); + + // 楠岃瘉閰嶇疆 + if (!config.Validate(out string errorMessage)) + { + throw new InvalidOperationException($"閰嶇疆楠岃瘉澶辫触: {errorMessage}"); + } + + return config; + } + catch (Exception ex) + { + throw new Exception($"瀵煎叆閰嶇疆澶辫触: {ex.Message}", ex); + } + } + } +} + diff --git a/StandardScene.Core/Charge/ChargeUdpService.cs b/StandardScene.Core/Charge/ChargeUdpService.cs new file mode 100644 index 0000000..1bb271e --- /dev/null +++ b/StandardScene.Core/Charge/ChargeUdpService.cs @@ -0,0 +1,62 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net.Sockets; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using SimpleLite; +using SimpleCore; +using SimpleCore.Library; +using StandardScene.ChargeStationType; + +namespace StandardScene.Charge +{ + /// + /// 鍏呯數妗﹗dp鐩戝惉 + /// + public class ChargeUdpService + { + public Thread ListenerThread; + + public ChargeUdpService() + { + ListenerThread = new Thread(ListenerProcess); + ListenerThread.Start(); + } + + private static async void ListenerProcess() + { + var messageService = CommunicationMessageService.Instance; + using (UdpClient udpListener = new UdpClient(40001)) + { + Diagnosis.Log($"Listening for UDP messages on port {40001}"); + while (true) + { + try + { + var result = await udpListener.ReceiveAsync(); + var remoteEndPoint = result.RemoteEndPoint; + var message = result.Buffer; + messageService.AddReceiveMessage(remoteEndPoint.Address.ToString(), 40001, BitConverter.ToString(message).Replace("-", " "), "FRLDShort"); + Diagnosis.Log($"ChargeStation ADD:[{BitConverter.ToString(message).Replace("-", " ")}]","UDP杩斿洖鎶ユ枃淇℃伅",true); + var chargeMission = SimpleProject.proj.Missions.OfType().FirstOrDefault(); + if (chargeMission == null) continue; + var chargeStation = + chargeMission.ChargeStations.FirstOrDefault(c => + c.Value.Ip == remoteEndPoint.Address.ToString()).Value; + if (chargeStation == null) continue; + if (message.Length > 28) + chargeStation.IsSafe = message[28] == 1; + chargeStation.OnUdpMessage(message); + } + catch (Exception e) + { + // 鍗曞抚寮傚父锛堝惈瓒婄晫/鍗婂寘锛変笉寰椾腑鏂 UDP 鐩戝惉绾跨▼ + Diagnosis.Log($"鍏呯數UDP鎺ユ敹澶勭悊寮傚父: {e.Message}", "UDP", true); + } + } + } + } + } +} diff --git a/StandardScene.Core/Charge/CommunicationMessage.cs b/StandardScene.Core/Charge/CommunicationMessage.cs new file mode 100644 index 0000000..9bbbb1c --- /dev/null +++ b/StandardScene.Core/Charge/CommunicationMessage.cs @@ -0,0 +1,89 @@ +using System; + +namespace StandardScene.Charge +{ + /// + /// 閫氳鎶ユ枃鏁版嵁妯″瀷 + /// + public class CommunicationMessage + { + /// + /// 鎶ユ枃ID锛堣嚜鍔ㄧ敓鎴愶級 + /// + public string MessageId { get; set; } + + /// + /// 鏃堕棿鎴 + /// + public DateTime Timestamp { get; set; } + + /// + /// 鏂瑰悜锛堝彂閫/鎺ユ敹锛 + /// + public MessageDirection Direction { get; set; } + + /// + /// IP鍦板潃 + /// + public string IpAddress { get; set; } + + /// + /// 绔彛鍙 + /// + public int Port { get; set; } + + /// + /// 鍘熷鎶ユ枃鏁版嵁锛堝崄鍏繘鍒跺瓧绗︿覆锛 + /// + public string RawData { get; set; } + + /// + /// 鎶ユ枃闀垮害锛堝瓧鑺傦級 + /// + public int Length { get; set; } + /// + /// 鍗忚绫诲瀷 + /// + public string Type { get; set; } + + /// + /// 鍏宠仈鐨勫厖鐢垫々ID锛堝彲閫夛級 + /// + public string StationId { get; set; } + + public CommunicationMessage() + { + MessageId = GenerateMessageId(); + Timestamp = DateTime.Now; + } + + private static string GenerateMessageId() + { + return $"MSG{DateTime.Now:yyyyMMddHHmmssfff}{new Random().Next(100, 999)}"; + } + + public override string ToString() + { + return $"[{Timestamp:HH:mm:ss.fff}] {Direction} {IpAddress}:{Port} - {Length}瀛楄妭"; + } + } + + //瑙f瀽鍚庣殑鏁版嵁 + + /// + /// 鎶ユ枃鏂瑰悜鏋氫妇 + /// + public enum MessageDirection + { + /// + /// 鍙戦 + /// + Send = 0, + + /// + /// 鎺ユ敹 + /// + Receive = 1 + } +} + diff --git a/StandardScene.Core/Charge/CommunicationMessageService.cs b/StandardScene.Core/Charge/CommunicationMessageService.cs new file mode 100644 index 0000000..10cb21b --- /dev/null +++ b/StandardScene.Core/Charge/CommunicationMessageService.cs @@ -0,0 +1,587 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; + +namespace StandardScene.Charge +{ + /// + /// 閫氳鎶ユ枃鏁版嵁鏈嶅姟锛堝崟渚嬫ā寮忥級 + /// + public class CommunicationMessageService + { + private static CommunicationMessageService _instance; + private static readonly object _lock = new object(); + private readonly object _dataLock = new object(); + private readonly AlarmConfigDataService dataService; + private readonly LinkedList _messages; + private const int MaxMessages = 100; // 鏈澶氫繚鐣100鏉 + + /// + /// 鎶ユ枃娣诲姞浜嬩欢 + /// + public event EventHandler MessageAdded; + + /// + /// 鑾峰彇鍗曚緥瀹炰緥 + /// + public static CommunicationMessageService Instance + { + get + { + if (_instance == null) + { + lock (_lock) + { + if (_instance == null) + { + _instance = new CommunicationMessageService(); + } + } + } + return _instance; + } + } + + private CommunicationMessageService() + { + _messages = new LinkedList(); + dataService = AlarmConfigDataService.Instance; + } + + /// + /// 娣诲姞鎶ユ枃 + /// + public void AddMessage(CommunicationMessage message) + { + if (message == null) + return; + + lock (_dataLock) + { + // 娣诲姞鍒伴摼琛ㄥご閮紙鏈鏂扮殑鍦ㄥ墠闈級 + _messages.AddFirst(message); + + // 濡傛灉瓒呰繃鏈澶ф暟閲忥紝绉婚櫎鏈鏃х殑 + while (_messages.Count > MaxMessages) + { + _messages.RemoveLast(); + } + } + + // 瑙﹀彂浜嬩欢 + MessageAdded?.Invoke(this, message); + } + + /// + /// 娣诲姞鍙戦佹姤鏂 + /// + public void AddSendMessage(string ipAddress, int port, string rawData, string type, string stationId = null) + { + var message = new CommunicationMessage + { + Direction = MessageDirection.Send, + IpAddress = ipAddress, + Port = port, + RawData = rawData, + Length = rawData.Split(' ')?.Length ?? 0, // 鍋囪鏄崄鍏繘鍒跺瓧绗︿覆 + StationId = stationId, + Type = type + }; + AddMessage(message); + + // 鍙戦佹姤鏂囧悗锛岃В鏋愬苟鏇存柊鍏呯數妗╂暟鎹紙鍙戦佹柟鍚戯級 + ParseSendDataAndUpdateStation(ipAddress, port, rawData, type); + } + + /// + /// 娣诲姞鎺ユ敹鎶ユ枃 + /// + public void AddReceiveMessage(string ipAddress, int port, string rawData, string type, string stationId = null) + { + var message = new CommunicationMessage + { + Direction = MessageDirection.Receive, + IpAddress = ipAddress, + Port = port, + RawData = rawData, + Length = rawData.Split(' ')?.Length ?? 0, + StationId = stationId, + Type = type + }; + AddMessage(message); + + // 鎺ユ敹鍒版姤鏂囧悗锛岃В鏋愬苟鏇存柊鍏呯數妗╂暟鎹紙鎺ユ敹鏂瑰悜锛 + ParseReceiveDataAndUpdateStation(ipAddress, port, rawData, type); + } + + /// + /// 瑙f瀽鍙戦佹姤鏂囧苟鏇存柊鍏呯數妗╂暟鎹 + /// + private void ParseSendDataAndUpdateStation(string ipAddress, int port, string rawData, string type) + { + try + { + var dataService = ChargeStationDataService.Instance; + + // 鏍规嵁IP鍦板潃鏌ユ壘鍏呯數妗 + var station = dataService.GetStationByIp(ipAddress, port); + if (station == null) + { + return; // 鏈壘鍒板搴斿厖鐢垫々锛屼笉澶勭悊 + } + + // 瑙f瀽鍙戦佹姤鏂囨暟鎹 + var parsedData = ParseSendRawData(rawData, type); + if (parsedData == null) + { + return; // 瑙f瀽澶辫触锛屼笉澶勭悊 + } + + // 鏇存柊鍏呯數妗╂暟鎹紙鍙戦佹柟鍚戯級 + UpdateStationFromSendData(station, parsedData); + + // 鏇存柊鍒版暟鎹湇鍔 + dataService.UpdateStation(station, out string errorMessage); + + } + catch + { + // 闈欓粯澶勭悊寮傚父锛屼笉褰卞搷鎶ユ枃璁板綍 + } + } + + /// + /// 瑙f瀽鎺ユ敹鎶ユ枃骞舵洿鏂板厖鐢垫々鏁版嵁 + /// + public void ParseReceiveDataAndUpdateStation(string ipAddress, int port, string rawData, string type) + { + try + { + var dataService = ChargeStationDataService.Instance; + + // 鏍规嵁IP鍦板潃鏌ユ壘鍏呯數妗 + var station = dataService.GetStationByIp(ipAddress); + if (station == null) + { + return; // 鏈壘鍒板搴斿厖鐢垫々锛屼笉澶勭悊 + } + + // 瑙f瀽鎺ユ敹鎶ユ枃鏁版嵁 + var parsedData = ParseReceiveRawData(rawData, type); + if (parsedData == null) + { + return; // 瑙f瀽澶辫触锛屼笉澶勭悊 + } + + // 鏇存柊鍏呯數妗╂暟鎹紙鎺ユ敹鏂瑰悜锛 + UpdateStationFromReceiveData(station, parsedData); + + // 鍚堝苟鍙戦佸拰鎺ユ敹鏁版嵁锛屾洿鏂板埌鏁版嵁鏈嶅姟 + dataService.UpdateStation(station, out string errorMessage); + } + catch + { + // 闈欓粯澶勭悊寮傚父锛屼笉褰卞搷鎶ユ枃璁板綍 + } + } + + /// + /// 瑙f瀽鍙戦佹姤鏂囨暟鎹 + /// + public ParsedSendData ParseSendRawData(string rawData, string type) + { + try + { + // 灏嗛楀彿鍒嗛殧鐨勫瓧绗︿覆杞崲涓哄瓧鑺傛暟缁 + var parts = rawData.Split(' '); + if (parts.Length < 10) // 鍙戦佹姤鏂囪嚦灏10瀛楄妭 + { + return null; + } + + var bytes = new byte[parts.Length]; + for (int i = 0; i < parts.Length; i++) + { + if (!byte.TryParse(parts[i], NumberStyles.HexNumber, CultureInfo.InvariantCulture, out bytes[i])) + { + return null; + } + } + + byte chargeCommand = 0; + double setVoltage = 0; + double setCurrent = 0; + short carId = 0; + int carSoc = 0; + double carVoltage = 0; + double carCurrent = 0; + + if (type == "FRLDShort") + { + chargeCommand = bytes[2]; + + setCurrent = BitConverter.ToInt32(new byte[] { bytes[6], bytes[5], bytes[4], bytes[3] }, 0) / 10f; + setVoltage = BitConverter.ToInt32(new byte[] { bytes[10], bytes[9], bytes[8], bytes[7] }, 0) / 10f; + carId = BitConverter.ToInt16(new byte[] { bytes[14], bytes[13] }, 0); + carSoc = bytes[15]; + carVoltage = BitConverter.ToInt32(new byte[] { bytes[19], bytes[18], bytes[17], bytes[16] }, 0); + carCurrent = BitConverter.ToInt32(new byte[] { bytes[23], bytes[22], bytes[21], bytes[20] }, 0); + + } + else if (type == "FRLDTall") + { + chargeCommand = bytes[1]; + + setCurrent = BitConverter.ToSingle(new byte[] { bytes[5], bytes[4], bytes[3], bytes[2] }, 0); + setVoltage = BitConverter.ToSingle(new byte[] { bytes[9], bytes[8], bytes[7], bytes[6] }, 0); + carId = BitConverter.ToInt16(new byte[] { bytes[13], bytes[12] }, 0); + carSoc = BitConverter.ToInt16(new byte[] { bytes[15], bytes[14] }, 0); + carVoltage = BitConverter.ToSingle(new byte[] { bytes[19], bytes[18], bytes[17], bytes[16] }, 0); + carCurrent = BitConverter.ToSingle(new byte[] { bytes[23], bytes[22], bytes[21], bytes[20] }, 0); + } + + // 瑙f瀽鍙戦佹姤鏂囷紙鏍规嵁瀹為檯鍗忚锛 + var parsed = new ParsedSendData + { + + ChargeCommand = chargeCommand, + SetVoltage = setVoltage, + SetCurrent = setCurrent, + CurrentVehicleId = carId, + BatteryLevel = carSoc, + CarVoltage = carVoltage, + CarCurrent = carCurrent, + // 鍙戦佹椂闂 + SendTime = DateTime.Now + }; + + return parsed; + } + catch + { + return null; + } + } + + /// + /// 瑙f瀽鎺ユ敹鎶ユ枃鏁版嵁 + /// + public ParsedReceiveData ParseReceiveRawData(string rawData, string type) + { + try + { + // 灏嗛楀彿鍒嗛殧鐨勫瓧绗︿覆杞崲涓哄瓧鑺傛暟缁 + var parts = rawData.Split(' '); + if (parts.Length < 30) // 鍋囪鎶ユ枃鑷冲皯30瀛楄妭 + { + return null; + } + + var bytes = new byte[parts.Length]; + for (int i = 0; i < parts.Length; i++) + { + if (!byte.TryParse(parts[i], NumberStyles.HexNumber, CultureInfo.InvariantCulture, out bytes[i])) + { + return null; + } + } + + + double realTimeVoltage = 0; + double realTimeCurrent = 0; + byte chargeStationStatus = 0; + byte chargeId = 0; + short batteryAH = 0; + byte mechanismStatus = 0; + byte alarmValue = 0; + + + if (type == "FRLDShort") + { + + realTimeCurrent = BitConverter.ToInt32(new byte[] { bytes[5], bytes[4], bytes[3], bytes[2] }, 0) / 10f; + realTimeVoltage = BitConverter.ToInt32(new byte[] { bytes[9], bytes[8], bytes[7], bytes[6] }, 0) / 10f; + chargeStationStatus = bytes[14]; + chargeId = bytes[15]; + batteryAH = BitConverter.ToInt16(new byte[] { bytes[17], bytes[16] }, 0); + mechanismStatus = bytes[28]; + } + else if (type == "FRLDTall") + { + chargeStationStatus = bytes[14]; + mechanismStatus = bytes[28]; + alarmValue = bytes[15]; + } + + + // 鏍规嵁瀹為檯鍗忚瑙f瀽鎺ユ敹鏁版嵁 + var parsed = new ParsedReceiveData + { + RealTimeCurrent = realTimeCurrent, + RealTimeVoltage = realTimeVoltage, + Status = type == "FRLDTall" ? ParseStationStatusFRLDTall(chargeStationStatus) : ParseStationStatus(chargeStationStatus), + ChargeID = chargeId, + BatteryAH = batteryAH, + MechanismStatus = type == "FRLDTall" ? ParseMechanismStatusFRLDTall(mechanismStatus) : ParseMechanismStatus(mechanismStatus), + HasAlarm = chargeStationStatus == 2, + AlarmCode = alarmValue, + // AlarmLevel = ParseAlarmLevel(bytes[20]) + ReceiveTime = DateTime.Now + }; + + return parsed; + } + catch + { + return null; + } + } + + + + /// + /// 浠庡彂閫佹姤鏂囨洿鏂板厖鐢垫々鏁版嵁 + /// + private void UpdateStationFromSendData(ChargeStation station, ParsedSendData parsedData) + { + // 鏇存柊鍙戦佺殑璁惧畾鍊 + //station.SetVoltage = parsedData.SetVoltage; + //station.SetElectricCurrent = parsedData.SetCurrent; + + // 鏇存柊鏈鍚庡彂閫佹椂闂 + station.LastSendTime = parsedData.SendTime; + + // 鏍规嵁鍙戦佺殑鍏呯數鎸囦护鏇存柊鐘舵 + if (parsedData.ChargeCommand == 1) + { + // 鍙戦佷簡鍚姩鍏呯數鎸囦护 + station.ChargeCommandStatus = ChargeCommandStatus.Started; + } + else if (parsedData.ChargeCommand == 0) + { + // 鍙戦佷簡鍋滄鍏呯數鎸囦护 + station.ChargeCommandStatus = ChargeCommandStatus.Stopped; + } + + station.BatteryLevel = parsedData.BatteryLevel; + station.CurrentVehicle = parsedData.CurrentVehicleId.ToString(); + + } + + /// + /// 浠庢帴鏀舵姤鏂囨洿鏂板厖鐢垫々鏁版嵁 + /// + private void UpdateStationFromReceiveData(ChargeStation station, ParsedReceiveData parsedData) + { + station.LastReceiveTime = parsedData.ReceiveTime; + station.MechanismStatus = parsedData.MechanismStatus; + station.RealTimeVoltage = parsedData.RealTimeVoltage; + station.RealTimeCurrent = parsedData.RealTimeCurrent; + station.Status = parsedData.Status; + station.HasAlarm = parsedData.HasAlarm; + station.AlarmLevel = parsedData.AlarmLevel; + if (parsedData.HasAlarm) + { + var alarmInfo = dataService.GetAlarmConfigAlarmCode(parsedData.AlarmCode); + if (alarmInfo != null) + { + station.AlarmMessage = $"鎶ヨ绾у埆: {GetAlarmLevelText(alarmInfo.Level)}:{alarmInfo.AlarmContent}"; + } + + } + else + { + station.AlarmMessage = string.Empty; + } + + + + + } + + /// + /// 瑙f瀽鏈烘瀯鐘舵 + /// + private MechanismStatus ParseMechanismStatus(byte statusByte) + { + switch (statusByte) + { + case 1: return MechanismStatus.Retracted; + case 2: return MechanismStatus.Extended; + default: return MechanismStatus.Extending; + } + } + + /// + /// 瑙f瀽鏈烘瀯鐘舵 + /// + private MechanismStatus ParseMechanismStatusFRLDTall(byte statusByte) + { + switch (statusByte) + { + case 1: return MechanismStatus.Extended; + case 2: return MechanismStatus.Retracted; + default: return MechanismStatus.Extending; + } + } + + /// + /// 瑙f瀽鎶ヨ绾у埆 + /// + private AlarmLevel ParseAlarmLevel(byte alarmByte) + { + if (alarmByte == 0) return AlarmLevel.None; + if (alarmByte <= 2) return AlarmLevel.Low; + if (alarmByte <= 5) return AlarmLevel.Medium; + if (alarmByte <= 8) return AlarmLevel.High; + return AlarmLevel.Critical; + } + + /// + /// 瑙f瀽鍏呯數妗╃姸鎬 + /// + private ChargeStationStatus ParseStationStatus(byte statusByte) + { + switch (statusByte) + { + case 0: return ChargeStationStatus.Idle; + case 1: return ChargeStationStatus.Charging; + case 2: return ChargeStationStatus.Fault; + case 3: return ChargeStationStatus.Battery; + default: return ChargeStationStatus.Idle; + } + } + + private ChargeStationStatus ParseStationStatusFRLDTall(byte statusByte) + { + switch (statusByte) + { + case 0: return ChargeStationStatus.Idle; + case 2: return ChargeStationStatus.Idle; + case 3: return ChargeStationStatus.Charging; + case 4: return ChargeStationStatus.Fault; + default: return ChargeStationStatus.Idle; + } + } + + /// + /// 鑾峰彇鎶ヨ绾у埆鏂囨湰 + /// + private string GetAlarmLevelText(AlarmLevel level) + { + switch (level) + { + case AlarmLevel.None: return "鏃"; + case AlarmLevel.Low: return "浣"; + case AlarmLevel.Medium: return "涓"; + case AlarmLevel.High: return "楂"; + case AlarmLevel.Critical: return "涓ラ噸"; + default: return "鏈煡"; + } + } + + /// + /// 瑙f瀽鍚庣殑鍙戦佹姤鏂囨暟鎹紙鍐呴儴绫伙級 + /// + public class ParsedSendData + { + public byte ChargeCommand { get; set; } + public double SetVoltage { get; set; } + public double SetCurrent { get; set; } + public double BatteryLevel { get; set; } + public int CurrentVehicleId { get; set; } + public double CarVoltage { get; set; } + public double CarCurrent { get; set; } + public DateTime SendTime { get; set; } + } + + /// + /// 瑙f瀽鍚庣殑鎺ユ敹鎶ユ枃鏁版嵁锛堝唴閮ㄧ被锛 + /// + public class ParsedReceiveData + { + public CommunicationStatus CommStatus { get; set; } + public ChargeCommandStatus ChargeCommandStatus { get; set; } + public MechanismStatus MechanismStatus { get; set; } + public double RealTimeVoltage { get; set; } + public double RealTimeCurrent { get; set; } + public int ChargeID { get; set; } + public float BatteryAH { get; set; } + public bool HasAlarm { get; set; } + public AlarmLevel AlarmLevel { get; set; } + public int AlarmCode { get; set; } + public ChargeStationStatus Status { get; set; } + + public DateTime ReceiveTime { get; set; } + } + + + + /// + /// 鑾峰彇鎵鏈夋姤鏂 + /// + public List GetAllMessages() + { + lock (_dataLock) + { + return _messages.ToList(); + } + } + + /// + /// 鏍规嵁IP绛涢夋姤鏂 + /// + public List GetMessagesByIp(string ipAddress) + { + if (string.IsNullOrWhiteSpace(ipAddress)) + return GetAllMessages(); + + lock (_dataLock) + { + return _messages.Where(m => m.IpAddress == ipAddress).ToList(); + } + } + + /// + /// 鏍规嵁鍏呯數妗㊣D绛涢夋姤鏂 + /// + public List GetMessagesByStationId(string stationId) + { + if (string.IsNullOrWhiteSpace(stationId)) + return GetAllMessages(); + + lock (_dataLock) + { + return _messages.Where(m => m.StationId == stationId).ToList(); + } + } + + /// + /// 娓呯┖鎵鏈夋姤鏂 + /// + public void Clear() + { + lock (_dataLock) + { + _messages.Clear(); + } + } + + /// + /// 鑾峰彇鎵鏈夊敮涓IP鍦板潃鍒楄〃 + /// + public List GetUniqueIpAddresses() + { + lock (_dataLock) + { + return _messages + .Select(m => m.IpAddress) + .Distinct() + .OrderBy(ip => ip) + .ToList(); + } + } + } +} + diff --git a/StandardScene.Core/Charge/CommunicationMonitorForm.Designer.cs b/StandardScene.Core/Charge/CommunicationMonitorForm.Designer.cs new file mode 100644 index 0000000..f83e9b4 --- /dev/null +++ b/StandardScene.Core/Charge/CommunicationMonitorForm.Designer.cs @@ -0,0 +1,376 @@ +namespace StandardScene.Charge +{ + partial class CommunicationMonitorForm + { + private System.ComponentModel.IContainer components = null; + + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + private void InitializeComponent() + { + System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle1 = new System.Windows.Forms.DataGridViewCellStyle(); + this.splitContainer = new System.Windows.Forms.SplitContainer(); + this.pnlLeft = new System.Windows.Forms.Panel(); + this.dgvMessages = new System.Windows.Forms.DataGridView(); + this.colTime = new System.Windows.Forms.DataGridViewTextBoxColumn(); + this.colDirection = new System.Windows.Forms.DataGridViewTextBoxColumn(); + this.colIpAddress = new System.Windows.Forms.DataGridViewTextBoxColumn(); + this.colPort = new System.Windows.Forms.DataGridViewTextBoxColumn(); + this.colLength = new System.Windows.Forms.DataGridViewTextBoxColumn(); + this.colRawData = new System.Windows.Forms.DataGridViewTextBoxColumn(); + this.colStationId = new System.Windows.Forms.DataGridViewTextBoxColumn(); + this.type = new System.Windows.Forms.DataGridViewTextBoxColumn(); + this.pnlLeftTop = new System.Windows.Forms.Panel(); + this.button1 = new System.Windows.Forms.Button(); + this.btnClear = new System.Windows.Forms.Button(); + this.btnRefresh = new System.Windows.Forms.Button(); + this.lblStatistics = new System.Windows.Forms.Label(); + this.cmbIpFilter = new System.Windows.Forms.ComboBox(); + this.lblIpFilter = new System.Windows.Forms.Label(); + this.pnlRight = new System.Windows.Forms.Panel(); + this.txtParsedData = new System.Windows.Forms.TextBox(); + this.pnlRightTop = new System.Windows.Forms.Panel(); + this.btnClose = new System.Windows.Forms.Button(); + this.lblParsedTitle = new System.Windows.Forms.Label(); + ((System.ComponentModel.ISupportInitialize)(this.splitContainer)).BeginInit(); + this.splitContainer.Panel1.SuspendLayout(); + this.splitContainer.Panel2.SuspendLayout(); + this.splitContainer.SuspendLayout(); + this.pnlLeft.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.dgvMessages)).BeginInit(); + this.pnlLeftTop.SuspendLayout(); + this.pnlRight.SuspendLayout(); + this.pnlRightTop.SuspendLayout(); + this.SuspendLayout(); + // + // splitContainer + // + this.splitContainer.Dock = System.Windows.Forms.DockStyle.Fill; + this.splitContainer.Location = new System.Drawing.Point(0, 0); + this.splitContainer.Name = "splitContainer"; + // + // splitContainer.Panel1 + // + this.splitContainer.Panel1.Controls.Add(this.pnlLeft); + // + // splitContainer.Panel2 + // + this.splitContainer.Panel2.Controls.Add(this.pnlRight); + this.splitContainer.Size = new System.Drawing.Size(1400, 800); + this.splitContainer.SplitterDistance = 850; + this.splitContainer.TabIndex = 0; + // + // pnlLeft + // + this.pnlLeft.Controls.Add(this.dgvMessages); + this.pnlLeft.Controls.Add(this.pnlLeftTop); + this.pnlLeft.Dock = System.Windows.Forms.DockStyle.Fill; + this.pnlLeft.Location = new System.Drawing.Point(0, 0); + this.pnlLeft.Name = "pnlLeft"; + this.pnlLeft.Size = new System.Drawing.Size(850, 800); + this.pnlLeft.TabIndex = 0; + // + // dgvMessages + // + this.dgvMessages.AllowUserToAddRows = false; + this.dgvMessages.AllowUserToDeleteRows = false; + this.dgvMessages.AutoSizeColumnsMode = System.Windows.Forms.DataGridViewAutoSizeColumnsMode.Fill; + this.dgvMessages.BackgroundColor = System.Drawing.Color.White; + this.dgvMessages.BorderStyle = System.Windows.Forms.BorderStyle.None; + dataGridViewCellStyle1.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleLeft; + dataGridViewCellStyle1.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(63)))), ((int)(((byte)(81)))), ((int)(((byte)(181))))); + dataGridViewCellStyle1.Font = new System.Drawing.Font("寰蒋闆呴粦", 9F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + dataGridViewCellStyle1.ForeColor = System.Drawing.Color.White; + dataGridViewCellStyle1.SelectionBackColor = System.Drawing.SystemColors.Highlight; + dataGridViewCellStyle1.SelectionForeColor = System.Drawing.SystemColors.HighlightText; + dataGridViewCellStyle1.WrapMode = System.Windows.Forms.DataGridViewTriState.True; + this.dgvMessages.ColumnHeadersDefaultCellStyle = dataGridViewCellStyle1; + this.dgvMessages.ColumnHeadersHeight = 35; + this.dgvMessages.Columns.AddRange(new System.Windows.Forms.DataGridViewColumn[] { + this.colTime, + this.colDirection, + this.colIpAddress, + this.colPort, + this.colLength, + this.colRawData, + this.colStationId, + this.type}); + this.dgvMessages.Dock = System.Windows.Forms.DockStyle.Fill; + this.dgvMessages.EnableHeadersVisualStyles = false; + this.dgvMessages.GridColor = System.Drawing.Color.LightGray; + this.dgvMessages.Location = new System.Drawing.Point(0, 80); + this.dgvMessages.MultiSelect = false; + this.dgvMessages.Name = "dgvMessages"; + this.dgvMessages.ReadOnly = true; + this.dgvMessages.RowHeadersVisible = false; + this.dgvMessages.RowHeadersWidth = 51; + this.dgvMessages.RowTemplate.Height = 30; + this.dgvMessages.SelectionMode = System.Windows.Forms.DataGridViewSelectionMode.FullRowSelect; + this.dgvMessages.Size = new System.Drawing.Size(850, 720); + this.dgvMessages.TabIndex = 1; + this.dgvMessages.SelectionChanged += new System.EventHandler(this.dgvMessages_SelectionChanged); + // + // colTime + // + this.colTime.FillWeight = 80F; + this.colTime.HeaderText = "鏃堕棿"; + this.colTime.MinimumWidth = 6; + this.colTime.Name = "colTime"; + this.colTime.ReadOnly = true; + // + // colDirection + // + this.colDirection.FillWeight = 50F; + this.colDirection.HeaderText = "鏂瑰悜"; + this.colDirection.MinimumWidth = 6; + this.colDirection.Name = "colDirection"; + this.colDirection.ReadOnly = true; + // + // colIpAddress + // + this.colIpAddress.FillWeight = 80F; + this.colIpAddress.HeaderText = "IP鍦板潃"; + this.colIpAddress.MinimumWidth = 6; + this.colIpAddress.Name = "colIpAddress"; + this.colIpAddress.ReadOnly = true; + // + // colPort + // + this.colPort.FillWeight = 50F; + this.colPort.HeaderText = "绔彛"; + this.colPort.MinimumWidth = 6; + this.colPort.Name = "colPort"; + this.colPort.ReadOnly = true; + // + // colLength + // + this.colLength.FillWeight = 50F; + this.colLength.HeaderText = "闀垮害"; + this.colLength.MinimumWidth = 6; + this.colLength.Name = "colLength"; + this.colLength.ReadOnly = true; + // + // colRawData + // + this.colRawData.FillWeight = 200F; + this.colRawData.HeaderText = "鍘熷鏁版嵁"; + this.colRawData.MinimumWidth = 6; + this.colRawData.Name = "colRawData"; + this.colRawData.ReadOnly = true; + // + // colStationId + // + this.colStationId.FillWeight = 80F; + this.colStationId.HeaderText = "鍏呯數妗"; + this.colStationId.MinimumWidth = 6; + this.colStationId.Name = "colStationId"; + this.colStationId.ReadOnly = true; + // + // type + // + this.type.HeaderText = "鍗忚绫诲瀷"; + this.type.MinimumWidth = 6; + this.type.Name = "type"; + this.type.ReadOnly = true; + // + // pnlLeftTop + // + this.pnlLeftTop.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(250)))), ((int)(((byte)(250)))), ((int)(((byte)(250))))); + this.pnlLeftTop.Controls.Add(this.button1); + this.pnlLeftTop.Controls.Add(this.btnClear); + this.pnlLeftTop.Controls.Add(this.btnRefresh); + this.pnlLeftTop.Controls.Add(this.lblStatistics); + this.pnlLeftTop.Controls.Add(this.cmbIpFilter); + this.pnlLeftTop.Controls.Add(this.lblIpFilter); + this.pnlLeftTop.Dock = System.Windows.Forms.DockStyle.Top; + this.pnlLeftTop.Location = new System.Drawing.Point(0, 0); + this.pnlLeftTop.Name = "pnlLeftTop"; + this.pnlLeftTop.Padding = new System.Windows.Forms.Padding(10); + this.pnlLeftTop.Size = new System.Drawing.Size(850, 80); + this.pnlLeftTop.TabIndex = 0; + // + // button1 + // + this.button1.Font = new System.Drawing.Font("寰蒋闆呴粦", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + this.button1.Location = new System.Drawing.Point(546, 16); + this.button1.Name = "button1"; + this.button1.Size = new System.Drawing.Size(80, 32); + this.button1.TabIndex = 5; + this.button1.Text = "鏆傚仠"; + this.button1.UseVisualStyleBackColor = true; + this.button1.Click += new System.EventHandler(this.button1_Click); + // + // btnClear + // + this.btnClear.Font = new System.Drawing.Font("寰蒋闆呴粦", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + this.btnClear.Location = new System.Drawing.Point(460, 15); + this.btnClear.Name = "btnClear"; + this.btnClear.Size = new System.Drawing.Size(80, 32); + this.btnClear.TabIndex = 4; + this.btnClear.Text = "娓呯┖"; + this.btnClear.UseVisualStyleBackColor = true; + this.btnClear.Click += new System.EventHandler(this.btnClear_Click); + // + // btnRefresh + // + this.btnRefresh.Font = new System.Drawing.Font("寰蒋闆呴粦", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + this.btnRefresh.Location = new System.Drawing.Point(370, 15); + this.btnRefresh.Name = "btnRefresh"; + this.btnRefresh.Size = new System.Drawing.Size(80, 32); + this.btnRefresh.TabIndex = 3; + this.btnRefresh.Text = "鍒锋柊"; + this.btnRefresh.UseVisualStyleBackColor = true; + this.btnRefresh.Click += new System.EventHandler(this.btnRefresh_Click); + // + // lblStatistics + // + this.lblStatistics.AutoSize = true; + this.lblStatistics.Font = new System.Drawing.Font("寰蒋闆呴粦", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + this.lblStatistics.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(100)))), ((int)(((byte)(100)))), ((int)(((byte)(100))))); + this.lblStatistics.Location = new System.Drawing.Point(13, 52); + this.lblStatistics.Name = "lblStatistics"; + this.lblStatistics.Size = new System.Drawing.Size(115, 20); + this.lblStatistics.TabIndex = 2; + this.lblStatistics.Text = "鏄剧ず: 0 | 鎬绘暟: 0"; + // + // cmbIpFilter + // + this.cmbIpFilter.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.cmbIpFilter.Font = new System.Drawing.Font("寰蒋闆呴粦", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + this.cmbIpFilter.FormattingEnabled = true; + this.cmbIpFilter.Location = new System.Drawing.Point(100, 17); + this.cmbIpFilter.Name = "cmbIpFilter"; + this.cmbIpFilter.Size = new System.Drawing.Size(250, 28); + this.cmbIpFilter.TabIndex = 1; + this.cmbIpFilter.SelectedIndexChanged += new System.EventHandler(this.cmbIpFilter_SelectedIndexChanged); + // + // lblIpFilter + // + this.lblIpFilter.AutoSize = true; + this.lblIpFilter.Font = new System.Drawing.Font("寰蒋闆呴粦", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + this.lblIpFilter.Location = new System.Drawing.Point(13, 21); + this.lblIpFilter.Name = "lblIpFilter"; + this.lblIpFilter.Size = new System.Drawing.Size(67, 20); + this.lblIpFilter.TabIndex = 0; + this.lblIpFilter.Text = "IP绛涢夛細"; + // + // pnlRight + // + this.pnlRight.Controls.Add(this.txtParsedData); + this.pnlRight.Controls.Add(this.pnlRightTop); + this.pnlRight.Dock = System.Windows.Forms.DockStyle.Fill; + this.pnlRight.Location = new System.Drawing.Point(0, 0); + this.pnlRight.Name = "pnlRight"; + this.pnlRight.Size = new System.Drawing.Size(546, 800); + this.pnlRight.TabIndex = 0; + // + // txtParsedData + // + this.txtParsedData.BackColor = System.Drawing.Color.White; + this.txtParsedData.Dock = System.Windows.Forms.DockStyle.Fill; + this.txtParsedData.Font = new System.Drawing.Font("Consolas", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.txtParsedData.Location = new System.Drawing.Point(0, 60); + this.txtParsedData.Multiline = true; + this.txtParsedData.Name = "txtParsedData"; + this.txtParsedData.ReadOnly = true; + this.txtParsedData.ScrollBars = System.Windows.Forms.ScrollBars.Both; + this.txtParsedData.Size = new System.Drawing.Size(546, 740); + this.txtParsedData.TabIndex = 1; + this.txtParsedData.WordWrap = false; + // + // pnlRightTop + // + this.pnlRightTop.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(250)))), ((int)(((byte)(250)))), ((int)(((byte)(250))))); + this.pnlRightTop.Controls.Add(this.btnClose); + this.pnlRightTop.Controls.Add(this.lblParsedTitle); + this.pnlRightTop.Dock = System.Windows.Forms.DockStyle.Top; + this.pnlRightTop.Location = new System.Drawing.Point(0, 0); + this.pnlRightTop.Name = "pnlRightTop"; + this.pnlRightTop.Padding = new System.Windows.Forms.Padding(10); + this.pnlRightTop.Size = new System.Drawing.Size(546, 60); + this.pnlRightTop.TabIndex = 0; + // + // btnClose + // + this.btnClose.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); + this.btnClose.Font = new System.Drawing.Font("寰蒋闆呴粦", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + this.btnClose.Location = new System.Drawing.Point(446, 15); + this.btnClose.Name = "btnClose"; + this.btnClose.Size = new System.Drawing.Size(80, 32); + this.btnClose.TabIndex = 1; + this.btnClose.Text = "鍏抽棴"; + this.btnClose.UseVisualStyleBackColor = true; + this.btnClose.Click += new System.EventHandler(this.btnClose_Click); + // + // lblParsedTitle + // + this.lblParsedTitle.AutoSize = true; + this.lblParsedTitle.Font = new System.Drawing.Font("寰蒋闆呴粦", 10F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + this.lblParsedTitle.Location = new System.Drawing.Point(13, 20); + this.lblParsedTitle.Name = "lblParsedTitle"; + this.lblParsedTitle.Size = new System.Drawing.Size(112, 24); + this.lblParsedTitle.TabIndex = 0; + this.lblParsedTitle.Text = "鎶ユ枃鏁版嵁瑙f瀽"; + // + // CommunicationMonitorForm + // + this.AutoScaleDimensions = new System.Drawing.SizeF(8F, 15F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(1400, 800); + this.Controls.Add(this.splitContainer); + this.Name = "CommunicationMonitorForm"; + this.Text = "閫氳鐩戞帶"; + this.Load += new System.EventHandler(this.CommunicationMonitorForm_Load); + this.splitContainer.Panel1.ResumeLayout(false); + this.splitContainer.Panel2.ResumeLayout(false); + ((System.ComponentModel.ISupportInitialize)(this.splitContainer)).EndInit(); + this.splitContainer.ResumeLayout(false); + this.pnlLeft.ResumeLayout(false); + ((System.ComponentModel.ISupportInitialize)(this.dgvMessages)).EndInit(); + this.pnlLeftTop.ResumeLayout(false); + this.pnlLeftTop.PerformLayout(); + this.pnlRight.ResumeLayout(false); + this.pnlRight.PerformLayout(); + this.pnlRightTop.ResumeLayout(false); + this.pnlRightTop.PerformLayout(); + this.ResumeLayout(false); + + } + + #endregion + + private System.Windows.Forms.SplitContainer splitContainer; + private System.Windows.Forms.Panel pnlLeft; + private System.Windows.Forms.DataGridView dgvMessages; + private System.Windows.Forms.Panel pnlLeftTop; + private System.Windows.Forms.ComboBox cmbIpFilter; + private System.Windows.Forms.Label lblIpFilter; + private System.Windows.Forms.Panel pnlRight; + private System.Windows.Forms.TextBox txtParsedData; + private System.Windows.Forms.Panel pnlRightTop; + private System.Windows.Forms.Label lblParsedTitle; + private System.Windows.Forms.Label lblStatistics; + private System.Windows.Forms.Button btnRefresh; + private System.Windows.Forms.Button btnClear; + private System.Windows.Forms.Button btnClose; + private System.Windows.Forms.DataGridViewTextBoxColumn colTime; + private System.Windows.Forms.DataGridViewTextBoxColumn colDirection; + private System.Windows.Forms.DataGridViewTextBoxColumn colIpAddress; + private System.Windows.Forms.DataGridViewTextBoxColumn colPort; + private System.Windows.Forms.DataGridViewTextBoxColumn colLength; + private System.Windows.Forms.DataGridViewTextBoxColumn colRawData; + private System.Windows.Forms.DataGridViewTextBoxColumn colStationId; + private System.Windows.Forms.DataGridViewTextBoxColumn type; + private System.Windows.Forms.Button button1; + } +} + diff --git a/StandardScene.Core/Charge/CommunicationMonitorForm.cs b/StandardScene.Core/Charge/CommunicationMonitorForm.cs new file mode 100644 index 0000000..bc9db7f --- /dev/null +++ b/StandardScene.Core/Charge/CommunicationMonitorForm.cs @@ -0,0 +1,561 @@ +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Linq; +using System.Windows.Forms; + +namespace StandardScene.Charge +{ + /// + /// 閫氳鐩戞帶绐椾綋 + /// + public partial class CommunicationMonitorForm : Form + { + private readonly CommunicationMessageService messageService; + private bool isFormLoaded = false; + private bool isFormMessageStop = false; + private const int MaxDisplayRows = 100; + private const int UiBatchSize = 20; + private const int StatsRefreshMs = 500; + private readonly Queue pendingMessages = new Queue(); + private readonly object pendingMessagesLock = new object(); + private readonly Timer uiFlushTimer; + private readonly Timer statsRefreshTimer; + private bool pendingStatsRefresh = false; + private int lastDisplayCountForStats = 0; + public CommunicationMonitorForm() + { + InitializeComponent(); + messageService = CommunicationMessageService.Instance; + uiFlushTimer = new Timer { Interval = 500 }; + uiFlushTimer.Tick += UiFlushTimer_Tick; + statsRefreshTimer = new Timer { Interval = StatsRefreshMs }; + statsRefreshTimer.Tick += StatsRefreshTimer_Tick; + + // 璁㈤槄绐椾綋鍏抽棴浜嬩欢 + this.FormClosing += CommunicationMonitorForm_FormClosing; + } + + private void CommunicationMonitorForm_Load(object sender, EventArgs e) + { + try + { + InitializeForm(); + LoadMessages(); + + // 鏍囪绐椾綋宸插姞杞藉畬鎴 + isFormLoaded = true; + uiFlushTimer.Start(); + statsRefreshTimer.Start(); + + // 鍦ㄧ獥浣撳姞杞藉畬鎴愬悗鍐嶈闃呮姤鏂囨坊鍔犱簨浠讹紙閬垮厤鍦ㄥ垵濮嬪寲鏈熼棿瑙﹀彂锛 + messageService.MessageAdded += OnMessageAdded; + } + catch (Exception ex) + { + MessageBox.Show($"绐椾綋鍔犺浇澶辫触: {ex.Message}\r\n{ex.StackTrace}", "閿欒", + MessageBoxButtons.OK, MessageBoxIcon.Error); + } + } + + private void InitializeForm() + { + this.Text = "閫氳鐩戞帶"; + this.Size = new Size(1400, 800); + this.StartPosition = FormStartPosition.CenterScreen; + this.MinimumSize = new Size(1200, 600); + + // 鍒濆鍖朓P绛涢変笅鎷夋 + RefreshIpFilter(); + } + + /// + /// 鍒锋柊IP绛涢変笅鎷夋 + /// + private void RefreshIpFilter() + { + try + { + if (cmbIpFilter == null || messageService == null) + return; + + var selectedIp = cmbIpFilter.SelectedItem?.ToString(); + + cmbIpFilter.Items.Clear(); + cmbIpFilter.Items.Add("鍏ㄩ儴"); + + var ipAddresses = messageService.GetUniqueIpAddresses(); + if (ipAddresses != null) + { + foreach (var ip in ipAddresses) + { + if (!string.IsNullOrEmpty(ip)) + { + cmbIpFilter.Items.Add(ip); + } + } + } + + // 鎭㈠閫変腑椤 + if (!string.IsNullOrEmpty(selectedIp) && cmbIpFilter.Items.Contains(selectedIp)) + { + cmbIpFilter.SelectedItem = selectedIp; + } + else if (cmbIpFilter.Items.Count > 0) + { + cmbIpFilter.SelectedIndex = 0; + } + } + catch (Exception ex) + { + System.Diagnostics.Debug.WriteLine($"鍒锋柊IP绛涢夊け璐: {ex.Message}"); + } + } + + /// + /// 鍔犺浇鎶ユ枃鍒楄〃 + /// + private void LoadMessages() + { + var layoutSuspended = false; + try + { + if (dgvMessages == null|| isFormMessageStop) + return; + + var selectedIp = cmbIpFilter?.SelectedItem?.ToString(); + var messages = string.IsNullOrEmpty(selectedIp) || selectedIp == "鍏ㄩ儴" + ? messageService.GetAllMessages() + : messageService.GetMessagesByIp(selectedIp); + + dgvMessages.SuspendLayout(); + layoutSuspended = true; + dgvMessages.Rows.Clear(); + + foreach (var msg in messages) + { + AddMessageRow(msg, false); + } + + UpdateStatistics(messages.Count); + pendingStatsRefresh = false; + } + catch (Exception ex) + { + MessageBox.Show($"鍔犺浇鎶ユ枃澶辫触: {ex.Message}", "閿欒", + MessageBoxButtons.OK, MessageBoxIcon.Error); + } + finally + { + if (layoutSuspended && dgvMessages != null) + { + dgvMessages.ResumeLayout(); + } + } + } + + /// + /// 瀹氭椂鎵归噺鍒锋柊UI锛岄伩鍏嶆瘡鏉℃姤鏂囬兘鎶㈠崰UI绾跨▼ + /// + private void UiFlushTimer_Tick(object sender, EventArgs e) + { + if (!isFormLoaded || isFormMessageStop) + return; + + List batch = null; + lock (pendingMessagesLock) + { + if (pendingMessages.Count == 0) + return; + + int count = Math.Min(UiBatchSize, pendingMessages.Count); + batch = new List(count); + for (int i = 0; i < count; i++) + { + batch.Add(pendingMessages.Dequeue()); + } + } + + if (batch == null || batch.Count == 0) + return; + + dgvMessages.SuspendLayout(); + try + { + var selectedIp = cmbIpFilter?.SelectedItem?.ToString(); + bool displayChanged = false; + + foreach (var message in batch) + { + EnsureIpInFilter(message.IpAddress); + if (string.IsNullOrEmpty(selectedIp) || selectedIp == "鍏ㄩ儴" || selectedIp == message.IpAddress) + { + AddMessageRow(message, true); + displayChanged = true; + } + } + + if (displayChanged) + { + RequestStatisticsRefresh(dgvMessages.Rows.Count); + } + } + finally + { + dgvMessages.ResumeLayout(); + } + } + + /// + /// 缁熻淇℃伅浣庨鍒锋柊锛500ms锛 + /// + private void StatsRefreshTimer_Tick(object sender, EventArgs e) + { + if (!isFormLoaded || isFormMessageStop || !pendingStatsRefresh) + return; + + pendingStatsRefresh = false; + UpdateStatistics(lastDisplayCountForStats); + } + + private void RequestStatisticsRefresh(int displayCount) + { + lastDisplayCountForStats = displayCount; + pendingStatsRefresh = true; + } + + private void EnsureIpInFilter(string ipAddress) + { + if (cmbIpFilter == null || string.IsNullOrWhiteSpace(ipAddress)) + return; + + if (!cmbIpFilter.Items.Contains(ipAddress)) + { + cmbIpFilter.Items.Add(ipAddress); + } + } + + /// + /// 鍚戣〃鏍兼柊澧炰竴鏉℃姤鏂囪锛堟敮鎸佸ご閮ㄦ彃鍏ワ級 + /// + private void AddMessageRow(CommunicationMessage msg, bool insertAtTop = true) + { + if (msg == null || dgvMessages == null) + return; + + DataGridViewRow row; + if (insertAtTop) + { + dgvMessages.Rows.Insert(0, + msg.Timestamp.ToString("HH:mm:ss.fff"), + msg.Direction == MessageDirection.Send ? "鍙戦" : "鎺ユ敹", + msg.IpAddress, + msg.Port, + msg.Length, + msg.RawData, + msg.StationId ?? "-", + msg.Type); + row = dgvMessages.Rows[0]; + } + else + { + var index = dgvMessages.Rows.Add( + msg.Timestamp.ToString("HH:mm:ss.fff"), + msg.Direction == MessageDirection.Send ? "鍙戦" : "鎺ユ敹", + msg.IpAddress, + msg.Port, + msg.Length, + msg.RawData, + msg.StationId ?? "-", + msg.Type); + row = dgvMessages.Rows[index]; + } + + if (msg.Direction == MessageDirection.Send) + { + row.DefaultCellStyle.BackColor = Color.FromArgb(232, 245, 233); + row.DefaultCellStyle.ForeColor = Color.FromArgb(46, 125, 50); + } + else + { + row.DefaultCellStyle.BackColor = Color.FromArgb(227, 242, 253); + row.DefaultCellStyle.ForeColor = Color.FromArgb(13, 71, 161); + } + + while (dgvMessages.Rows.Count > MaxDisplayRows) + { + dgvMessages.Rows.RemoveAt(dgvMessages.Rows.Count - 1); + } + } + + /// + /// 鏇存柊缁熻淇℃伅 + /// + private void UpdateStatistics(int displayCount) + { + try + { + if (lblStatistics == null || messageService == null) + return; + + var allMessages = messageService.GetAllMessages(); + if (allMessages == null) + return; + + var sendCount = allMessages.Count(m => m.Direction == MessageDirection.Send); + var receiveCount = allMessages.Count(m => m.Direction == MessageDirection.Receive); + + lblStatistics.Text = $"鏄剧ず: {displayCount} | 鎬绘暟: {allMessages.Count} | 鍙戦: {sendCount} | 鎺ユ敹: {receiveCount}"; + } + catch (Exception ex) + { + System.Diagnostics.Debug.WriteLine($"鏇存柊缁熻淇℃伅澶辫触: {ex.Message}"); + if (lblStatistics != null) + { + lblStatistics.Text = "缁熻淇℃伅鍔犺浇澶辫触"; + } + } + } + + /// + /// 鏂版姤鏂囨坊鍔犱簨浠跺鐞嗭紙绾跨▼瀹夊叏锛 + /// + private void OnMessageAdded(object sender, CommunicationMessage message) + { + // 濡傛灉绐椾綋杩樻湭鍔犺浇瀹屾垚锛屽拷鐣ユ浜嬩欢 + if (!isFormLoaded || isFormMessageStop) + return; + + try + { + if (message == null) + { + return; + } + lock (pendingMessagesLock) + { + pendingMessages.Enqueue(message); + } + } + catch (Exception ex) + { + System.Diagnostics.Debug.WriteLine($"澶勭悊鏂版姤鏂囧け璐: {ex.Message}"); + } + } + + /// + /// 瑙f瀽鎶ユ枃鏁版嵁 + /// + private void ParseMessage(CommunicationMessage message) + { + if (message == null || txtParsedData == null) + return; + + try + { + var parsed = new System.Text.StringBuilder(); + parsed.AppendLine("=== 鎶ユ枃瑙f瀽 ==="); + parsed.AppendLine($"鏃堕棿: {message.Timestamp:yyyy-MM-dd HH:mm:ss.fff}"); + parsed.AppendLine($"鏂瑰悜: {(message.Direction == MessageDirection.Send ? "鍙戦" : "鎺ユ敹")}"); + parsed.AppendLine($"鍦板潃: {message.IpAddress}:{message.Port}"); + parsed.AppendLine($"绔欑偣: {message.StationId ?? "鏈叧鑱"}"); + parsed.AppendLine($"闀垮害: {message.Length} 瀛楄妭"); + parsed.AppendLine(); + parsed.AppendLine("=== 鍘熷鏁版嵁 (HEX) ==="); + parsed.AppendLine(message.RawData); + // parsed.AppendLine(FormatHexString(message.RawData)); + parsed.AppendLine(); + parsed.AppendLine("=== 鏁版嵁瑙f瀽 ==="); + + // TODO: 鏍规嵁瀹為檯鍗忚杩涜瑙f瀽 + parsed.AppendLine(); + if (message.Direction== MessageDirection.Send) + { + var sendDate = messageService.ParseSendRawData(message.RawData, message.Type); + parsed.AppendLine("绀轰緥瑙f瀽锛"); + parsed.AppendLine($"鍏呯數鎸囦护锛歿sendDate.ChargeCommand}"); + parsed.AppendLine($"鍙戦佺數鍘嬶細{sendDate.SetVoltage}"); + parsed.AppendLine($"鍙戦佺數娴侊細{sendDate.SetCurrent}"); + parsed.AppendLine($"杞﹁締ID锛歿sendDate.CurrentVehicleId}"); + parsed.AppendLine($"杞﹁締鐢甸噺锛歿sendDate.BatteryLevel}"); + parsed.AppendLine($"杞﹁締鐢靛帇锛歿sendDate.CarVoltage}"); + parsed.AppendLine($"杞﹁締鐢垫祦锛歿sendDate.CarCurrent}"); + + } + else + { + + var recDate = messageService.ParseReceiveRawData(message.RawData, message.Type); + string mechanismStatus = (int)recDate.MechanismStatus == 1 ? "浼稿嚭" : (int)recDate.MechanismStatus == 2 ? "缂╁洖" : (int)recDate.MechanismStatus == 3 ? "杩愬姩涓" : recDate.MechanismStatus.ToString(); + parsed.AppendLine("绀轰緥瑙f瀽锛"); + parsed.AppendLine($"鏈烘瀯鐘舵侊細{mechanismStatus}"); + parsed.AppendLine($"瀹炴椂鐢靛帇锛歿recDate.RealTimeVoltage}"); + parsed.AppendLine($"瀹炴椂鐢垫祦锛歿recDate.RealTimeCurrent}"); + parsed.AppendLine($"鍏呯數閲忥細 {recDate.BatteryAH}"); + parsed.AppendLine($"鏄惁鎶ヨ锛歿recDate.HasAlarm}"); + parsed.AppendLine($"鍏呯數鐘舵侊細{recDate.Status.ToString()}"); + + } + + + + txtParsedData.Text = parsed.ToString(); + } + catch (Exception ex) + { + txtParsedData.Text = $"瑙f瀽澶辫触: {ex.Message}"; + } + } + + /// + /// 鏍煎紡鍖栧崄鍏繘鍒跺瓧绗︿覆 + /// + private string FormatHexString(string hexData) + { + if (string.IsNullOrEmpty(hexData)) + return string.Empty; + + var formatted = new System.Text.StringBuilder(); + for (int i = 0; i < hexData.Length; i += 2) + { + if (i > 0 && i % 32 == 0) + formatted.AppendLine(); + else if (i > 0) + formatted.Append(" "); + + if (i + 1 < hexData.Length) + formatted.Append(hexData.Substring(i, 2)); + else + formatted.Append(hexData[i]); + } + return formatted.ToString(); + } + + // ==================== 浜嬩欢澶勭悊 ==================== + + private void cmbIpFilter_SelectedIndexChanged(object sender, EventArgs e) + { + try + { + LoadMessages(); + } + catch (Exception ex) + { + System.Diagnostics.Debug.WriteLine($"绛涢夋敼鍙樺け璐: {ex.Message}"); + } + } + + private void dgvMessages_SelectionChanged(object sender, EventArgs e) + { + try + { + if (dgvMessages.SelectedRows.Count > 0) + { + var row = dgvMessages.SelectedRows[0]; + var rawData = row.Cells[5].Value?.ToString(); + var ipAddress = row.Cells[2].Value?.ToString(); + var port = int.Parse(row.Cells[3].Value?.ToString() ?? "0"); + var timeStr = row.Cells[0].Value?.ToString(); + var directionStr = row.Cells[1].Value?.ToString(); + var stationId = row.Cells[6].Value?.ToString(); + var type = row.Cells[7].Value?.ToString(); + + // 鏋勯犳秷鎭璞$敤浜庤В鏋 + var message = new CommunicationMessage + { + RawData = rawData, + IpAddress = ipAddress, + Port = port, + Direction = directionStr == "鍙戦" ? MessageDirection.Send : MessageDirection.Receive, + StationId = stationId == "-" ? null : stationId, + Length = rawData.Split(' ')?.Length ?? 0, + Type=type, + + }; + + if (DateTime.TryParse(timeStr, out DateTime timestamp)) + { + message.Timestamp = timestamp; + } + + ParseMessage(message); + } + } + catch (Exception ex) + { + System.Diagnostics.Debug.WriteLine($"閫夋嫨鎶ユ枃澶辫触: {ex.Message}"); + } + } + + private void btnRefresh_Click(object sender, EventArgs e) + { + try + { + RefreshIpFilter(); + LoadMessages(); + RequestStatisticsRefresh(dgvMessages?.Rows.Count ?? 0); + } + catch (Exception ex) + { + MessageBox.Show($"鍒锋柊澶辫触: {ex.Message}", "閿欒", + MessageBoxButtons.OK, MessageBoxIcon.Error); + } + } + + private void btnClear_Click(object sender, EventArgs e) + { + try + { + var result = MessageBox.Show( + "纭畾瑕佹竻绌烘墍鏈夋姤鏂囪褰曞悧锛", + "纭娓呯┖", + MessageBoxButtons.YesNo, + MessageBoxIcon.Question); + + if (result == DialogResult.Yes) + { + messageService.Clear(); + RefreshIpFilter(); + LoadMessages(); + if (txtParsedData != null) + { + txtParsedData.Clear(); + } + dgvMessages.Rows.Clear(); + RequestStatisticsRefresh(0); + } + + } + catch (Exception ex) + { + MessageBox.Show($"娓呯┖鎶ユ枃澶辫触: {ex.Message}", "閿欒", + MessageBoxButtons.OK, MessageBoxIcon.Error); + } + } + + private void btnClose_Click(object sender, EventArgs e) + { + this.Close(); + } + + private void CommunicationMonitorForm_FormClosing(object sender, FormClosingEventArgs e) + { + // 鍙栨秷璁㈤槄浜嬩欢 + messageService.MessageAdded -= OnMessageAdded; + uiFlushTimer.Stop(); + uiFlushTimer.Dispose(); + statsRefreshTimer.Stop(); + statsRefreshTimer.Dispose(); + } + + private void button1_Click(object sender, EventArgs e) + { + isFormMessageStop = !isFormMessageStop; + if (sender is Button pauseButton) + { + pauseButton.Text = isFormMessageStop ? "缁х画" : "鏆傚仠"; + } + } + } +} + diff --git a/StandardScene.Core/Charge/CommunicationMonitorForm.resx b/StandardScene.Core/Charge/CommunicationMonitorForm.resx new file mode 100644 index 0000000..a41e003 --- /dev/null +++ b/StandardScene.Core/Charge/CommunicationMonitorForm.resx @@ -0,0 +1,123 @@ +锘 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + True + + \ No newline at end of file diff --git a/StandardScene.Core/Charge/QUICKSTART.md b/StandardScene.Core/Charge/QUICKSTART.md new file mode 100644 index 0000000..3da8731 --- /dev/null +++ b/StandardScene.Core/Charge/QUICKSTART.md @@ -0,0 +1,413 @@ +# 馃殌 鍏呯數妗╃鐞嗙郴缁 - 蹇熷惎鍔ㄦ寚鍗 + +## 馃摝 鏂囦欢娓呭崟 + +宸插垱寤虹殑鏂囦欢锛 +``` +Charge/ +鈹溾攢鈹 ChargeStation.cs # 鍏呯數妗╂暟鎹ā鍨 +鈹溾攢鈹 ChargeStationDataService.cs # 鏁版嵁鏈嶅姟锛堝崟渚嬶級 +鈹溾攢鈹 ChargeStationManagementForm.cs # 绠$悊绐楀彛涓荤被 +鈹溾攢鈹 ChargeStationManagementForm.Designer.cs # 绐楀彛UI璁捐 +鈹溾攢鈹 ChargeStationManagementExample.cs # 绀轰緥浠g爜 +鈹溾攢鈹 README_ChargeStationManagement.md # 璇︾粏浣跨敤璇存槑 +鈹斺攢鈹 QUICKSTART.md # 鏈枃浠 +``` + +## 鈿 5鍒嗛挓蹇熶笂鎵 + +### 姝ラ1锛氬湪涓荤獥鍙f坊鍔犺彍鍗曪紙鎺ㄨ崘鏂瑰紡锛 + +濡傛灉鎮ㄧ殑涓荤獥鍙f湁鑿滃崟鏍忥紝娣诲姞涓涓彍鍗曢」锛 + +```csharp +// 鍦ㄤ富绐楀彛鐨 InitializeComponent() 鎴栨瀯閫犲嚱鏁颁腑娣诲姞 + +// 鏂规硶1: 濡傛灉鏈夊伐鍏锋爮 +var btnChargeManagement = new ToolStripButton("鍏呯數妗╃鐞"); +btnChargeManagement.Click += (s, e) => { + var form = new StandardScene.Charge.ChargeStationManagementForm(); + form.Show(); +}; +toolStrip.Items.Add(btnChargeManagement); + +// 鏂规硶2: 濡傛灉鏈夎彍鍗曟爮 +var menuItemCharge = new ToolStripMenuItem("鍏呯數妗╃鐞(&C)"); +menuItemCharge.Click += (s, e) => { + var form = new StandardScene.Charge.ChargeStationManagementForm(); + form.Show(); +}; +menuStrip.Items.Add(menuItemCharge); + +// 鏂规硶3: 濡傛灉鏈夋寜閽潰鏉 +var btnChargeManagement = new Button +{ + Text = "鍏呯數妗╃鐞", + Size = new Size(120, 40), + Location = new Point(10, 10) +}; +btnChargeManagement.Click += (s, e) => { + var form = new StandardScene.Charge.ChargeStationManagementForm(); + form.Show(); +}; +this.Controls.Add(btnChargeManagement); +``` + +### 姝ラ2锛氬垵濮嬪寲娴嬭瘯鏁版嵁锛堥娆¤繍琛岋級 + +鍦ㄧ▼搴忓惎鍔ㄦ椂鎴栭氳繃鑿滃崟璋冪敤锛 + +```csharp +// 鍦ㄤ富绐楀彛鐨 Load 浜嬩欢鎴栧惎鍔ㄤ唬鐮佷腑 +StandardScene.Charge.ChargeStationManagementExample.InitializeTestData(); +``` + +### 姝ラ3锛氭墦寮绠$悊绐楀彛 + +鐐瑰嚮鎮ㄦ坊鍔犵殑鑿滃崟椤规垨鎸夐挳锛屽嵆鍙墦寮鍏呯數妗╃鐞嗙獥鍙c + +--- + +## 馃幆 闆嗘垚鍒 AbstractChargeMission + +濡傛灉鎮ㄦ兂灏嗗厖鐢垫々鏁版嵁涓庡厖鐢典换鍔″叧鑱旓紝鍦 `AbstractChargeMission.cs` 涓坊鍔狅細 + +### 1. 寮曠敤鍛藉悕绌洪棿 + +```csharp +using StandardScene.Charge; +``` + +### 2. 鍦ㄩ夋嫨鍏呯數绔欑偣鏃朵娇鐢ㄥ厖鐢垫々鏁版嵁 + +```csharp +// 鍦 Execute() 鏂规硶鐨勫厖鐢靛喅绛栭儴鍒 +var dataService = ChargeStationDataService.Instance; + +// 鑾峰彇绌洪棽鐨勫厖鐢垫々 +var idleStations = dataService.GetIdleStations(); + +// 鏍规嵁鍏呯數妗╃殑绔欑偣ID绛涢 +targetPlan = Commons.GetNearestPlan((Car)car, site => + site.fields.ContainsKey("group") && + site.fields.ContainsKey("Charge") && + GetChargeType(car).Contains(site.fields["group"]) && + idleStations.Any(s => s.SiteId == site.id) // 纭繚绔欑偣鏈夌┖闂插厖鐢垫々 +); +``` + +### 3. 鍦ㄥ紑濮嬪厖鐢垫椂鏇存柊鍏呯數妗╃姸鎬 + +```csharp +// 鍦ㄨ溅杈嗗埌杈惧厖鐢电珯鏃 +public override void ArriveAction(Car car, Site site) +{ + var dataService = ChargeStationDataService.Instance; + var station = dataService.GetAllStations() + .FirstOrDefault(s => s.SiteId == site.id && s.Status == ChargeStationStatus.Idle); + + if (station != null) + { + dataService.UpdateStationStatus(station.StationId, ChargeStationStatus.Charging); + car.tags.Add("chargingStationId", station.StationId); + + Diagnosis.Log($"杞﹁締 {car.name} 寮濮嬪湪鍏呯數妗 {station.Name} 鍏呯數", + "Charge", true); + } +} +``` + +### 4. 鍦ㄧ寮鍏呯數绔欐椂鏇存柊鍏呯數妗╃姸鎬 + +```csharp +// 鍦ㄨ溅杈嗙寮鍏呯數绔欐椂 +public override void LeaveAction(Car car, Site site) +{ + if (car.tags.TryGetValue("chargingStationId", out var stationId)) + { + var dataService = ChargeStationDataService.Instance; + dataService.UpdateStationStatus(stationId, ChargeStationStatus.Idle); + car.tags.Remove("chargingStationId"); + + Diagnosis.Log($"杞﹁締 {car.name} 鍏呯數瀹屾垚锛屽厖鐢垫々 {stationId} 鎭㈠绌洪棽", + "Charge", true); + } +} +``` + +--- + +## 馃搳 鍦ㄤ富鐣岄潰鏄剧ず鍏呯數妗╃粺璁 + +鍦ㄤ富绐楀彛娣诲姞瀹炴椂缁熻鏄剧ず锛 + +```csharp +// 娣诲姞涓涓 Timer 瀹氭椂鏇存柊缁熻淇℃伅 +private Timer chargeStationStatusTimer; +private Label lblChargeStationStatus; + +private void InitializeChargeStationMonitor() +{ + // 鍒涘缓鐘舵佹爣绛 + lblChargeStationStatus = new Label + { + Text = "鍏呯數妗: 鍔犺浇涓...", + AutoSize = true, + Location = new Point(10, 10), + Font = new Font("寰蒋闆呴粦", 10F, FontStyle.Bold) + }; + this.Controls.Add(lblChargeStationStatus); + + // 鍒涘缓瀹氭椂鍣紙姣3绉掓洿鏂颁竴娆★級 + chargeStationStatusTimer = new Timer + { + Interval = 3000, + Enabled = true + }; + chargeStationStatusTimer.Tick += UpdateChargeStationStatus; + chargeStationStatusTimer.Start(); +} + +private void UpdateChargeStationStatus(object sender, EventArgs e) +{ + try + { + var dataService = StandardScene.Charge.ChargeStationDataService.Instance; + var stations = dataService.GetAllStations(); + + var idle = stations.Count(s => s.Status == StandardScene.Charge.ChargeStationStatus.Idle); + var charging = stations.Count(s => s.Status == StandardScene.Charge.ChargeStationStatus.Charging); + var fault = stations.Count(s => s.Status == StandardScene.Charge.ChargeStationStatus.Fault); + + lblChargeStationStatus.Text = $"鍏呯數妗: 鎬绘暟 {stations.Count} | " + + $"绌洪棽 {idle} | 鍏呯數涓 {charging} | 鏁呴殰 {fault}"; + + // 鏍规嵁鐘舵佽缃鑹 + if (fault > 0) + lblChargeStationStatus.ForeColor = Color.Red; + else if (idle == 0 && charging > 0) + lblChargeStationStatus.ForeColor = Color.Orange; + else + lblChargeStationStatus.ForeColor = Color.Green; + } + catch (Exception ex) + { + lblChargeStationStatus.Text = $"鍏呯數妗: 鑾峰彇鐘舵佸け璐 - {ex.Message}"; + lblChargeStationStatus.ForeColor = Color.Gray; + } +} +``` + +--- + +## 馃敡 閰嶇疆鍏呯數妗╀笌绔欑偣鐨勬槧灏 + +### 鏂瑰紡1: 鍦ㄧ珯鐐瑰睘鎬т腑娣诲姞鍏呯數妗╃紪鍙 + +淇敼鍦板浘绔欑偣鐨 `fields`锛 + +```csharp +// 涓虹珯鐐规坊鍔犲厖鐢垫々缂栧彿 +site.fields.Add("ChargeStationId", "CS20240115123456"); +``` + +### 鏂瑰紡2: 鍦ㄥ厖鐢垫々绠$悊鐣岄潰鐩存帴璁剧疆绔欑偣ID + +鍦ㄥ厖鐢垫々绠$悊绐楀彛涓紝缂栬緫鍏呯數妗╂椂濉啓"绔欑偣ID"瀛楁銆 + +### 鏂瑰紡3: 鑷姩鍏宠仈锛堜唬鐮佸疄鐜帮級 + +```csharp +// 鑷姩灏嗗厖鐢垫々涓庢渶杩戠殑鍏呯數绔欑偣鍏宠仈 +public void AutoAssignStationsToSites() +{ + var dataService = ChargeStationDataService.Instance; + var allStations = dataService.GetAllStations(); + var chargeSites = SimpleLib.GetAllSites() + .Where(s => s.fields.ContainsKey("Charge")) + .ToList(); + + foreach (var station in allStations) + { + if (station.SiteId == null || station.SiteId == 0) + { + // 鏍规嵁鍚嶇О鎴栧叾浠栬鍒欒嚜鍔ㄥ尮閰嶇珯鐐 + var matchedSite = chargeSites.FirstOrDefault(s => + s.fields.ContainsKey("name") && + s.fields["name"].Contains(station.Name) + ); + + if (matchedSite != null) + { + station.SiteId = matchedSite.id; + dataService.UpdateStation(station, out _); + + Diagnosis.Log($"鑷姩鍏宠仈鍏呯數妗 {station.Name} 鍒扮珯鐐 {matchedSite.id}", + "ChargeStation", true); + } + } + } +} +``` + +--- + +## 馃摫 娣诲姞蹇嵎閿 + +涓虹鐞嗙獥鍙f坊鍔犲揩鎹烽敭锛堝湪涓荤獥鍙o級锛 + +```csharp +protected override bool ProcessCmdKey(ref Message msg, Keys keyData) +{ + // Ctrl+C 鎵撳紑鍏呯數妗╃鐞 + if (keyData == (Keys.Control | Keys.C)) + { + var form = new StandardScene.Charge.ChargeStationManagementForm(); + form.Show(); + return true; + } + + return base.ProcessCmdKey(ref msg, keyData); +} +``` + +--- + +## 馃帹 鑷畾涔夌晫闈㈡牱寮 + +濡傛灉闇瑕佽皟鏁寸獥鍙f牱寮忥紝淇敼 `ChargeStationManagementForm.Designer.cs`锛 + +```csharp +// 淇敼绐楀彛澶у皬 +this.Size = new Size(1400, 800); + +// 淇敼鎸夐挳棰滆壊 +btnAdd.BackColor = Color.FromArgb(144, 238, 144); // 娴呯豢鑹 +btnSave.BackColor = Color.FromArgb(135, 206, 250); // 娴呰摑鑹 +btnDelete.BackColor = Color.FromArgb(255, 182, 193); // 娴呯孩鑹 + +// 淇敼瀛椾綋 +this.Font = new Font("寰蒋闆呴粦", 9F); +``` + +--- + +## 馃悰 甯歌闂 + +### Q1: 绐楀彛鎵撲笉寮 +**A**: 妫鏌ユ槸鍚︽纭紩鐢ㄤ簡鍛藉悕绌洪棿锛 +```csharp +using StandardScene.Charge; +``` + +### Q2: 鏁版嵁淇濆瓨澶辫触 +**A**: 纭繚 `Data` 鏂囦欢澶规湁鍐欏叆鏉冮檺锛 +```bash +# Windows +鍙抽敭 Data 鏂囦欢澶 -> 灞炴 -> 瀹夊叏 -> 纭繚褰撳墠鐢ㄦ埛鏈"鍐欏叆"鏉冮檺 +``` + +### Q3: 鎵句笉鍒板厖鐢垫々鏁版嵁 +**A**: 棣栨杩愯鏃堕渶瑕佸垵濮嬪寲鏁版嵁锛 +```csharp +ChargeStationManagementExample.InitializeTestData(); +``` + +### Q4: 鍏呯數妗╃姸鎬佷笉鏇存柊 +**A**: 鎵嬪姩鍒锋柊鏁版嵁锛 +```csharp +ChargeStationDataService.Instance.Reload(); +``` + +--- + +## 馃摎 杩涢樁鍔熻兘 + +### 瀹炴椂鐩戞帶鍏呯數妗╅氫俊鐘舵 + +```csharp +// 瀹氭湡 Ping 鍏呯數妗 IP +private async Task PingChargeStation(string ip, int port) +{ + try + { + using (var client = new System.Net.Sockets.TcpClient()) + { + var result = client.BeginConnect(ip, port, null, null); + var success = result.AsyncWaitHandle.WaitOne(TimeSpan.FromSeconds(3)); + + if (success) + { + client.EndConnect(result); + return true; + } + return false; + } + } + catch + { + return false; + } +} +``` + +### 鍏呯數妗╂暟鎹彲瑙嗗寲 + +```csharp +// 鍦ㄤ富鐣岄潰娣诲姞鍏呯數妗╃姸鎬佸浘琛 +private void DrawChargeStationChart(Graphics g) +{ + var dataService = ChargeStationDataService.Instance; + var stations = dataService.GetAllStations(); + + int x = 10, y = 10, size = 40; + + foreach (var station in stations) + { + Color color = station.Status switch + { + ChargeStationStatus.Idle => Color.Green, + ChargeStationStatus.Charging => Color.Yellow, + ChargeStationStatus.Fault => Color.Red, + ChargeStationStatus.Offline => Color.Gray, + _ => Color.White + }; + + g.FillRectangle(new SolidBrush(color), x, y, size, size); + g.DrawString(station.Name, this.Font, Brushes.Black, x, y + size + 5); + + x += size + 10; + if (x > this.Width - 100) + { + x = 10; + y += size + 30; + } + } +} +``` + +--- + +## 馃帀 瀹屾垚 + +鐜板湪鎮ㄥ凡缁忓畬鎴愪簡鍏呯數妗╃鐞嗙郴缁熺殑闆嗘垚锛 + +**涓嬩竴姝**锛 +1. 鉁 娣诲姞鑿滃崟椤规垨鎸夐挳 +2. 鉁 鍒濆鍖栨祴璇曟暟鎹 +3. 鉁 鎵撳紑绠$悊绐楀彛娴嬭瘯 +4. 鉁 灏嗗厖鐢垫々鏁版嵁闆嗘垚鍒板厖鐢典换鍔 +5. 鉁 娣诲姞瀹炴椂鐩戞帶鍜岀粺璁 + +**闇瑕佸府鍔╋紵** +- 鏌ョ湅 `README_ChargeStationManagement.md` 鑾峰彇璇︾粏鏂囨。 +- 鍙傝 `ChargeStationManagementExample.cs` 鏌ョ湅绀轰緥浠g爜 +- 妫鏌ユ棩蹇椾腑鐨 `ChargeStation` 鏍囩 + +--- + +**鐗堟湰**: 1.0.0 +**鏈鍚庢洿鏂**: 2024-01-15 + + + diff --git a/StandardScene.Core/Charge/README_ChargeStationManagement.md b/StandardScene.Core/Charge/README_ChargeStationManagement.md new file mode 100644 index 0000000..6600042 --- /dev/null +++ b/StandardScene.Core/Charge/README_ChargeStationManagement.md @@ -0,0 +1,310 @@ +# 鍏呯數妗╃鐞嗙郴缁熶娇鐢ㄨ鏄 + +## 馃搵 姒傝堪 + +鍏呯數妗╃鐞嗙郴缁熸槸涓涓熀浜 WinForms 鐨勫彲瑙嗗寲绠$悊宸ュ叿锛岀敤浜庣鐞 AGV 绯荤粺涓殑鍏呯數妗╄澶囥 + +## 馃殌 蹇熷紑濮 + +### 鎵撳紑绠$悊绐楀彛 + +```csharp +// 鍦ㄤ唬鐮佷腑鎵撳紑鍏呯數妗╃鐞嗙獥鍙 +var form = new StandardScene.Charge.ChargeStationManagementForm(); +form.ShowDialog(); + +// 鎴栬呭湪鎸夐挳鐐瑰嚮浜嬩欢涓 +private void btnOpenChargeManagement_Click(object sender, EventArgs e) +{ + var form = new StandardScene.Charge.ChargeStationManagementForm(); + form.Show(); +} +``` + +### 娣诲姞鑿滃崟椤癸紙鎺ㄨ崘锛 + +鍦ㄤ富绐楀彛鐨勮彍鍗曟爮涓坊鍔狅細 + +```csharp +// 鍦ㄤ富绐楀彛鐨勫垵濮嬪寲浠g爜涓 +var menuItem = new ToolStripMenuItem("鍏呯數妗╃鐞"); +menuItem.Click += (s, e) => { + var form = new StandardScene.Charge.ChargeStationManagementForm(); + form.Show(); +}; +// 灏 menuItem 娣诲姞鍒颁富鑿滃崟 +``` + +## 馃摉 鍔熻兘璇存槑 + +### 1. 鍏呯數妗╁垪琛紙宸︿晶闈㈡澘锛 + +#### 鍔熻兘鐗规 +- **瀹炴椂鏄剧ず**锛氭樉绀烘墍鏈夊厖鐢垫々鐨勮缁嗕俊鎭 +- **棰滆壊鏍囪瘑**锛 + - 馃煝 **缁胯壊**锛氬厖鐢典腑 + - 馃敶 **绾㈣壊**锛氭晠闅 + - 鈿 **鐏拌壊**锛氱绾 + - 鈿 **鐧借壊**锛氱┖闂/鍏朵粬鐘舵 +- **鎼滅储鍔熻兘**锛氭敮鎸佹寜缂栧彿銆佸悕绉般両P鍦板潃鎼滅储 +- **鍙屽嚮缂栬緫**锛氬弻鍑诲垪琛ㄩ」鍙揩閫熺紪杈 + +#### 鍒楄〃瀛楁 +| 瀛楁 | 璇存槑 | 绀轰緥 | +|-----|------|------| +| 缂栧彿 | 鍏呯數妗╁敮涓鏍囪瘑 | CS20240115123456 | +| 鍚嶇О | 鍏呯數妗╁悕绉 | 1鍙峰厖鐢垫々 | +| IP鍦板潃 | 璁惧IP | 192.168.1.100 | +| 绔彛 | 閫氫俊绔彛 | 502 | +| 鐢靛帇(V) | 棰濆畾鐢靛帇 | 220.0 | +| 鐢垫祦(A) | 棰濆畾鐢垫祦 | 32.0 | +| 鍔熺巼(W) | 璁$畻鍔熺巼 | 7040.0 | +| 鐘舵 | 褰撳墠鐘舵 | 绌洪棽/鍏呯數涓/鏁呴殰 | +| 鍚敤 | 鏄惁鍚敤 | 鏄/鍚 | +| 绔欑偣ID | 鍏宠仈绔欑偣 | 1001 | +| 澶囨敞 | 澶囨敞淇℃伅 | 鍗楀尯1鍙峰厖鐢垫々 | + +### 2. 鍏呯數妗╃紪杈戯紙鍙充晶闈㈡澘锛 + +#### 蹇呭~瀛楁 +- 鉁 **缂栧彿**锛氳嚜鍔ㄧ敓鎴愶紙鏍煎紡锛欳S+鏃堕棿鎴+闅忔満鏁帮級 +- 鉁 **鍚嶇О**锛氬厖鐢垫々鍚嶇О锛屼究浜庤瘑鍒 +- 鉁 **IP鍦板潃**锛氳澶嘔P锛屽繀椤讳负鏈夋晥IP鏍煎紡 +- 鉁 **绔彛**锛氶氫俊绔彛锛1-65535锛 +- 鉁 **鐢靛帇**锛氶瀹氱數鍘嬶紙0-1000V锛 +- 鉁 **鐢垫祦**锛氶瀹氱數娴侊紙0-500A锛 + +#### 閫夊~瀛楁 +- 馃搶 **鐘舵**锛氱┖闂/鍏呯數涓/鏁呴殰/绂荤嚎/缁存姢涓/棰勭害涓 +- 馃搶 **鍚敤**锛氭槸鍚﹀惎鐢ㄨ鍏呯數妗 +- 馃搶 **绔欑偣ID**锛氬叧鑱旂殑绔欑偣缂栧彿锛堜笌鍦板浘绔欑偣鍏宠仈锛 +- 馃搶 **澶囨敞**锛氶澶栬鏄庝俊鎭 + +#### 鑷姩璁$畻 +- 鈿 **鍔熺巼**锛氳嚜鍔ㄨ绠楋紙鐢靛帇 脳 鐢垫祦锛 + +### 3. 鎿嶄綔鎸夐挳 + +#### 鍙充晶缂栬緫鍖 +- 馃啎 **鏂板**锛氭竻绌鸿〃鍗曪紝鍑嗗娣诲姞鏂板厖鐢垫々 +- 馃捑 **淇濆瓨**锛氫繚瀛樺綋鍓嶅厖鐢垫々淇℃伅锛堟柊澧炴垨鏇存柊锛 +- 馃棏锔 **鍒犻櫎**锛氬垹闄ゅ綋鍓嶉変腑鐨勫厖鐢垫々 +- 鉂 **鍙栨秷**锛氭竻绌鸿〃鍗 + +#### 宸︿晶鍒楄〃鍖 +- 馃攧 **鍒锋柊**锛氶噸鏂板姞杞芥暟鎹 +- 馃摛 **瀵煎嚭**锛氬鍑哄厖鐢垫々鏁版嵁涓 JSON 鎴 CSV 鏂囦欢 + +## 馃敀 鏁版嵁楠岃瘉瑙勫垯 + +### IP鍦板潃楠岃瘉 +``` +鉁 鏈夋晥锛192.168.1.100, 10.0.0.1, 172.16.0.1 +鉂 鏃犳晥锛192.168.1, 256.1.1.1, abc.def.ghi.jkl +``` + +### 绔彛楠岃瘉 +``` +鉁 鏈夋晥锛502, 8080, 1234 +鉂 鏃犳晥锛0, 70000, -1 +``` + +### 鐢靛帇楠岃瘉 +``` +鉁 鏈夋晥锛220V, 380V, 110V +鉂 鏃犳晥锛-10V, 1500V, 0V +``` + +### 鐢垫祦楠岃瘉 +``` +鉁 鏈夋晥锛32A, 16A, 63A +鉂 鏃犳晥锛-5A, 600A, 0A +``` + +### 鍞竴鎬ч獙璇 +- 鉂 缂栧彿涓嶈兘閲嶅 +- 鉂 IP鍦板潃+绔彛缁勫悎涓嶈兘閲嶅 + +## 馃捑 鏁版嵁瀛樺偍 + +### 瀛樺偍浣嶇疆 +``` +椤圭洰鏍圭洰褰/Data/ChargeStations.json +``` + +### 鏁版嵁鏍煎紡 +```json +[ + { + "StationId": "CS20240115123456", + "Name": "1鍙峰厖鐢垫々", + "IpAddress": "192.168.1.100", + "Port": 502, + "Voltage": 220.0, + "Current": 32.0, + "Status": 0, + "Enabled": true, + "SiteId": 1001, + "Remarks": "鍗楀尯1鍙峰厖鐢垫々", + "CreatedTime": "2024-01-15T12:34:56", + "ModifiedTime": "2024-01-15T14:20:30" + } +] +``` + +## 馃搳 浠g爜闆嗘垚 + +### 鑾峰彇鍏呯數妗╂暟鎹 + +```csharp +using StandardScene.Charge; + +// 鑾峰彇鏁版嵁鏈嶅姟瀹炰緥 +var dataService = ChargeStationDataService.Instance; + +// 鑾峰彇鎵鏈夊厖鐢垫々 +var allStations = dataService.GetAllStations(); + +// 鑾峰彇绌洪棽鍏呯數妗 +var idleStations = dataService.GetIdleStations(); + +// 鏍规嵁缂栧彿鑾峰彇鍏呯數妗 +var station = dataService.GetStationById("CS20240115123456"); + +// 鑾峰彇鍏呯數涓殑鍏呯數妗╂暟閲 +int chargingCount = dataService.GetChargingCount(); +``` + +### 娣诲姞/鏇存柊鍏呯數妗 + +```csharp +// 鍒涘缓鏂板厖鐢垫々 +var newStation = new ChargeStation +{ + Name = "2鍙峰厖鐢垫々", + IpAddress = "192.168.1.101", + Port = 502, + Voltage = 220.0, + Current = 32.0 +}; + +// 娣诲姞 +if (dataService.AddStation(newStation, out string errorMsg)) +{ + Console.WriteLine("娣诲姞鎴愬姛"); +} +else +{ + Console.WriteLine($"娣诲姞澶辫触: {errorMsg}"); +} + +// 鏇存柊鐘舵 +dataService.UpdateStationStatus("CS20240115123456", ChargeStationStatus.Charging); +``` + +### 鍒犻櫎鍏呯數妗 + +```csharp +// 鍒犻櫎鍏呯數妗 +if (dataService.DeleteStation("CS20240115123456", out string errorMsg)) +{ + Console.WriteLine("鍒犻櫎鎴愬姛"); +} +else +{ + Console.WriteLine($"鍒犻櫎澶辫触: {errorMsg}"); +} +``` + +## 鈿狅笍 娉ㄦ剰浜嬮」 + +1. **鏁版嵁鎸佷箙鍖**锛氭墍鏈夋暟鎹嚜鍔ㄤ繚瀛樺埌 JSON 鏂囦欢锛岄噸鍚悗鏁版嵁涓嶄細涓㈠け +2. **绾跨▼瀹夊叏**锛氭暟鎹湇鍔′娇鐢ㄥ崟渚嬫ā寮忓拰閿佹満鍒讹紝鏀寔澶氱嚎绋嬭闂 +3. **鐘舵佺鐞**锛氬厖鐢典腑鐨勫厖鐢垫々鏃犳硶鍒犻櫎锛岄渶鍏堝仠姝㈠厖鐢 +4. **IP鍐茬獊妫娴**锛氱郴缁熶細鑷姩妫娴婭P鍜岀鍙g殑鍐茬獊 +5. **鏁版嵁澶囦唤**锛氬缓璁畾鏈熷浠 `Data/ChargeStations.json` 鏂囦欢 + +## 馃敡 鎵╁睍鍔熻兘寤鸿 + +### 涓庡厖鐢典换鍔¢泦鎴 + +鍦 `AbstractChargeMission.cs` 涓泦鎴愬厖鐢垫々鏁版嵁锛 + +```csharp +// 鍦ㄥ厖鐢典换鍔′腑鑾峰彇鍏呯數妗╀俊鎭 +private void SelectChargeStation(Car car) +{ + var dataService = ChargeStationDataService.Instance; + var idleStations = dataService.GetIdleStations(); + + if (idleStations.Count > 0) + { + var nearestStation = FindNearestStation(car, idleStations); + + // 鏇存柊鍏呯數妗╃姸鎬 + dataService.UpdateStationStatus( + nearestStation.StationId, + ChargeStationStatus.Reserved + ); + + // 鍒嗛厤杞﹁締鍒板厖鐢垫々 + AssignCarToStation(car, nearestStation); + } +} + +// 鍏呯數瀹屾垚鍚 +private void OnChargeComplete(Car car, ChargeStation station) +{ + var dataService = ChargeStationDataService.Instance; + dataService.UpdateStationStatus( + station.StationId, + ChargeStationStatus.Idle + ); +} +``` + +### 鐩戞帶鍏呯數妗╃姸鎬 + +```csharp +// 瀹氭湡妫鏌ュ厖鐢垫々鍦ㄧ嚎鐘舵 +private void MonitorChargeStations() +{ + var dataService = ChargeStationDataService.Instance; + var stations = dataService.GetAllStations(); + + foreach (var station in stations) + { + if (station.Enabled) + { + bool isOnline = PingStation(station.IpAddress, station.Port); + + var newStatus = isOnline + ? ChargeStationStatus.Idle + : ChargeStationStatus.Offline; + + if (station.Status != newStatus) + { + dataService.UpdateStationStatus(station.StationId, newStatus); + Diagnosis.Log($"鍏呯數妗 {station.Name} 鐘舵佸彉鏇: {newStatus}", + "ChargeStation", true); + } + } + } +} +``` + +## 馃摓 鎶鏈敮鎸 + +濡傛湁闂锛岃妫鏌ワ細 +1. `Data` 鏂囦欢澶规槸鍚︽湁鍐欏叆鏉冮檺 +2. JSON 鏂囦欢鏍煎紡鏄惁姝g‘ +3. 鏃ュ織涓殑閿欒淇℃伅锛堟爣绛撅細`ChargeStation`锛 + +--- + +**鐗堟湰**: 1.0.0 +**鏈鍚庢洿鏂**: 2024-01-15 +**浣滆**: MDCS System + + + diff --git a/StandardScene.Core/Charge/StandardChargeMission.cs b/StandardScene.Core/Charge/StandardChargeMission.cs new file mode 100644 index 0000000..22d8820 --- /dev/null +++ b/StandardScene.Core/Charge/StandardChargeMission.cs @@ -0,0 +1,785 @@ +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 +{ + /// + /// 鏍囧噯鍏呯數杩涚▼鐘舵 + /// + public class StandardChargeMissionStatus : AbstractChargeMissionStatus + { + /// + /// 鏄惁灞忚斀鍏呯數妗╀氦浜掞紙true=灞忚斀锛宖alse=鍏佽锛 + /// + public bool ShieldInterLock = false; + } + + /// + /// 鏍囧噯鍏呯數杩涚▼ + /// 璐熻矗鍏呯數妗╃殑鍒濆鍖栥侀氳绠$悊鍜屽厖鐢典笟鍔¢昏緫澶勭悊 + /// + [MissionType(Name = "鍏呯數杩涚▼", editor = typeof(StandardChargeMission))] + [I18N.DocumentTranslation(Name = "Charge Mission", locale = "en")] + public class StandardChargeMission : AbstractChargeLogiceMission + { + #region 瀛楁鍜屽睘鎬 + + /// + /// 鍏呯數绔欏瓧鍏 Key=绔欑偣ID, Value=鍏呯數绔欏璞 + /// + [JsonIgnore] + public Dictionary ChargeStations; + + /// + /// 杩涚▼鐘舵 + /// + public override MissionStatus status { get; set; } = new StandardChargeMissionStatus(); + + /// + /// 杩涚▼鏄惁宸插惎鍔 + /// + [JsonIgnore] + public bool myStarted = false; + + /// + /// 鏃堕棿鍚屾鏈嶅姟鏄惁宸插惎鍔 + /// + [JsonIgnore] + public bool tsStarted = false; + + /// + /// 鍏呯數澶勭悊绾跨▼ + /// + [JsonIgnore] + private Thread ChargeThread; + + /// + /// 褰撳墠姝e湪鍏呯數鐨勮溅杈 Key=杞﹁締ID, Value=鍏呯數娆℃暟 + /// + [JsonIgnore] + public Dictionary inChargeCar = new(); + + /// + /// 涓婁竴娆″厖鐢电殑杞﹁締璁板綍 + /// + [JsonIgnore] + public Dictionary lastinChargeCar = new(); + + /// + /// 鍏呯數寮濮嬫椂闂 Key=杞﹁締ID, Value=寮濮嬫椂闂 + /// + [JsonIgnore] + public Dictionary BeginTime = new(); + + /// + /// 鍏呯數鏉′欢鐘舵 + /// + [JsonIgnore] + public Dictionary Condition = new(); + + /// + /// 涓婁竴娆″厖鐢垫潯浠剁姸鎬 + /// + [JsonIgnore] + public Dictionary lastCondition = new(); + + /// + /// 鍏呯數瓒呮椂鏃堕棿锛堝皬鏃讹級 + /// + [JsonIgnore] + public double outtimeOfCharge = 0.5; + + /// + /// UDP閫氳鏈嶅姟 + /// + [JsonIgnore] + public ChargeUdpService UdpService; + + #endregion + + #region 杈呭姪鏂规硶 + + /// + /// 鑾峰彇鏈浣庣數閲忚溅杈嗙殑SOC鍊 + /// 鐢ㄤ簬鍏呯數绛栫暐鍒ゆ柇锛屾壘鍒扮郴缁熶腑鐢甸噺鏈浣庣殑绌洪棽杞﹁締 + /// + /// 褰撳墠杞﹁締 + /// 鎵鏈夊厖鐢电珯鐐瑰垪琛 + /// 鏈浣庣數閲忓 + public override float LowerCarSoc(AbstractCar car, List allChargeSite) + { + try + { + // 鏌ユ壘绗﹀悎鏉′欢鐨勬渶浣庣數閲忚溅杈嗭細 + // 1. 杞﹁締鍦ㄦ湁鏁堢珯鐐逛笂锛圙etLastSite != -1锛 + // 2. 杞﹁締鏈夊潗鏍囦俊鎭紙haveCoordination锛 + // 3. 杞﹁締鏈鍗犵敤锛!occupied锛 + // 4. 杞﹁締鏈湪鍏呯數锛!charging锛 + // 5. 杞﹁締涓嶅湪鍏呯數绔欑偣涓 + // 6. 杞﹁締鍦ㄧ嚎 + var lowCar = SimpleLib.GetAllCars() + .OfType() + .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; + } + } + + /// + /// 鑾峰彇杞﹁締鐨勫厖鐢电被鍨 + /// 鏍规嵁杞﹁締瀛楁鍒ゆ柇鏄疐RLD杩樻槸MuXing绫诲瀷 + /// + /// 杞﹁締瀵硅薄 + /// 鍏呯數绫诲瀷瀛楃涓 + 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"; + } + + /// + /// 鍒ゆ柇杞﹁締鏄惁鍦ㄧ嚎 + /// 閫氳繃杞﹁締鏄惁鍦ㄦ湁鏁堢珯鐐逛笂鏉ュ垽鏂 + /// + /// 杞﹁締瀵硅薄 + /// true=鍦ㄧ嚎, false=绂荤嚎 + public override bool IsOnlineCar(Car car) + { + return car.GetLastSite() != -1; + } + + /// + /// 杞﹁締鍒拌揪绔欑偣鏃剁殑澶勭悊 + /// 璁板綍杞﹁締鍒拌揪鍏呯數绔欑偣鐨勬棩蹇 + /// + /// 杞﹁締瀵硅薄 + /// 绔欑偣瀵硅薄 + 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"); + } + } + + /// + /// 杞﹁締绂诲紑绔欑偣鏃剁殑澶勭悊 + /// 璁板綍杞﹁締绂诲紑鍏呯數绔欑偣鐨勬棩蹇 + /// + /// 杞﹁締瀵硅薄 + /// 绔欑偣瀵硅薄 + 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"); + } + } + + /// + /// 绔欑偣绛涢夊櫒 + /// 鍒ゆ柇绔欑偣鏄惁涓哄厖鐢电珯鐐 + /// + /// 绔欑偣ID + /// true=鍏呯數绔欑偣, false=闈炲厖鐢电珯鐐 + public override bool SiteFilter(int siteId) + { + // 濡傛灉鏈睆钄藉厖鐢垫々浜や簰锛岃繑鍥瀎alse + if (((StandardChargeMissionStatus)status).ShieldInterLock) + { + return false; + } + + // 妫鏌ョ珯鐐瑰瓧娈典腑鏄惁鍖呭惈"Charge"鍏抽敭瀛 + var site = SimpleLib.GetSite(siteId); + return site.fields.Keys.ToList().Any(p => p.Contains("Charge")); + } + + #endregion + + #region 鏍稿績涓氬姟鏂规硶 + + /// + /// 鍚姩鍏呯數杩涚▼ + /// 鍒濆鍖栧厖鐢电珯銆佸垱寤洪氳杩炴帴銆佸惎鍔ㄥ厖鐢典笟鍔″惊鐜 + /// + [MethodMember(Name = "鍚姩杩涚▼", Description = "寮濮嬪鐞嗗厖鐢电淮鎶よ繘绋")] + public override void Execute() + { + + + // 闃叉閲嶅鍚姩 + if (myStarted) + { + MessageBox.Show("鍏呯數杩涚▼宸插惎鍔紝涓嶅彲閲嶅鍚姩"); + return; + } + status.status = "宸插惎鍔"; + + + myStarted = true; + int iteration = 0; + ChargeStations = new Dictionary(); + + // 鍒涘缓鍏呯數澶勭悊绾跨▼ + ChargeThread = new Thread(() => + { + ChargeStations = new Dictionary(); + + 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 妫鏌ョ珯鐐规槸鍚﹀凡瀛樺湪锛屼互鍙奍P/绔彛鏄惁鍙樻洿 + if (ChargeStations.ContainsKey(siteId)) + { + var existingStation = ChargeStations[siteId]; + + // 妫鏌P鎴栫鍙f槸鍚﹀彉鏇 + 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); + // 璺ㄧ▼搴忛泦瑙f瀽锛氬厖鐢垫々鍏蜂綋绫诲瀷鍙兘浣嶄簬鍗槦鎻掍欢 dll锛圫tandardScene.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 璁剧疆閫氳绫诲瀷锛堜粠閰嶇疆璇诲彇锛岄粯璁CP锛 + 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: 鍒濆鍖朥DP鏈嶅姟 ==================== + // 濡傛灉鏈変换鎰忓厖鐢垫々浣跨敤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 鏌ユ壘褰撳墠鍦ㄥ厖鐢电珯鐐圭殑杞﹁締 + // 鏉′欢锛氳溅杈嗗湪绔欑偣涓 鎴 姝e湪鑾峰彇绔欑偣閿 鎴 鎸佹湁绔欑偣閿 + 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 鍒ゆ柇杞﹁締鏄惁姝e湪鍏呯數 + var charging = car.tags.Contains("charging"); + + // 3.3.3 妫鏌ュ厖鐢垫潯浠 + // 鏉′欢锛氭湭琚崰鐢 && 鏈幏鍙栧叾浠栭攣 && 姝e湪鍏呯數鏍囪 + 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 disabledSites = new HashSet(); + + 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(); + } + + /// + /// 鍏抽棴鍏呯數杩涚▼ + /// 鍋滄鎵鏈夌浉鍏崇嚎绋 + /// + [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()}"); + } + + } + + /// + /// 鍒囨崲鍏呯數妗╀氦浜掑睆钄界姸鎬 + /// true=灞忚斀浜や簰锛宖alse=鍏佽浜や簰 + /// + [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}"); + } + + /// + /// 鎵撳紑鍏呯數妗╃鐞嗙晫闈 + /// 鐢ㄤ簬閰嶇疆鍜岀洃鎺у厖鐢垫々 + /// + [MethodMember(Name = "鎵撳紑鍏呯數妗╃鐞嗙晫闈", Description = "鎵撳紑鍏呯數妗╃鐞嗙晫闈")] + public void OpenManagementWindow() + { + ChargeStationHelper.OpenManagementWindow(); + } + + #endregion + + #region 杈呭姪宸ュ叿鏂规硶 + + /// + /// 灏嗗厖鐢垫々绫诲瀷鏋氫妇杞崲涓虹被鍨嬪瓧绗︿覆 + /// 鐢ㄤ簬閫氳繃鍙嶅皠鍒涘缓瀵瑰簲鐨勫厖鐢电珯瀵硅薄 + /// + /// 鍏呯數妗╃被鍨嬫灇涓 + /// 鍏呯數绔欑被鍚 + 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"; + } + } + + /// + /// 缁欒溅杈嗕笅鍙戝厖鐢典换鍔 + /// 涓烘祴璇曟垨璋冭瘯鐢ㄩ旓紝鎵嬪姩缁欒溅杈嗘坊鍔爏houldCharge鏍囩 + /// + [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 + + + } +} diff --git a/StandardScene.Core/ChargeStationType/AbstractChargeStation.cs b/StandardScene.Core/ChargeStationType/AbstractChargeStation.cs new file mode 100644 index 0000000..44f51c1 --- /dev/null +++ b/StandardScene.Core/ChargeStationType/AbstractChargeStation.cs @@ -0,0 +1,49 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net; +using System.Text; +using System.Threading.Tasks; +using SimpleLite.RCS; +using SimpleLite.RCS.CarTypes; +using SimpleCore.PropType; + +namespace StandardScene.ChargeStationType +{ + public class AbstractChargeStation + { + public bool IsSafe = true; + public int SiteId { get; set; } + + public string Ip { get; set; } + public int Port { get; set; } + public string CommunicationType { get; set; } + + /// + /// 鍏抽棴褰撳墠閫氳杩炴帴 + /// + public virtual void CloseCommunication() + { + // 榛樿瀹炵幇涓虹┖锛屽瓙绫诲彲閲嶅啓 + } + + /// + /// 鍒涘缓閫氳杩炴帴 + /// + public virtual void CreateCommunication(IPAddress ip, int port) + { + + } + + public virtual void SendToChargeStation(int isCharge,Car car,Site site) + {} + + /// + /// UDP 鎶ユ枃鍥炶皟閽╁瓙锛氶粯璁ょ┖瀹炵幇锛屽叿浣撴々鍨嬫寜闇閲嶅啓銆 + /// 鐢ㄤ簬瑙h ChargeUdpService 瀵瑰叿浣撳厖鐢垫々绫诲瀷鐨 is 鍒ゆ柇锛屼究浜庨┍鍔ㄥ绉昏嚦鍗槦 dll銆 + /// + public virtual void OnUdpMessage(byte[] message) + { + } + } +} diff --git a/StandardScene.Core/Coders/CommonTrackCoders.cs b/StandardScene.Core/Coders/CommonTrackCoders.cs new file mode 100644 index 0000000..db24fe7 --- /dev/null +++ b/StandardScene.Core/Coders/CommonTrackCoders.cs @@ -0,0 +1,111 @@ +using SimpleCore.Compiler; +using SimpleCore.PropType; +using StandardScene.CarTypes; + +namespace StandardScene.Coders +{ + /// + /// 閫氱敤锛堝鑸棤鍏筹級杞ㄩ亾 coder 鍩虹被銆 + /// + /// 鑳屾櫙锛氶伩闅/IO/绾犲亸绛 coder 鍘熶互 [TemplateTrackCoderSettings] 鍐呰仈閲嶅鍦ㄥ悇杞﹀瀷涓娿 + /// 杩欓噷鎶婂畠浠娊绂讳负鍗曚竴鍙鐢ㄥ疄鐜帮紝鍚勮溅鍨嬫敼鐢 [ProgramTrackCoderSettings] 鎸夊悇鑷 priority 寮曠敤銆 + /// + /// 绛変环鎬э細鍐呴儴浠嶈蛋鍐呮牳 Template 鏈哄埗锛圥rogramCoderHelper.PrepareTrackEngine + Topaz 姹傚硷級锛 + /// useVerb / templateString 涓庡師妯℃澘閫愬瓧涓鑷达紱杩欎簺 coder 浠呭紩鐢 Basic 瀛楁锛屾晠缁熶竴鐢 BasicXxxFields锛 + /// 涓庡師鍏堜紶杞﹀瀷 Fields 鍦ㄢ滄ā鏉挎墍寮曠敤瀛楁鈥濅笂琛屼负涓鑷淬俻riority 鐢卞紩鐢ㄦ柟杞﹀瀷鐗规ф寚瀹氾紝鎵ц椤哄簭涓嶅彉銆 + /// + public abstract class CommonTemplateTrackCoder : ITrackCoder + { + protected abstract string UseVerb { get; } + protected abstract string TemplateString { get; } + + // 榛樿浠呯敤 Basic 瀛楁琚嬶紱涓埆 coder锛堝閬块殰鍚 ChangeAvoidanceParam 鏍囧織浣嶏級鍙鍐欑珯鐐瑰瓧娈佃銆 + protected virtual System.Type SiteFieldsType => typeof(BasicSiteFields); + + public virtual bool toBlock() => false; + + public bool Code(SegmentPlan plan, Track track, Site src, Site dst, int i) + { + var engine = ProgramCoderHelper.PrepareTrackEngine(plan, track, src, dst, i, + carFields: typeof(BasicCarFields), + siteFields: SiteFieldsType, + trackFields: typeof(BasicTrackFields), + planFields: typeof(BasicPlanFields)); + + if (!(engine.ExecuteExpression(UseVerb) is bool ok) || !ok) + return false; + + plan.codeArr[i] += (string)engine.ExecuteExpression("`" + TemplateString + "`"); + return false; + } + } + + // 鍘 useVerb: track.LidarArea != -2 + public class LidarAreaSwitchCoder : CommonTemplateTrackCoder + { + protected override string UseVerb => "track.LidarArea != -2"; + protected override string TemplateString => + "agv.Queue(()=>{},()=>{ agv.SwitchLidarArea(${track.LidarArea}); });"; + } + + // 鍘熸棤 useVerb锛堢瓑浠 true锛屾瘡娈垫墽琛岋級 + public class AvoidanceDistanceCoder : CommonTemplateTrackCoder + { + protected override string UseVerb => "true"; + protected override string TemplateString => + "agv.Queue(()=>{},()=>{ agv.ChangeAvoidanceDistance(${track.StopDistance},${track.SlowDistance}); });"; + } + + // 鍘 useVerb: track.IOArea != -1 + public class IoAreaSwitchCoder : CommonTemplateTrackCoder + { + protected override string UseVerb => "track.IOArea != -1"; + protected override string TemplateString => + "agv.Queue(()=>{},()=>{ agv.SwitchIoArea(${track.IOArea}); });"; + } + + // 鍘 useVerb: track.BiasAlarmThresh >0 || track.DthAlarmThresh > 0 + public class TrackingErrThreshCoder : CommonTemplateTrackCoder + { + protected override string UseVerb => "track.BiasAlarmThresh >0 || track.DthAlarmThresh > 0"; + protected override string TemplateString => + "agv.Queue(()=>{},()=>{ agv.ChangeTrackingErrThresh(${track.BiasAlarmThresh},${track.DthAlarmThresh}); });"; + } + + // 閬块殰鍖哄昂瀵稿垏鎹紙4 鍙傦紝鍚腑蹇冪偣锛夌珯鐐瑰瓧娈佃锛氬湪 Basic 鍩虹涓婅ˉ ChangeAvoidanceParam 鏍囧織浣嶃 + // 绛変环浜 MWL/MultiVehicle 鍘熺敤鐨 MultiWheelLifterSiteFields 涓湰 coder 瀹為檯寮曠敤鍒扮殑瀛楁銆 + internal class AvoidanceParamSiteFields : BasicSiteFields + { + public bool ChangeAvoidanceParam = false; + } + + /// + /// 閬块殰鍖哄昂瀵稿垏鎹 coder锛4 鍙傦紝鍚腑蹇冪偣锛夈 + /// 鍚堝苟鑷 MultiWheelLifterCar 涓 MultiVehicleCar 涓や唤**閫愬瓧鐩稿悓**鐨勫唴鑱旀ā鏉匡紙闆惰涓哄彉鏇达級锛 + /// useVerb=dst.ChangeAvoidanceParam==true锛涙ā鏉=ChangeAvoidanceParam(L,W,CenterX,CenterY)銆 + /// 娉細Kiva / Forklift 鐨 2 鍙傚彉浣撹鍚屾枃浠 AvoidanceParamLWCoder锛堟柟妗 B锛4 鍙 flag 鐗 + 2 鍙 L,W 鐗堬級銆 + /// + public class AvoidanceParamCoder : CommonTemplateTrackCoder + { + protected override System.Type SiteFieldsType => typeof(AvoidanceParamSiteFields); + protected override string UseVerb => "dst.ChangeAvoidanceParam==true"; + protected override string TemplateString => + "agv.Queue(()=>{},()=>{ agv.ChangeAvoidanceParam(${dst.CarLength},${dst.CarWidth},${dst.CarCenterX},${dst.CarCenterY}); });"; + } + + /// + /// 閬块殰鍖哄昂瀵稿垏鎹 coder锛2 鍙傦紝鏃犱腑蹇冪偣锛夈 + /// 鍚堝苟鑷 Kiva 涓 Forklift 涓や唤**閫愬瓧鐩稿悓**鐨 2 鍙傚唴鑱旀ā鏉裤 + /// 瑙﹀彂鏉′欢缁熶竴鍙 Forklift 鐨勩屽凡閰嶇疆杞︿綋闀垮銆(dst.CarLength!=-1 && dst.CarWidth!=-1)锛 + /// - Forklift锛歶seVerb/妯℃澘閫愬瓧涓鑷达紝琛屼负涓嶅彉锛 + /// - Kiva锛氬師涓烘棤鏉′欢瑙﹀彂锛岀粺涓鍚庡綋绔欑偣鏈厤缃暱瀹(=-1)鏃朵笉鍐嶄笅鍙 ChangeAvoidanceParam(-1,-1)锛 + /// 灞為鏈熷唴鐨勫畨鍏ㄦ敹鏁涳紙鏂规 B锛岃 StandardScene鎷嗗垎璁″垝.md 搂11.4-2锛夈 + /// 浠呭紩鐢 Basic 瀛楁锛圕arLength/CarWidth 宸插湪 BasicSiteFields锛夛紝鏁呯敤榛樿 BasicSiteFields銆 + /// + public class AvoidanceParamLWCoder : CommonTemplateTrackCoder + { + protected override string UseVerb => "dst.CarLength != -1 && dst.CarWidth != -1"; + protected override string TemplateString => + "agv.Queue(()=>{},()=>{ agv.ChangeAvoidanceParam(${dst.CarLength},${dst.CarWidth}); });"; + } +} diff --git a/StandardScene.Core/CommonTools/AtomicFileUpdateHelper.cs b/StandardScene.Core/CommonTools/AtomicFileUpdateHelper.cs new file mode 100644 index 0000000..0649533 --- /dev/null +++ b/StandardScene.Core/CommonTools/AtomicFileUpdateHelper.cs @@ -0,0 +1,69 @@ +using System; +using System.Collections.Concurrent; +using System.IO; +using SimpleCore.Library; + +namespace StandardScene.CommonTools +{ + /// + /// 鎸夋枃浠惰矾寰勪覆琛屽寲璇诲啓锛屼繚璇侀珮骞跺彂涓嬪鍚屼竴鏂囦欢鐨勬洿鏂板師瀛愩佷笉瑕嗙洊鍏朵粬璁板綍銆 + /// 璋冪敤鏂瑰湪濮旀墭涓畬鎴愨滆褰撳墠鍐呭 鈫 淇敼 鈫 杩斿洖鏂板唴瀹光濓紝鐢辨湰绫昏礋璐e姞閿佷笌鍐欏洖銆 + /// + public static class AtomicFileUpdateHelper + { + private static readonly ConcurrentDictionary PathLocks = new ConcurrentDictionary(StringComparer.OrdinalIgnoreCase); + + /// + /// 瀵规寚瀹氳矾寰勬墽琛屽師瀛愭洿鏂帮細鍦ㄦ寔閿佷笅璇诲彇褰撳墠鏂囦欢鍐呭锛岃皟鐢 update 寰楀埌鏂板唴瀹瑰苟鍐欏洖銆 + /// 涓嶄慨鏀瑰叾浠栬褰曟椂锛屽簲鍦 update 涓粎鍙樻洿闇瑕佸彉鏇寸殑鏉$洰鍚庤繑鍥炲畬鏁村唴瀹广 + /// + /// 鏂囦欢瀹屾暣璺緞 + /// 鎺ユ敹褰撳墠鏂囦欢鏂囨湰锛堣嫢鏂囦欢涓嶅瓨鍦ㄥ垯涓 null锛夛紝杩斿洖瑕佸啓鍥炵殑鏂版枃鏈紱杩斿洖 null 琛ㄧず涓嶅啓鍏 + public static void ExecuteAtomicUpdate(string filePath, Func update) + { + if (string.IsNullOrEmpty(filePath)) + throw new ArgumentNullException(nameof(filePath)); + if (update == null) + throw new ArgumentNullException(nameof(update)); + + var lockObj = PathLocks.GetOrAdd(filePath, _ => new object()); + lock (lockObj) + { + string current = null; + try + { + if (File.Exists(filePath)) + current = File.ReadAllText(filePath); + } + catch (Exception ex) + { + Diagnosis.Log($"AtomicFileUpdate read error: {filePath}, {ex.Message}", "AtomicFileUpdate", true); + throw; + } + + string newContent = update(current); + if (newContent == null) + return; + + var dir = Path.GetDirectoryName(filePath); + if (!string.IsNullOrEmpty(dir)) + Directory.CreateDirectory(dir); + + try + { + var tempPath = filePath + ".tmp"; + File.WriteAllText(tempPath, newContent); + if (File.Exists(filePath)) + File.Replace(tempPath, filePath, null); + else + File.Move(tempPath, filePath); + } + catch (Exception ex) + { + Diagnosis.Log($"AtomicFileUpdate write error: {filePath}, {ex.Message}", "AtomicFileUpdate", true); + throw; + } + } + } + } +} diff --git a/StandardScene.Core/CommonTools/SnowflakeIdGenerator.cs b/StandardScene.Core/CommonTools/SnowflakeIdGenerator.cs new file mode 100644 index 0000000..8d74adc --- /dev/null +++ b/StandardScene.Core/CommonTools/SnowflakeIdGenerator.cs @@ -0,0 +1,128 @@ +using System; +using System.Threading; + +namespace StandardScene.CommonTools +{ + public sealed class SnowflakeIdGenerator + { + // 榛樿璧峰鏃堕棿鎴筹細2026-01-01T00:00:00.000Z锛圲nix 姣锛 + // 濡傛灉浣犲笇鏈涚敓鎴愮殑鏁板瓧鏇寸煭锛屽彲浠ュ湪鏋勯犲嚱鏁伴噷浼犲叆鏇粹滆繎鈥濈殑 _epochMs锛堝缓璁叏绯荤粺缁熶竴锛夈 + private const long DefaultEpochMs = 1767225600000L; + private const int WorkerIdBits = 5; // 鏈哄櫒ID鎵鍗犵殑浣嶆暟 + private const int DatacenterIdBits = 5; // 鏁版嵁涓績ID鎵鍗犵殑浣嶆暟 + private const int MaxWorkerId = -1 ^ (-1 << WorkerIdBits); // 鏈澶ф満鍣↖D + private const int MaxDatacenterId = -1 ^ (-1 << DatacenterIdBits); // 鏈澶ф暟鎹腑蹇僆D + private const int SequenceBits = 12; // 搴忓垪鍙锋墍鍗犵殑浣嶆暟 + private const int WorkerIdShift = SequenceBits; // 鏈哄櫒ID宸︾Щ鐨勪綅鏁 + private const int DatacenterIdShift = SequenceBits + WorkerIdBits; // 鏁版嵁涓績ID宸︾Щ鐨勪綅鏁 + private const int TimestampLeftShift = SequenceBits + WorkerIdBits + DatacenterIdBits; // 鏃堕棿鎴冲乏绉荤殑浣嶆暟 + private const long SequenceMask = -1L ^ (-1L << SequenceBits); // 搴忓垪鍙风殑鏈澶у + + private const string Base62Alphabet = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; + + private readonly object _syncRoot = new object(); + private readonly long _epochMs; + private readonly long _workerId; // 鏈哄櫒ID + private readonly long _datacenterId; // 鏁版嵁涓績ID + private long _sequence; // 搴忓垪鍙 + private long _lastTimestamp = -1L; // 涓婃鐢熸垚ID鐨勬椂闂存埑 + + public SnowflakeIdGenerator(long workerId, long datacenterId, long? epochMs = null) + { + this._epochMs = epochMs ?? DefaultEpochMs; + if (this._epochMs > TimeGen()) + { + throw new ArgumentException("_epochMs cannot be in the future."); + } + if (workerId is > MaxWorkerId or < 0) + { + throw new ArgumentException($"worker Id can't be greater than {MaxWorkerId} or less than 0"); + } + if (datacenterId is > MaxDatacenterId or < 0) + { + throw new ArgumentException($"{datacenterId} can't be greater than {MaxDatacenterId} or less than 0"); + } + this._workerId = workerId; + this._datacenterId = datacenterId; + } + + public long NextId() + { + lock (_syncRoot) + { + long timestamp = TimeGen(); + if (timestamp < _lastTimestamp) + { + // 瀹瑰繊绯荤粺鏃堕挓鍥炴嫧锛氱瓑寰呭埌杩戒笂 _lastTimestamp锛岄伩鍏嶇洿鎺ユ姏寮傚父鎶婁笟鍔℃墦宕┿ + timestamp = TilNextMillis(_lastTimestamp); + } + if (_lastTimestamp == timestamp) + { + _sequence = (_sequence + 1) & SequenceMask; + if (_sequence == 0) + { + timestamp = TilNextMillis(_lastTimestamp); + } + } + else + { + _sequence = 0; + } + _lastTimestamp = timestamp; + long id = ((timestamp - _epochMs) << TimestampLeftShift) + | (_datacenterId << DatacenterIdShift) + | (_workerId << WorkerIdShift) + | _sequence; + return id; + } + } + + /// + /// 鐢熸垚鏇寸煭鐨勫瓧绗︿覆褰㈠紡 ID锛圔ase62 缂栫爜锛夛紝渚夸簬鏄剧ず/瀛樺偍銆 + /// + public string NextIdBase62() + { + ulong value = unchecked((ulong)NextId()); + return ToBase62(value); + } + + private static long TilNextMillis(long lastTimestamp) + { + var spin = new SpinWait(); + long timestamp; + do + { + spin.SpinOnce(); + timestamp = TimeGen(); + } + while (timestamp <= lastTimestamp); + + return timestamp; + } + + private static long TimeGen() + { + return DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(); + } + + private static string ToBase62(ulong value) + { + if (value == 0) + { + return "0"; + } + + // 2^64-1 鐨 base62 鏈澶ч暱搴︿负 11锛堝洜涓 62^11 > 2^64锛夈 + char[] buffer = new char[11]; + int pos = buffer.Length; + while (value > 0) + { + ulong rem = value % 62; + value /= 62; + buffer[--pos] = Base62Alphabet[(int)rem]; + } + + return new string(buffer, pos, buffer.Length - pos); + } + } +} diff --git a/StandardScene.Core/Commons.cs b/StandardScene.Core/Commons.cs new file mode 100644 index 0000000..ebb43a4 --- /dev/null +++ b/StandardScene.Core/Commons.cs @@ -0,0 +1,717 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Numerics; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using System.Windows.Forms; +using SimpleLite.RCS; +using SimpleLite.RCS.CarTypes; +using SimpleLite.CADTools; +using SimpleLite.Props; +using SimpleLite.UI; +using SimpleCore; +using SimpleCore.Compiler; +using SimpleCore.Library; +using SimpleCore.PropType; +using SimpleCore.Traffic; +using StandardScene.InterLock; +using StandardScene.Model; +using static StandardScene.Chained.ChainedDeliveryMission; + +namespace StandardScene +{ + /// + /// 鎻掍欢鏈湴鍖栬缃 + /// + public class CustomOperationsBeforeLoading + { + public static void Set() + { + ///姝婚攣鎻愰啋 + TrafficControl.OnDeadLock = (loopingCar) => + { + try + { + var cars = string.Join(",", loopingCar.Select(p => $"{p.id}")); + var msg = $"灏忚溅:{cars}闂村彂鐢熸閿" + + $"璇峰強鏃朵汉宸ヤ粙鍏ュ鐞!!!!"; + MessageBox.Show(msg); + } + catch { } + }; + } + } + public class NoReflectionApi : Attribute + { + + } + + public class ReflectionApiWithParameter : Attribute + { + public string name; + public string desc; + public string Hint; + } + + public static class Commons + { + public static VehicleStatus GetVehicleStatus(Car car) + { + if(car is DummyCar && car.lstatus.Contains("涓婄嚎")) + return VehicleStatus.Normal; + if (car.lstatus.Contains("姝e父浣嗘湭鍒濆鍖")||car.lstatus.Contains("Normal but not initialized")) + return VehicleStatus.NeedInit; + if (car.lstatus.Contains("姝e父") || car.lstatus.Contains("Normal")||car.lstatus.Contains("涓婄嚎")) + return VehicleStatus.Normal; + if (car.lstatus.Contains("鑷姩椹鹃┒绯荤粺澶辫仈") || car.lstatus.Contains("Autonomous driving system lost")) + return VehicleStatus.Offline; + return VehicleStatus.Unknown; + } + + /// + /// 鑾峰彇杞﹁締鐘舵 + /// + /// 杞﹁締 + /// 鐘舵-閿 + /// 鐘舵-鍊 + public static string GetCarStatus(Car car, string key) + { + string value = "0"; + if (car == null) return value; + if (car.status.enums.TryGetValue(key, out var valueStr)) + value = valueStr; + return value; + } + + /// + /// 娣诲姞鏍囩 + /// + /// + /// + /// + public static void AddOrUpdateTag(T item, string tag, string value) where T : TagSet + { + if (item.Contains(tag)) + item.Remove(tag); + item.Add(tag, value); + } + + /// + /// 鍒犻櫎鏍囩 + /// + /// + /// + /// + public static void DeleteTag(T item, string tag) where T : TagSet + { + if (item.Contains(tag)) + { + item.Remove(tag); + } + } + + /// + /// 娓呯┖鏍囩 + /// + /// + /// + public static void ClearTags(T item) where T : TagSet + { + item.Clear(); + } + + /// + /// 娣诲姞鎴栨洿鏂板瓧娈 + /// + /// + /// + /// + public static void AddOrUpdateCarField(Car car, string field, string value) + { + if (car.fields.ContainsKey(field)) + { + car.fields.Remove(field); + car.fields.Add(field, value); + } + else + car.fields.Add(field, value); + } + + /// + /// 鍒犻櫎瀛楁 + /// + /// + /// + public static void DeleteCarField(Car car, string field) + { + if (car.fields.ContainsKey(field)) + { + car.fields.Remove(field); + } + } + + /// + /// 娓呯┖瀛楁 + /// + /// + public static void ClearCarFields(Car car) + { + car.fields.Clear(); + } + + /// + /// 娣诲姞鎴栨洿鏂皊ite鐨勫瓧娈 + /// + /// + /// + /// + public static void AddOrUpdateSiteField(Site site, string field, string value) + { + if (site.fields.ContainsKey(field)) + { + site.fields.Remove(field); + site.fields.Add(field, value); + } + else + site.fields.Add(field, value); + } + + /// + /// 鍒犻櫎site鐨勫瓧娈 + /// + /// + /// + public static void DeleteSiteField(Site site, string field) + { + if (site.fields.ContainsKey(field)) + { + site.fields.Remove(field); + } + } + + /// + /// 娓呯┖site鐨勫瓧娈 + /// + /// + public static void ClearSiteFields(Site site) + { + site.fields.Clear(); + } + + /// + /// 妫鏌ョ粰瀹氬瓧绗︿覆鏄惁涓哄悎娉曠殑 HTTP/HTTPS URL锛堣嚦灏戝寘鍚崗璁笌涓绘満锛夈 + /// + /// 寰呮牎楠岀殑 URL 瀛楃涓 + /// true 琛ㄧず URL 鍚堟硶 + public static bool IsValidHttpUrl(string url) + { + if (string.IsNullOrWhiteSpace(url)) return false; + if (!Uri.TryCreate(url, UriKind.Absolute, out var uri)) return false; + if (uri.Scheme != Uri.UriSchemeHttp && uri.Scheme != Uri.UriSchemeHttps) return false; + if (string.IsNullOrEmpty(uri.Host)) return false; + return true; + } + + public static double CarValue(Car car, string key) + { + if (car.fields.TryGetValue("electricCurrent", out var strelectricCurrent)) + return double.Parse(strelectricCurrent); + if (key == "Soc" && (car.name.Contains("妯℃嫙") || car.name.ToLower().Contains("sim"))) + { + + if (car.fields.TryGetValue("Soc", out var strSoc)) + return double.Parse(strSoc); + + return 100; + } + + var value = "0"; + if (car.status.enums.TryGetValue(key, out var valueStr)) + value = valueStr; + return double.Parse(value); + } + + /// + /// 娣诲姞鎴栨洿鏂癿ission鐨勫瓧娈 + /// + /// + /// + /// + public static void AddOrUpdateMissionField(Mission mission, string field, string value) + { + if (mission.fields.ContainsKey(field)) + { + mission.fields.Remove(field); + mission.fields.Add(field, value); + } + else + mission.fields.Add(field, value); + } + + /// + /// 鍒犻櫎mission鐨勫瓧娈 + /// + /// + /// + /// + public static void DeleteMissionField(Mission mission, string field) + { + if (mission.fields.ContainsKey(field)) + mission.fields.Remove(field); + } + public static List GetOnlineCars() + { + return SimpleLib.GetAllCars().Where(c => c.GetLastSite() != -1 && Commons.GetVehicleStatus((Car)c)== VehicleStatus.Normal).ToList(); + } + public static bool IsSceneSiteByCode(int[] siteCodes) + { + try + { + foreach (var siteCode in siteCodes) + { + var site = SimpleLib.GetAllSites().FirstOrDefault(s => s.id == siteCode); + if (site == null) return false; + } + } + catch (Exception ex) + { + Console.WriteLine($@"IsSceneSite 鏍¢獙鎶ラ敊=>{ex.Message}"); + return false; + } + return true; + } + + public static readonly object PlanSession = new object(); + + public static int SelectCar(AbstractCar car, bool enableGoingStandbyCar = false, bool needCharge = false, bool checkHoldCar = false) + { + var carHaveCoordination = (Car)car; + var conditions = car.GetLastSite() != -1 && GetVehicleStatus((Car)car)== VehicleStatus.Normal&& + !car.tags.Contains("changePriority") && + !car.tags.Contains("priority") && !car.tags.Contains("occupied") + && !car.tags.Contains("agvOffline") + && car.status.pendingLocks.Length == 0 && !car.tags.Contains("deliver") &&//pendingLocks涓存椂澧炲姞瑙e喅杞︽帴澶氫换鍔¢棶棰 + (!car.tags.Contains("shouldCharge") || needCharge) + && !car.tags.Contains("currentWorkStep") && !car.fields.ContainsKey("StopAccept") && car.tags.Contains("Online"); //宸¤埅浠诲姟鏈畬鎴愮殑杞 + if (car.name.Contains("妯℃嫙")) + { + conditions = car.GetLastSite() != -1 && + (!car.tags.Contains("holdCar") || !checkHoldCar) && !car.tags.Contains("occupied") + + && car.status.pendingLocks.Length == 0 && !car.tags.Contains("deliver") &&//pendingLocks涓存椂澧炲姞瑙e喅杞︽帴澶氫换鍔¢棶棰 + (!car.tags.Contains("shouldCharge") || needCharge) + && !car.tags.Contains("currentWorkStep") && !car.fields.ContainsKey("StopAccept"); //宸¤埅浠诲姟鏈畬鎴愮殑杞 + } + //((carHaveCoordination.haveCoordination || car.name.Contains("妯℃嫙")) && (!car.tags.Contains("shouldCharge") || needCharge)); + if (conditions) return 1; + + if (enableGoingStandbyCar && car.tags.TryGetValue("dest", out var dst) && + int.TryParse(dst, out var dstId) && + SimpleLib.GetSite(dstId).fields.ContainsKey(GetGiveWayType((Car)car))) return 1; + return -1; + } + + private static string GetGiveWayType(Car car) + { + if (car.fields.ContainsKey("mover")) return "standbyMover"; + if (car.fields.ContainsKey("loader")) return "standbyLoader"; + return "giveWay"; + } + public static bool SiteHaveTask(Site site) + { + var siteHaveTask = SimpleLib.GetAllCars().ToList().FindAll(o => + { + if (o.status.pendingLocks.Length > 0) + { + if (o.status.pendingLocks.Contains(site.id)) + { + return true; + } + } + if (o.status.holdingLocks.Length > 0) + { + if (o.status.holdingLocks.Contains(site.id)) + { + return true; + } + } + + + return false; + }).Count != 0; + return siteHaveTask; + } + public static SegmentPlan GetNearestPlan(Car car, Func test, int srcId = -1, bool disableConflict = false) + { + var curRouteLength = float.MaxValue; + SegmentPlan targetPlan = null; + foreach (var site in SimpleLib.GetAllSites()) + { + if (!test(site)) continue; + bool occupied = false; + foreach (var cc in SimpleLib.GetAllCars()) + { + if (cc != car) + { + if (cc.tags.Contains("dest") && cc.tags.IsEqual("dest", site.id.ToString()) || + cc.status.holdingLocks.Contains(site.id)) + occupied = true; + } + } + + if (occupied) continue; + + var mPlan = new SegmentPlan { usingCar = car }; + if (disableConflict) + mPlan.fields["forbid_cross"] = "false"; + try + { + var newRouteLength = mPlan.FindRoute( + SimpleLib.GetSite(srcId == -1 ? car.GetLastSite() : srcId), + SimpleLib.GetSite(site.id)); + + if (newRouteLength < curRouteLength) + { + curRouteLength = newRouteLength; + targetPlan = mPlan; + } + } + catch + { + // ignored + } + } + + return targetPlan; + } + + public static void NearestTask(List deliveries, List runningDeliveries = null) + { + try + { + var curW = float.MaxValue; + var priority = int.MinValue; + Delivery priorityNextDelivery = null; + Delivery deliveryTask = null; + + //杞︿笉鏄┖闂茶溅锛屼絾杩欎釜浠诲姟缁撴潫鐨勬椂鍊欑鍙︿竴涓皢瑕佹墽琛岀殑浠诲姟寰堣繎銆 + //锛堥氬父鏉ヨ鏄悓涓涓伐搴忕殑锛岄櫎闈炲崟绾湴杩旂┖鎵橈紙鎴栬矾绾胯鍒掑け璐ワ紝璺冲埆鐨勪换鍔′簡鏈寜浼樺厛绾ф墽琛岋級鍦ㄤ粨搴擄級 + // 杞︾┖闂 + foreach (var car in SimpleLib.GetAllCars().OfType()) //鎵惧埌閭d釜杞 璺濈寰呮墽琛屼换鍔℃渶杩 + { + DeleteTag(car.tags, "changePriority"); + //杞︾殑鐘舵佹槸OK鐨 + if (car.GetLastSite() == -1 || car.tags.Contains("agvOffline") || car.tags.Contains("occupied")) continue; + //杞﹀凡缁忚鍒嗛厤杩囦换鍔′簡 + if (deliveries.FirstOrDefault(t => t.UsingCar == car) != null) continue; + + int dst = 0; + #region ObsoleteCode + //if (car.tags.ContainsKey("occupied")) //璇存槑鏄鍦ㄦ墽琛屼换鍔$殑杞 + //{ + + // if (car.tags.ContainsKey("dest") + // && deliverys.Where(d => d.usingCar != null && d.usingCar == car) == null //骞朵笖杩欎釜杞︽病鏈夐渶瑕佺瓑寰呯殑浠诲姟鎵ц鐨勪换鍔 + // //杩樺瓨鍦ㄤ竴绉嶆儏鍐佃溅琚変簡浣嗕腑閫旀湁楂樹紭鍏堢骇鐨勪换鍔¤繘鏉 + // ) + // { + // //杩欎釜杞︽鍦ㄦ墽琛岀殑浠诲姟 + // dst = int.Parse(car.tags["dest"]); + // } + // else + // { + // //杩欎釜杞﹀凡缁忔湁闇瑕佺瓑寰呯殑浠诲姟浜嗐 + // continue; + // } + + + //} + //else //杞︾┖闂 + //{ + // dst = car.GetLastSite(); + //} + //鏃犳晥 + #endregion + dst = car.GetLastSite(); + if (dst == -1) + { + //杈撳嚭鏃ュ織 鍙樻洿dst; + dst = car.status.holdingLocks.First(); + Diagnosis.Post($"璋冨害灏忚溅{car.name}:{car.id}鐨凣etLastSite 涓 -1 銆傚彉鏇 holdingLocks.First() 涓哄皬杞﹀垵濮嬬偣{dst}"); + } + //绌洪棽鐨勮溅 //濡傛灉鏈夊涓偅涓鐨勬渶杩 + + foreach (var delivery in deliveries.Where(d => d.UsingCar == null)) + { + //杞︽槸鍚﹁兘鎺ュ埌璇ヤ换鍔° + if (car.fields.ContainsKey("group") && !string.IsNullOrEmpty(car.fields["group"]) && !car.fields["group"].Contains(delivery.Group)) + continue; + + var mPlan = new SegmentPlan { usingCar = car }; + + try + { + var myW = mPlan.FindRoute( + SimpleLib.GetSite(dst), + SimpleLib.GetSite(delivery.Src)); + + //鎵惧埌鏈杩戠殑锛 + if (myW < curW) + { + curW = myW; + //鎵惧埌浼樺厛绾ф渶楂樼殑銆備笖璺濈鏈杩 + if (delivery.Priority >= priority) + { + priorityNextDelivery = delivery; + priority = delivery.Priority; + Diagnosis.Post($"{priorityNextDelivery.Id}/{priorityNextDelivery.Group}/{priority}", " delivery.priority"); + } + + //鎵惧埌璺濈鏈杩戠殑銆 + Delivery nextDelivery = delivery; + + if (nextDelivery != priorityNextDelivery) //骞朵笖杩欎袱涓换鍔′笉鍚 浼樺厛绾ч珮鐨勬瘮杈冭繙 + { + if (runningDeliveries != null) + { + //浼樺厛绾ч珮鐨勫厛鎵ц + Delivery deliveryRunning = null; // runningDeliveries.Where(d => d.group == priorityNextDelivery.group).FirstOrDefault(); + + //鏈夎溅姝e湪鎵ц浼樺厛绾ч珮鐨勪换鍔°傛湁鐨勮瘽灏变笉璁╄溅杩囧幓 + if (deliveryRunning != null) //true + { + Diagnosis.Post($"{deliveryRunning.Id}/{deliveryRunning.UsingCar?.id}/" + + $"{deliveryRunning.Group}/{deliveryRunning.Priority}", "deliveryRunning"); + deliveryTask = nextDelivery; + } + else + { + deliveryTask = priorityNextDelivery; + Diagnosis.Post($"{priorityNextDelivery.Id}/{priorityNextDelivery.UsingCar?.id}/" + + $"{priorityNextDelivery.Group}/{priorityNextDelivery.Priority}", "priorityNextDelivery1"); + + } + } + else + { + deliveryTask = priorityNextDelivery; + Diagnosis.Post($"{priorityNextDelivery.Id}/{priorityNextDelivery.UsingCar?.id}/" + + $"{priorityNextDelivery.Group}/{priorityNextDelivery.Priority}", "priorityNextDelivery2"); + + } + } + else + { + deliveryTask = nextDelivery; + Diagnosis.Post($"{nextDelivery.Id}/{nextDelivery.UsingCar?.id}/" + + $"{nextDelivery.Group}/{nextDelivery.Priority}", "nextDelivery"); + } + } + } + catch (Exception ex) + { + Console.WriteLine(ex.Message + ex.ToString()); + } + } + if (deliveryTask != null) + { + deliveryTask.UsingCar = car; + deliveryTask.Priority = 50; + } + } + } + catch (Exception ex) + { + + Console.WriteLine(ex.Message + ex.ToString()); + } + } + + public static async Task GoSite(AbstractCar car, Site targetSite, int step, string action = "/", bool reverse = false) + { + + try + { + var plan = new SegmentPlan + { + usingCar = car, + fields = + { + ["reverse"] = reverse.ToString(), + ["action"] = action, + ["allow_destination_on_route"] = "true" + } + }; + /* if (stations!=null && stations.Count()>=1) + { + var finishLeftPtlSites = SimpleLib.GetAllSites().Where(site => site.name.Contains("TaskStation")).ToList(); + foreach (var site in finishLeftPtlSites) + { + var stationCode = site.name.Split('-')[1]; + if (stations.Contains(stationCode)) + { + site.fields.Add("askInfons1", "1"); + } + } + }*/ + plan.FindRoute(SimpleLib.GetSite(car.GetLastSite()), targetSite); + //plan.HintNotEnding(); + var program = plan.Compile("move"); + car.tags.Add("occupied", $"go{targetSite.id}"); + Console.WriteLine($">>Script:{program.script}"); + var tsk = program.Queue(); + /* if (stations != null && stations.Count() >= 1) + { + var finishLeftPtlSites = SimpleLib.GetAllSites().Where(site => site.name.Contains("TaskStation")).ToList(); + foreach (var site in finishLeftPtlSites) + { + var stationCode = site.name.Split('-')[1]; + if (stations.Contains(stationCode)) + { + site.fields.Remove("askInfons1"); + } + } + }*/ + await tsk; + car.tags.Remove("occupied"); + } + catch (Exception ex) + { + Console.WriteLine(ex.Message + "閲嶆柊鎵ц"); + Thread.Sleep(3000); + } + } + + public static SegmentPlan GenerateEscapePlan(SegmentPlan dstPlan, Func escapeSiteCondition = null) + { + var curW = float.MaxValue; + var car = dstPlan.usingCar; + + var escapedSites = SimpleLib.GetAllCars().Where(cc => cc.status.escape.Length > 0) + .Select(cc => cc.status.escape.Last()).ToHashSet(); + + SegmentPlan planEsc = null; + foreach (var site in SimpleLib.GetAllSites()) + { + if (site.id == dstPlan.Destination.id || site.id == dstPlan.Source.id) continue; + if (escapedSites.Contains(site.id)) continue; + if (escapeSiteCondition != null && !escapeSiteCondition(site)) continue; + + bool occupied = false; + foreach (var cc in SimpleLib.GetAllCars()) + { + if (cc != car) + { + if (cc.status.holdingLocks.Contains(site.id) || + (cc.tags.Contains("dest") && cc.tags["dest"] == site.id.ToString())) occupied = true; + } + } + + if (occupied) continue; + + TryGeneratePlan(dstPlan.usingCar, dstPlan.Destination, site, ref curW, ref planEsc); + } + + return planEsc; + } + private static void TryGeneratePlan(AbstractCar car, Site srcSite, Site dstSite, ref float curW, ref SegmentPlan generatedPlan) + { + var mPlan = new SegmentPlan { usingCar = car, findLoop = false, fields = new Dictionary() }; + mPlan.fields["forbid_cross"] = "false"; + try + { + var actuallyFindRoute = false; + + var myw = TryGetWeight(srcSite, dstSite); + if (myw < 0) + { + myw = mPlan.FindRoute(srcSite, dstSite); + AddWeight(srcSite, dstSite, myw); + actuallyFindRoute = true; + } + + if (myw < curW) + { + if (!actuallyFindRoute) mPlan.FindRoute(srcSite, dstSite); + curW = myw; + generatedPlan = mPlan; + } + } + catch + { + // ignored + } + } + public static Dictionary<(Site, Site), float> weightDictionary = new(); + public static float TryGetWeight(Site s1, Site s2) + { + lock (weightDictionary) + { + if (weightDictionary.TryGetValue((s1, s2), out var w)) return w; + if (weightDictionary.TryGetValue((s2, s1), out var ww)) return ww; + } + return -1; + } + + public static void AddWeight(Site s1, Site s2, float w) + { + lock (weightDictionary) weightDictionary[(s1, s2)] = w; + } + + + + public static SegmentPlan SimpleToNearestPlan(AbstractCar car, Func test, int srcID = -1) + { + var curw = float.MaxValue; + SegmentPlan targetPlan = null; + var srcSite = SimpleLib.GetSite(srcID == -1 ? car.GetLastSite() : srcID); + + foreach (var site in SimpleLib.GetAllSites()) + { + if (!test(site)) continue; + if (site.id == srcSite.id) continue; + bool occupied = false; + foreach (var cc in SimpleLib.GetAllCars()) + { + // if (cc != car)//褰撳墠 + { + if (cc.status.holdingLocks.Contains(site.id) || + (cc.tags.Contains("dest") && cc.tags["dest"] == site.id.ToString())) occupied = true; + } + } + + if (occupied) continue; + TryGeneratePlan(car, srcSite, site, ref curw, ref targetPlan); + } + + return targetPlan; + } + [DllImport("kernel32.dll")] + static extern IntPtr GetConsoleWindow(); + + [DllImport("user32.dll")] + static extern bool ShowWindow(IntPtr hWnd, int nCmdShow); + + + private static bool ShowConsole = false; + + public static void ShowConsoleFun() + { + ShowConsole = !ShowConsole; + var handle = GetConsoleWindow(); + int n = ShowConsole ? 0 : 5; + Console.WriteLine(n); + ShowWindow(handle, n); + + } + public static void HideConsoleFun() + { + var handle = GetConsoleWindow(); + ShowWindow(handle, 0); + } + } +} diff --git a/StandardScene.Core/Docs/Charge/2.鍏呯數绠$悊璇存槑.doc b/StandardScene.Core/Docs/Charge/2.鍏呯數绠$悊璇存槑.doc new file mode 100644 index 0000000..bf4ee73 Binary files /dev/null and b/StandardScene.Core/Docs/Charge/2.鍏呯數绠$悊璇存槑.doc differ diff --git a/StandardScene.Core/Docs/Charge/AbstractChargeMission浣跨敤鎵嬪唽.pdf b/StandardScene.Core/Docs/Charge/AbstractChargeMission浣跨敤鎵嬪唽.pdf new file mode 100644 index 0000000..18b05e5 Binary files /dev/null and b/StandardScene.Core/Docs/Charge/AbstractChargeMission浣跨敤鎵嬪唽.pdf differ diff --git a/StandardScene.Core/Docs/Charge/NetAssist.exe b/StandardScene.Core/Docs/Charge/NetAssist.exe new file mode 100644 index 0000000..163f4cb Binary files /dev/null and b/StandardScene.Core/Docs/Charge/NetAssist.exe differ diff --git a/StandardScene.Core/Docs/Charge/README.md b/StandardScene.Core/Docs/Charge/README.md new file mode 100644 index 0000000..94a6c69 --- /dev/null +++ b/StandardScene.Core/Docs/Charge/README.md @@ -0,0 +1,987 @@ +# StandardScene - Charge 妯″潡閫昏緫鏂囨。 + +鏈枃妗g敤浜庢⒊鐞 `StandardScene/Charge/` 鍏呯數妗╃鐞嗕笌鍏呯數涓氬姟鐨勬暣浣撻昏緫锛岄噸鐐瑰寘鍚細 + +- 鍏呯數妗╅厤缃紙鏁版嵁妯″瀷涓庢寔涔呭寲锛 +- 閫氫俊鎶ユ枃瑙f瀽涓 `ChargeStation` 鐘舵佽惤搴 +- `StandardChargeMission` 鐨勫厖鐢典笟鍔″惊鐜 +- 鐩稿叧 WinForms 鐣岄潰濡備綍灞曠ず涓庝氦浜 + +--- + +## 1. 鐩綍/妯″潡鑱岃矗閫熻锛圕harge/ 鍐咃級 + +### 鏁版嵁涓庨厤缃眰 +- `ChargeStation.cs`锛氬厖鐢垫々鏁版嵁妯″瀷锛坄ChargeStation`锛夊強鐩稿叧鏋氫妇锛坄ChargeStationStatus`銆乣CommunicationStatus`銆乣ChargeCommandStatus` 绛夛級 +- `ChargeStationDataService.cs`锛歚ChargeStation` 鐨勬寔涔呭寲涓庢煡璇/鏇存柊锛圝SON 鏂囦欢瀛樺偍锛 +- `AlarmConfig.cs`锛氭姤璀﹂厤缃ā鍨嬶紙`AlarmConfig`锛 +- `AlarmConfigDataService.cs`锛氭姤璀﹂厤缃殑鎸佷箙鍖栦笌鏌ヨ/鏇存柊锛圝SON 鏂囦欢瀛樺偍锛 +- `ChargeStrategyConfig.cs`锛氬厖鐢电瓥鐣ラ厤缃ā鍨 +- `ChargeStrategyConfigService.cs`锛氬厖鐢电瓥鐣ラ厤缃殑鎸佷箙鍖栵紙JSON 鏂囦欢瀛樺偍锛 + +### 杩愯鏃朵笌閫氫俊灞 +- `CommunicationMessageService.cs`锛氶氫俊鎶ユ枃鈥滆褰 + 瑙f瀽 + 鏇存柊 ChargeStation鈥濈殑鏍稿績鏈嶅姟 +- `ChargeUdpService.cs`锛歎DP 鐩戝惉鍏ュ彛锛屽皢 UDP 鏀跺埌鐨勬暟鎹浆涓 `CommunicationMessageService.AddReceiveMessage(...)` +- `StandardChargeMission.cs`锛氫富涓氬姟杩涚▼锛堝垵濮嬪寲鍏呯數绔欏疄渚嬨500ms 寰幆涓嬪彂鍏呯數鎸囦护锛 + +### WinForms 鐣岄潰灞 +- `ChargeStationManagementForm.cs`锛氬厖鐢垫々绠$悊绐楀彛锛堝垪琛ㄣ佸鍒犳敼銆佽烦杞叾瀹冪獥鍙o級 +- `ChargeStrategyConfigForm.cs`锛氬厖鐢电瓥鐣ュ弬鏁伴厤缃獥鍙 +- `CommunicationMonitorForm.cs`锛氶氫俊鎶ユ枃鐩戞帶绐楀彛锛堣闃 `MessageAdded` 骞跺埛鏂拌〃鏍硷級 +- `AlarmConfigManagementForm.cs`锛氭姤璀﹂厤缃鐞嗙獥鍙o紙澧炲垹鏀广佺瓫閫変笌鎼滅储锛 + +--- + +## 2. 鏁版嵁妯″瀷锛圕hargeStation / AlarmConfig / 绛栫暐锛 + +### 2.1 `ChargeStation`锛坄Charge/ChargeStation.cs`锛 + +`ChargeStation` 鏄墍鏈夌晫闈㈠睍绀轰笌閫氫俊钀藉簱鐨勬牳蹇冨璞°備笌鏈ā鍧楀己鐩稿叧鐨勫瓧娈靛寘鎷細 + +- 韬唤涓庨厤缃 + - `StationId`锛氬厖鐢垫々缂栧彿锛堢敤浜庡敮涓鏍囪瘑锛孶I 鏍¢獙 1-99锛 + - `Name`銆乣Type`锛堝厖鐢垫々绫诲瀷锛歚FRLDTall` / `FRLDShort` / `MuXing`锛 + - `ChargeMethod`锛堝湴鍏/灏惧厖/渚у厖锛 + - `IpAddress`銆乣Port`锛氶氫俊鍦板潃 + - `SetVoltage`銆乣SetElectricCurrent`锛氳瀹氬 + - `Enabled`锛氭槸鍚﹀惎鐢 + - `GroupCarType`銆乣SiteId`锛氫笌璋冨害绯荤粺绔欑偣閰嶇疆缁戝畾 + +- 閫氫俊涓庣姸鎬侊紙鐢ㄤ簬 UI 灞曠ず锛 + - `Status`锛坄ChargeStationStatus`锛夛細`Idle` / `Charging` / `Fault` / `Battery` + - `CommStatus`锛坄CommunicationStatus`锛夛細UI 涓睍绀虹敤鐨勯氳鐘舵侊紙閫氬父鐢 UI Ping 璁$畻锛 + - `ChargeCommandStatus`锛坄ChargeCommandStatus`锛夛細鏈杩戜竴娆♀滃惎鍔/鍋滄鍏呯數鎸囦护鈥濈殑鐘舵 + - `MechanismStatus`锛氭満鏋勪几缂╃姸鎬 + - `HasAlarm`銆乣AlarmLevel`銆乣AlarmMessage`锛氭姤璀︾浉鍏 + +- 瀹炴椂鏁板 + - `LastSendTime`銆乣LastReceiveTime` + - `RealTimeVoltage`銆乣RealTimeCurrent` + - `BatteryLevel`銆乣CurrentVehicle` + +### 2.2 鏋氫妇鍚箟锛坄ChargeStation.cs`锛 + +涓昏鏋氫妇锛 + +- `ChargeStationStatus`锛氱┖闂/鍏呯數涓/鎶ヨ涓/AGV鐢垫睜宸叉帴鍏 +- `CommunicationStatus`锛氭湭鐭/姝e父/寤惰繜/瓒呮椂/鏂紑/閿欒 +- `ChargeCommandStatus`锛氬仠姝/鍚姩 +- `MechanismStatus`锛氫几鍑/缂╁洖/杩愬姩涓 +- `AlarmLevel`锛氭棤/浣/涓/楂/涓ラ噸 +- `ChargeMethodType`锛氬湴鍏/灏惧厖/渚у厖 + +### 2.3 鎶ヨ閰嶇疆 `AlarmConfig`锛坄Charge/AlarmConfig.cs`锛 + +鎶ヨ閰嶇疆鐢ㄤ簬 UI 绠$悊涓庡睍绀猴紙`AlarmConfigManagementForm` 绠$悊锛夈傚瓧娈靛寘鎷細 + +- `AlarmId`銆乣AlarmCode`銆乣AlarmContent` +- `Level`锛堟姤璀︾骇鍒級銆乣Enabled` +- `Remarks` + +### 2.4 绛栫暐閰嶇疆 `ChargeStrategyConfig`锛坄Charge/ChargeStrategyConfig.cs`锛 + +绛栫暐閰嶇疆鍖呭惈 SOC 闃堝笺佹椂闂村弬鏁般佷互鍙婂紑鍏抽」锛堜緥濡 `AllowInterruptTask`銆乣UseLowerSocForCharge` 绛夛級锛岀敱 `ChargeStrategyConfigForm` 缂栬緫銆佺敱 `ChargeStrategyConfigService` 鎸佷箙鍖栥 + +--- + +## 3. 鎸佷箙鍖栦笌鏈嶅姟灞傦紙DataService锛 + +### 3.1 鍏呯數妗╂暟鎹寔涔呭寲锛歚ChargeStationDataService` + +鍏ュ彛涓庡叧閿兘鍔涳紙鏉ヨ嚜瀹炵幇锛夛細 + +- 鑾峰彇锛歚GetAllStations()`銆乣GetStationById(...)`銆乣GetStationByIp(ip,port)` +- 澧炲姞锛歚AddStation(...)` +- 鏇存柊锛歚UpdateStation(...)`锛堝彲閫 `isSave`锛夈乣UpdateStationStatus(...)` +- 鍒犻櫎锛歚DeleteStation(...)` +- 鍒锋柊锛歚Reload()` + +钀藉簱閫昏緫鐗圭偣锛 + +- 閫氫俊瑙f瀽鍚庝細璋冪敤 `ChargeStationDataService.UpdateStation(station, out errorMessage)`锛屾渶缁堟妸 `ChargeStation` 鏂扮姸鎬佸啓鍥 JSON銆 + +### 3.2 鎶ヨ閰嶇疆鎸佷箙鍖栵細`AlarmConfigDataService` + +鍏ュ彛涓庡叧閿兘鍔涳細 + +- 鑾峰彇锛歚GetAllAlarmConfigs()`銆乣GetAlarmConfig(alarmId)`銆乣GetAlarmConfigByCode(...)` +- 澧炲姞锛歚AddAlarmConfig(...)` +- 鏇存柊锛歚UpdateAlarmConfig(...)` +- 鍒犻櫎锛歚DeleteAlarmConfig(...)` +- 鍒锋柊锛歚Reload()`锛堝疄鐜颁腑涓鑸細閲嶆柊浠庢枃浠跺姞杞斤級 + +--- + +## 4. 閫氫俊鎶ユ枃瑙f瀽涓庤惤搴擄細CommunicationMessageService + +`Charge/CommunicationMessageService.cs` 鏄湰妯″潡鏈鏍稿績鐨勨滄ˉ姊佲濓細 + +1. 鎶婂彂閫/鎺ユ敹鎶ユ枃璁板綍鍒板唴瀛橀槦鍒楋紙`LinkedList`锛 +2. 鏍规嵁鎶ユ枃鍘熷 hex 瀛楃涓蹭笌鍗忚绫诲瀷 `type` 瑙f瀽鍑虹粨鏋勫寲鏁版嵁 +3. 鏇存柊瀵瑰簲鐨 `ChargeStation` 瀛楁 +4. 璋冪敤 `ChargeStationDataService.UpdateStation(...)` 钀藉簱鍒 JSON锛屽苟瑙﹀彂 UI 灞曠ず鏇存柊 + +### 4.1 鎶ユ枃璁板綍涓庤闃 + +- `MessageAdded` 浜嬩欢锛氬綋鏂版姤鏂囧姞鍏ユ椂瑙﹀彂 +- UI 閫氫俊鐩戞帶绐椾綋锛坄CommunicationMonitorForm`锛夎闃呰浜嬩欢锛屽苟鍦 UI 绾跨▼鍒锋柊琛ㄦ牸 + +### 4.2 鍙戦佹姤鏂囪矾寰勶紙AddSendMessage -> 鏇存柊 ChargeCommandStatus 绛夛級 + +- 澶栭儴璋冪敤锛歚AddSendMessage(ipAddress, port, rawData, type, stationId?)` +- 鍐呴儴娴佺▼锛 + - `ParseSendRawData(rawData, type)` 瑙f瀽 + - `UpdateStationFromSendData(station, parsedData)` 鏇存柊锛 + - `LastSendTime = SendTime` + - 鏍规嵁 `ChargeCommand`锛堝惎鍔/鍋滄锛夋洿鏂 `ChargeCommandStatus` + - 鏇存柊 `BatteryLevel` 涓 `CurrentVehicle` + - `ChargeStationDataService.UpdateStation(station, out errorMessage)` 钀藉簱 + +### 4.3 鎺ユ敹鎶ユ枃璺緞锛圓ddReceiveMessage -> 鏇存柊鐘舵/鏈烘瀯/鍛婅锛 + +- 澶栭儴璋冪敤锛歚AddReceiveMessage(ipAddress, port, rawData, type, stationId?)` +- 鍐呴儴娴佺▼锛 + - `ParseReceiveRawData(rawData, type)` 瑙f瀽 + - `UpdateStationFromReceiveData(station, parsedData)` 鏇存柊锛 + - `LastReceiveTime` + - `MechanismStatus`銆乣RealTimeVoltage`銆乣RealTimeCurrent` + - `Status`锛坄Idle/Charging/Fault/Battery`锛 + - `HasAlarm`銆乣AlarmLevel`銆乣AlarmMessage` + - `ChargeStationDataService.UpdateStation(...)` 钀藉簱 + +### 4.4 鍗忚绫诲瀷 `type` + +瑙f瀽鍒嗘敮涓父瑙佺被鍨嬬ず渚嬶細 + +- `FRLDShort` +- `FRLDTall` + +涓嶅悓绫诲瀷浼氫娇鐢ㄤ笉鍚岀储寮曚綅缃粠鎶ユ枃瀛楄妭鏁扮粍涓В鏋愬瓧娈点 + +--- + +## 5. 閫氫俊鎺ュ叆鍏ュ彛 + +### 5.1 UDP 鎺ュ叆锛欳hargeUdpService + +`Charge/ChargeUdpService.cs`锛 + +- 鍒涘缓绾跨▼鐩戝惉 UDP锛歚UdpClient(40001)` +- 寰幆鎺ユ敹骞惰浆鍙戯細 + - `CommunicationMessageService.AddReceiveMessage(remoteIp, 40001, hexString, "FRLDShort")` +- 鍚屾椂浼氶氳繃 `SimpleProject.proj.Missions` 鎵惧埌 `StandardChargeMission` 瀹炰緥锛屽苟鍦 `chargeMission.ChargeStations` 涓寜 IP 鎵惧埌瀵瑰簲绔欑偣 +- 瀵圭壒瀹氱珯鐐圭被鍨嬶紙渚嬪 `PCBChargeStation`锛夎繘涓姝ユ洿鏂扮珯鐐瑰瓧娈碉紙渚嬪 `IsSafe`銆乣IndexReceive`锛 + +### 5.2 TCP 鎺ュ叆锛氫互 FLChargeStation 涓轰緥锛圕hargeStationType锛 + +浠 `ChargeStationType/FLChargeStation.cs` 涓轰緥锛 + +- `OnPlaintextReceived(...)` 鍦ㄦ敹鍒 TCP 鏄庢枃鍚庯細 + - 鎻愬彇鎶ユ枃瀛楄妭锛堢ず渚嬩腑 `Take(35)`锛 + - 鏇存柊绔欑偣鍐呯殑涓浜涜繍琛屾椂瀛楁锛堜緥濡 `IsSafe`锛 + - 璋冪敤 `CommunicationMessageService.AddReceiveMessage(...)`锛屽苟鎶 `type` 浼犱负瀵瑰簲鍗忚绫诲瀷锛堜緥濡 `"FRLDTall"`锛 + +> 璇存槑锛氬叿浣 TCP 鏂繛/閲嶈繛鏈哄埗鐢卞簳灞 TCP 瀹㈡埛绔笌瀵瑰簲绔欑偣瀹炵幇鍐冲畾锛涙棤璁 TCP/UDP锛屾渶缁堥兘浼氭眹鑱氬埌 `CommunicationMessageService` 瀹屾垚瑙f瀽涓庤惤搴撱 + +--- + +## 6. 杩愯鏃跺厖鐢典笟鍔″惊鐜細StandardChargeMission + +`Charge/StandardChargeMission.cs` 璐熻矗鎶娾滆皟搴︾郴缁熶腑鐨勮溅鐨勭姸鎬 + 鍏呯數绛栫暐 + 绔欑偣閰嶇疆鈥濈粍鍚堟垚鍛ㄦ湡鎬х殑鍏呯數鎸囦护涓嬪彂銆 + +### 6.1 鍒濆鍖栧厖鐢垫々瀹炰緥锛堝垱寤 station 瀵硅薄锛 + +鍏抽敭姝ラ锛堟潵鑷疄鐜扮墖娈碉級锛 + +1. 閬嶅巻绯荤粺 `Site` 涓甫鏈 `fields["Charge"]` 鐨勭珯鐐癸紝鏋勫缓 station 閰嶇疆 +2. 鏍规嵁 `ChargeStationType` 浣跨敤鍙嶅皠鍒涘缓 `AbstractChargeStation` 瀹炰緥 +3. 缁欑珯鐐瑰璞¤祴鍊硷細 + - `SiteId`銆乣Ip`銆乣Port` + - `CommunicationType` + - 绀轰緥锛歚FRLDShort` 鏃惰缃负 `"UDP"`锛涘惁鍒欎娇鐢ㄩ厤缃腑鐨 `CommunicationType`锛堥粯璁よ蛋 TCP锛 +4. 璋冪敤 `chargeStation.CreateCommunication(ipAddress, port)` 寤虹珛閫氫俊閫氶亾 +5. 鎶婄珯鐐瑰璞″姞鍏 `ChargeStations` 瀛楀吀锛歚Dictionary` + +濡傛灉瀛樺湪浠讳綍 UDP 绔欑偣锛屼細鍒涘缓 `UdpService ??= new ChargeUdpService()`銆 + +### 6.2 500ms 涓氬姟寰幆锛堥夋嫨杞﹁締 -> 涓嬪彂鎸囦护锛 + +涓诲惊鐜紙瀹炵幇涓寘鍚 `Thread.Sleep(500)`锛夐昏緫澶ц嚧濡備笅锛 + +1. 瀵规瘡涓 `chargeStationEntry`锛堟寜绔欑偣閬嶅巻锛夛細 + - 閫氳繃 `SimpleLib.GetAllCars()` 鏌ユ壘锛 + - 杞﹁締褰撳墠鎵鍦ㄧ珯鐐 `c.GetLastSite() == siteId` + - 鎴栬溅杈嗘鍦ㄧ珵浜夐攣/鎸佹湁閿侊紙`aquiringLock == siteId` 鎴 `holdingLocks.Contains(siteId)`锛 +2. 鑻ユ壘鍒拌溅杈嗭細 + - 鍒ゆ柇杞﹁締鐘舵侊細`Commons.GetVehicleStatus((Car)car) == VehicleStatus.Normal` + - 鍒ゆ柇鏄惁姝e湪鈥滃厖鐢垫爣璁扳濓紙`car.tags.Contains("charging")`锛 + - 缁撳悎閿佺姸鎬佷笌 tag 鐘舵佽绠 `openCharge`锛0/1锛 +3. 褰撴湭灞忚斀浜や簰锛坄shieldInterLock == false`锛夋椂涓嬪彂鎸囦护锛 + - `chargeStation.SendToChargeStation(openCharge, (Car)car)` + +### 6.3 Stop/ShieldInterLock/绠$悊鐣岄潰鍏ュ彛 + +- `Stop()`锛氫腑姝 mission 绾跨▼锛屽苟瀵规瘡涓 station 璋冪敤 `CloseCommunication()` +- `ShieldInterLock()`锛氬垏鎹⑩滄槸鍚﹀睆钄藉厖鐢垫々浜や簰鈥 +- `OpenManagementWindow()`锛氭墦寮 `ChargeStationHelper.OpenManagementWindow()` + +--- + +## 7. WinForms 鐣岄潰涓庝氦浜掔粏鑺 + +### 7.1 鍏呯數妗╃鐞嗭細ChargeStationManagementForm + +鏂囦欢锛歚Charge/ChargeStationManagementForm.cs` + +#### 鏍稿績灞曠ず鏁版嵁鏉ユ簮 + +- 鍒楄〃鏁版嵁鏉ユ簮锛歚ChargeStationDataService.GetAllStations()` +- UI 渚ч氳鐘舵侊細 + - 鍦 `LoadStations()` 涓姣忎釜 station 鎵ц `Ping.Send(station.IpAddress, 1000)` + - Ping 鎴愬姛鍒 `station.CommStatus = CommunicationStatus.Normal`锛屽惁鍒 `CommunicationStatus.Error` +- 鐢垫皵/杩愯鏃朵俊鎭潵婧愶細 + - `ChargeCommandStatus`銆乣Status`銆乣MechanismStatus`銆乣HasAlarm/AlarmLevel/AlarmMessage`銆乣RealTimeVoltage/Current` 绛夐兘鏉ヨ嚜 `CommunicationMessageService` 瑙f瀽骞惰惤搴撳悗鐨 `ChargeStation` 瀛楁 + +#### 鑷姩鍒锋柊 + +- `autoRefreshTimer.Interval = 3000` +- `AutoRefreshTimer_Tick`锛 + - 淇濆瓨褰撳墠閫変腑琛岀殑 `StationId` + - 璋冪敤 `LoadStations()` 閲嶇粯 + - 鎭㈠閫変腑琛 + +#### 鍏抽敭缂栬緫涓庝繚瀛橀昏緫锛坆tnSave锛 + +- `btnSave.Text == "淇敼"`锛氬厛鍒囨崲涓虹紪杈戞ā寮 `SetEditMode(true)` +- 鏂板/淇濆瓨鏃舵牎楠岋細 + - `StationId` 涓嶈兘涓虹┖涓斿繀椤绘槸 1-99 鑼冨洿鏁存暟 + - 鏂板鏃剁姝㈤噸澶 `StationId` + - `SiteId` 蹇呴』瀛樺湪浜庤皟搴︾郴缁熺珯鐐归泦鍚堬紙`SimpleLib.GetSite((int)numSiteId.Value)`锛 +- 淇濆瓨璋冪敤锛 + - 鏂板锛歚dataService.AddStation(...)` + - 鏇存柊锛歚dataService.UpdateStation(..., isSave:true)` +- 鍚屾鍒拌皟搴︾郴缁 `Site.fields`锛 + - `setVoltage`銆乣setElectricCurrent` + - `group`锛氭牴鎹 `Enabled` 璁剧疆涓 `"绂佺敤"` 鎴 `GroupCarType` + +#### 鍒犻櫎閫昏緫锛坆tnDelete锛 + +- 璋冪敤 `dataService.DeleteStation(stationId, out errorMessage)` +- 鍚屾娓呯悊 `Site.fields`锛 + - 绉婚櫎 `setVoltage`銆乣setElectricCurrent`銆乣Charge`銆乣group` + +#### 鍒楄〃浜や簰 + +- `dgvStations_CellDoubleClick`锛 + - 鏍规嵁 `StationId` 鏌ユ壘 `ChargeStation` + - 璋冪敤 `LoadStationToFields(station)` + - 杩涘叆缂栬緫妯″紡 `SetEditMode(true, true)` + +#### 鍏跺畠绐楀彛鍏ュ彛鎸夐挳 + +- `btnStrategyConfig_Click`锛氭墦寮 `ChargeStrategyConfigForm` +- `btnCommMonitor_Click`锛氭墦寮 `CommunicationMonitorForm` +- `btnAlarmConfig_Click`锛氭墦寮 `AlarmConfigManagementForm` +- `btnExport_Click`锛氬鍑 JSON 鎴 CSV锛堜粠 `GetAllStations()` 璇诲彇锛 + +### 7.2 绛栫暐閰嶇疆锛欳hargeStrategyConfigForm + +鏂囦欢锛歚Charge/ChargeStrategyConfigForm.cs` + +- 鍒濆鍖栵細`config = configService.LoadConfig()` +- 淇濆瓨锛氭妸 UI 鎺т欢鍊煎啓鍏 `ChargeStrategyConfig` 鍚庤皟鐢 `configService.SaveConfig(config)` +- 鎭㈠榛樿锛氳皟鐢 `ChargeStrategyConfig.CreateDefault()` 骞堕噸鏂板姞杞藉埌鐣岄潰 + +### 7.3 閫氫俊鐩戞帶锛欳ommunicationMonitorForm + +鏂囦欢锛歚Charge/CommunicationMonitorForm.cs` + +- 鍒濆鍖栵細 + - `messageService = CommunicationMessageService.Instance` + - 绐椾綋鍔犺浇瀹屾垚鍚庤闃咃細`messageService.MessageAdded += OnMessageAdded` +- 鏂版姤鏂囧埌杈撅細`OnMessageAdded(...)` + - 鑻 `InvokeRequired` 鍒 `BeginInvoke` 鍥 UI 绾跨▼ + - 鏍规嵁褰撳墠 IP 绛涢夋潯浠跺埛鏂版秷鎭垪琛紙璋冪敤 `LoadMessages()`锛 +- 娑堟伅鍒楄〃灞曠ず锛 + - 浠 `messageService.GetAllMessages()` 鎴 `GetMessagesByIp(ip)` 鍙栧嚭鏁版嵁 + - 鏍规嵁 `Direction`锛堝彂閫/鎺ユ敹锛夎缃棰滆壊 +- 缁熻淇℃伅锛 + - `lblStatistics.Text = $"鏄剧ず: {displayCount} | 鎬绘暟: ... | 鍙戦: ... | 鎺ユ敹: ..."` + +### 7.4 鎶ヨ閰嶇疆绠$悊锛欰larmConfigManagementForm + +鏂囦欢锛歚Charge/AlarmConfigManagementForm.cs` + +- 鐣岄潰鍔犺浇锛 + - 鍒濆鍖栫骇鍒笅鎷夋涓庣瓫閫変笅鎷夋 + - 璋冪敤 `LoadAlarmConfigs()` +- 鍒楄〃鍔犺浇閫昏緫锛 + - 浠 `AlarmConfigDataService.GetAllAlarmConfigs()` 鑾峰彇鍏ㄩ噺 + - 鎸夌瓫閫夋潯浠讹紙绛夌骇 `cmbLevelFilter`銆佹悳绱㈡ `txtSearch`锛夎繃婊 + - 濉厖 `dgvAlarmConfigs` 骞舵牴鎹 `AlarmLevel` 璁剧疆琛岄鑹 +- 淇濆瓨锛 + - `selectedAlarmConfig == null` -> 鏂板 `dataService.AddAlarmConfig` + - 鍚﹀垯 -> 鏇存柊 `dataService.UpdateAlarmConfig` +- 鍒犻櫎锛 + - `dataService.DeleteAlarmConfig(selectedAlarmConfig.AlarmId, out ...)` +- 鍙屽嚮鍒楄〃锛 + - `dgvAlarmConfigs_CellDoubleClick` 璇诲彇 `AlarmId` 骞跺姞杞藉埌缂栬緫鍖 + +--- + +## 7锛堜唬鐮佷竴鑷存т慨璁級锛歎I 绐椾綋瀵艰埅涓庢洿鏂版祦 + +### 7.1 `ChargeStationManagementForm`锛堝厖鐢垫々绠$悊锛 + +鍏ュ彛/瀵艰埅 + +- 閫氳繃 `ChargeStationHelper.OpenManagementWindow()`锛堝崟渚 `Show()`锛夋垨 `ChargeStationHelper.OpenManagementDialog()`锛坄ShowDialog()`锛夋墦寮銆 +- 绐椾綋鍐呴氳繃鎸夐挳鎵撳紑锛 + - `btnStrategyConfig_Click` -> `ChargeStrategyConfigForm.ShowDialog()` + - `btnCommMonitor_Click` -> `CommunicationMonitorForm.Show()` + - `btnAlarmConfig_Click` -> `AlarmConfigManagementForm.ShowDialog()` + +鏇存柊/鍒锋柊 + +- 鍒楄〃鑷姩鍒锋柊锛歚autoRefreshTimer.Interval = 3000`锛宍AutoRefreshTimer_Tick` 浼氫繚瀛樺綋鍓嶉変腑 `StationId`銆侀噸寤 `dgvStations`锛坄LoadStations()`锛夈佸啀鎭㈠閫変腑琛屻 +- 鍏抽棴绐椾綋锛歚OnFormClosing` 鍋滄骞堕噴鏀 `autoRefreshTimer`銆 +- `LoadStations()` 鐨勭姸鎬佸埛鏂扮偣锛 + - 鏁版嵁锛歚ChargeStationDataService.GetAllStations()` + 鎸 `cmbStatusFilter` 杩囨护銆 + - 閫氳鐘舵侊細閫愪釜瀵圭珯鐐规墽琛 `Ping.Send(station.IpAddress, 1000)`锛屾垚鍔/澶辫触鍒嗗埆鍐欏叆 `station.CommStatus`锛屽啀鍒锋柊琛岄鑹层 +- 鎼滅储/绛涢夛細`txtSearch_TextChanged` 涓 `cmbStatusFilter_SelectedIndexChanged` 閮戒細瑙﹀彂 `ApplyFilters()`锛屾竻绌哄苟閲嶅缓 `dgvStations`锛堝寘鍚棰滆壊瑙勫垯锛夈 +- 鎵嬪姩鍒锋柊锛歚btnRefresh_Click` -> `dataService.Reload()` -> `LoadStations()`銆 + +缂栬緫涓庝繚瀛 + +- 鍙屽嚮鍒楄〃锛歚dgvStations_CellDoubleClick` -> `LoadStationToFields(station)` -> `SetEditMode(false)`锛堟煡鐪嬫ā寮忥紝`btnSave.Text="淇敼"`锛夈 +- `btnSave_Click` 涓ゆ寮忥細 + - `btnSave.Text=="淇敼"`锛氫粎鍒囧埌缂栬緫妯″紡 `SetEditMode(true)`銆 + - 鍚﹀垯鎵ц淇濆瓨锛氭牎楠 `StationId`锛1-99锛夈佹柊澧炴椂鏍¢獙鍞竴鎬с佹牎楠 `SiteId` 瀛樺湪锛岀劧鍚庤皟鐢 `AddStation` / `UpdateStation(..., isSave:true)`銆 + - 淇濆瓨鎴愬姛鍚庡悓姝ヨ皟搴︾郴缁 `Site.fields`锛歚setVoltage`銆乣setElectricCurrent`銆乣group`锛堝惎鐢ㄥ啓 `GroupCarType`锛岀鐢ㄥ啓 `"绂佺敤"`锛夛紝鍐嶅埛鏂板垪琛ㄥ苟娓呯┖缂栬緫鍖恒 +- 鍒犻櫎锛歚btnDelete_Click` 纭鍚 `DeleteStation`锛屽苟鍚屾娓呯悊 `Site.fields`锛坄setVoltage`銆乣setElectricCurrent`銆乣Charge`銆乣group`锛夈 + +### 7.2 `ChargeStrategyConfigForm`锛堝厖鐢电瓥鐣ラ厤缃級 + +鍏ュ彛/瀵艰埅 + +- 閫氬父鐢辩鐞嗙獥浣撴墦寮锛歚ChargeStationManagementForm` 鐨 `btnStrategyConfig_Click` 浣跨敤 `ShowDialog()`銆 + +鏇存柊/鍒锋柊 + +- 鍒濆鍖栵細`configService = ChargeStrategyConfigService.Instance`锛屾瀯閫犳椂 `LoadConfig()` 鎶婃枃浠堕厤缃姞杞藉埌鐣岄潰鎺т欢銆 +- 淇濆瓨/搴旂敤锛歚btnSave_Click` 涓 `btnApply_Click` 閮戒細鍏 `ValidateConfig()` 鏍¢獙闃堝煎叧绯伙紝鍐嶆妸鎺т欢鍊煎啓鍥 `config` 骞惰皟鐢 `configService.SaveConfig(config)`銆 +- 鎭㈠榛樿锛歚btnRestoreDefaults_Click` 纭鍚 `config = ChargeStrategyConfig.CreateDefault()`锛岃皟鐢 `LoadConfig(true)` 鍒锋柊鐣岄潰锛屼絾涓嶈嚜鍔ㄤ繚瀛橈紙鐘舵佹彁绀衡滄湭淇濆瓨鈥濓級銆 +- 鍙栨秷锛歚btnCancel_Click` -> `Close()`銆 + +### 7.3 `CommunicationMonitorForm`锛堥氫俊鐩戞帶锛 + +鍏ュ彛/瀵艰埅 + +- 鐢辩鐞嗙獥浣 `btnCommMonitor_Click` 鎵撳紑锛歚Show()`锛堥潪闃诲锛夈 + +鏇存柊/鍒锋柊锛堜簨浠堕┍鍔級 + +- 鏋勯犱腑鎷垮埌 `messageService = CommunicationMessageService.Instance`锛沗FormClosing` 閫璁 `MessageAdded`銆 +- `CommunicationMonitorForm_Load`锛 + - `InitializeForm()` + `LoadMessages()` 鍚庤缃 `isFormLoaded=true` + - 鍐嶈闃 `messageService.MessageAdded += OnMessageAdded` +- `OnMessageAdded`锛 + - `InvokeRequired` 鏃 `BeginInvoke` 鍥 UI 绾跨▼ + - 鏂 IP 鍒欏埛鏂 `cmbIpFilter`锛坄RefreshIpFilter()`锛 + - 鑻ュ綋鍓嶇瓫閫夊尮閰嶏紙鈥滃叏閮ㄢ濇垨绛変簬褰撳墠娑堟伅 IP锛夊垯璋冪敤 `LoadMessages()` 閲嶅缓娑堟伅鍒楄〃 +- 鎵嬪姩鎿嶄綔锛 + - `cmbIpFilter_SelectedIndexChanged` -> `LoadMessages()` + - `btnRefresh_Click` -> `RefreshIpFilter()` + `LoadMessages()` + - `btnClear_Click`锛氱‘璁 -> `messageService.Clear()` -> 鍒锋柊鍒楄〃骞舵竻绌 `txtParsedData` +- 鍒楄〃閫夋嫨涓庤В鏋愬睍绀猴細 + - `dgvMessages_SelectionChanged` 鏍规嵁鎵閫夎鏋勯犱复鏃 `CommunicationMessage`锛屽啀璋冪敤 `ParseMessage()`锛屽苟灏嗚В鏋愮粨鏋滃啓鍏 `txtParsedData`銆 + +### 7.4 `AlarmConfigManagementForm`锛堟姤璀﹂厤缃鐞嗭級 + +鍏ュ彛/瀵艰埅 + +- 鐢辩鐞嗙獥浣 `btnAlarmConfig_Click` 鎵撳紑锛歚ShowDialog()`銆 + +鏇存柊/鍒锋柊锛堝姞杞 + 绛涢/鎼滅储锛 + +- 鏋勯狅細`dataService = AlarmConfigDataService.Instance`锛屽苟璁㈤槄 `this.Load += AlarmConfigManagementForm_Load`銆 +- `InitializeForm()`锛 + - 鍒濆鍖 `cmbLevel` 涓 `cmbLevelFilter` + - 璋冪敤 `LoadAlarmConfigs()` 鍔犺浇鍒楄〃 + - 璋冪敤 `ClearEditFields()` 鍒濆鍖栫紪杈戝尯锛堥粯璁ゆ柊澧炴侊級 +- `LoadAlarmConfigs()`锛 + - 鏁版嵁婧愶細`dataService.GetAllAlarmConfigs()` + - 杩囨护锛歚cmbLevelFilter`锛堟槧灏勫埌 `AlarmLevel`锛変笌 `txtSearch`锛堝尮閰 `AlarmId/AlarmCode/AlarmContent`锛 + - 濉厖 `dgvAlarmConfigs` 骞舵寜 `AlarmLevel` + `Enabled` 璁剧疆琛岄鑹/鏍峰紡锛屽悓鏃舵洿鏂扮粺璁′笌鏍囬 +- 瀹炴椂鍒锋柊锛歚txtSearch_TextChanged` 涓 `cmbLevelFilter_SelectedIndexChanged` 閮界洿鎺ヨ皟鐢 `LoadAlarmConfigs()`锛沗btnRefresh_Click` 浼 `dataService.Reload()` 鍚庨噸鏂板姞杞姐 + +缂栬緫涓庝繚瀛 + +- 鍙屽嚮鍒楄〃锛歚dgvAlarmConfigs_CellDoubleClick` 璇诲彇 `AlarmId` -> `dataService.GetAlarmConfig(alarmId)` -> `LoadAlarmConfigToFields()`锛堢紪鍙蜂笉鍙紪杈戯紝鍒囦负缂栬緫鎬侊級銆 +- 淇濆瓨锛歚btnSave_Click` 鏍¢獙 `numAlarmCode >= 0`銆乣txtAlarmContent` 闈炵┖锛涙牴鎹槸鍚﹂変腑椤瑰喅瀹 `AddAlarmConfig` 鎴 `UpdateAlarmConfig`锛涙垚鍔熷悗鍒锋柊鍒楄〃骞舵竻绌虹紪杈戝尯銆 +- 鍒犻櫎锛歚btnDelete_Click` 纭鍚 `DeleteAlarmConfig(selectedAlarmConfig.AlarmId)`锛屾垚鍔熷悗鍒锋柊鍒楄〃骞舵竻绌虹紪杈戝尯銆 +- 鍙栨秷/鍏抽棴锛歚btnCancel_Click` 娓呯┖缂栬緫鍖猴紝`btnClose_Click` 鍏抽棴绐椾綋銆 + +--- + +## 8. 鍏抽敭璋冪敤閾撅紙寤鸿鎺掓煡/鐞嗚В鐢級 + +### 8.1 鍛ㄦ湡寰幆涓嬪彂鍏呯數鎸囦护 -> 鍙戦佹姤鏂囪褰 -> ChargeCommandStatus 鏇存柊 + +```mermaid +flowchart TD + A[StandardChargeMission 500ms寰幆] --> B[chargeStation.SendToChargeStation(openCharge, car)] + B --> C[chargeStation 鍐呴儴鏋勯犲彂閫佹姤鏂嘳 + C --> D[CommunicationMessageService.AddSendMessage(...)] + D --> E[ParseSendRawData(type)] + E --> F[UpdateStationFromSendData] + F --> G[ChargeStationDataService.UpdateStation] + G --> H[ChargeStation 瀛楁钀藉簱] + H --> I[ChargeStationManagementForm(3s鍒锋柊) 灞曠ず] +``` + +### 8.2 TCP/UDP 鎺ユ敹鎶ユ枃 -> 瑙f瀽 -> ChargeStation 鐘舵佷笌鍛婅鏇存柊 -> UI 灞曠ず + +```mermaid +flowchart TD + A[TCP 鏀跺埌鏄庢枃 鎴 UDP 鏀跺埌鎶ユ枃] --> B[CommunicationMessageService.AddReceiveMessage(...)] + B --> C[ParseReceiveRawData(type)] + C --> D[UpdateStationFromReceiveData] + D --> E[ChargeStationDataService.UpdateStation] + E --> F[ChargeStation 瀛楁钀藉簱] + F --> G[ChargeStationManagementForm(3s鍒锋柊) 灞曠ず Status/鍛婅/鐢靛帇鐢垫祦] +``` + +### 8.3 閫氫俊鐩戞帶鐣岄潰璁㈤槄鎶ユ枃浜嬩欢 + +```mermaid +flowchart TD + A[CommunicationMessageService.AddMessage/MessageAdded] --> B[CommunicationMonitorForm.OnMessageAdded] + B --> C[BeginInvoke 鍒囧埌UI绾跨▼] + C --> D[LoadMessages 鍒锋柊 dgvMessages] +``` + +--- + +## 9. 甯哥敤璋冭瘯鐐癸紙寤鸿锛 + +- 閫氫俊瑙f瀽钀藉簱锛 + - 鐪 `CommunicationMessageService` 鐨 `UpdateStationFromSendData/ReceiveData` 鏇存柊浜嗗摢浜涘瓧娈 +- UI 灞曠ず锛 + - `ChargeStationManagementForm.LoadStations()` 涓殑 `Ping.Send(...)` 浼氬奖鍝 `CommStatus` 灞曠ず +- 濡傛灉鈥滃垪琛ㄩ噷鐘舵佷笉鍙樷濓細 + - 浼樺厛纭鎶ユ枃鏄惁鐪熺殑杩涘叆 `CommunicationMessageService.AddSendMessage/AddReceiveMessage` + - 鍐嶇‘璁よВ鏋愭槸鍚﹁繑鍥為潪 null锛堣В鏋愬け璐ヤ細鐩存帴 `return null`锛 + +# StandardScene/Charge锛氬厖鐢垫々鏁版嵁妯″瀷涓庢寔涔呭寲锛堜粎鏁版嵁灞傦級 + +鏈〉鑱氱劍 `StandardScene/Charge/` 涓笌鈥滄暟鎹ā鍨 + DataService 鎸佷箙鍖/鏇存柊 API鈥濈浉鍏崇殑閮ㄥ垎锛岃鐩栵細 + +1. `ChargeStation`锛氬厖鐢垫々閰嶇疆/杩愯鏃剁姸鎬佸瓧娈靛惈涔変笌 `JsonIgnore` 鎸佷箙鍖栬竟鐣 +2. `ChargeStationDataService`锛歚Config/ChargeStations.json` 鐨勮鍙/淇濆瓨銆佸鍒犳敼涓庣姸鎬佹洿鏂 +3. `AlarmConfig` 涓 `AlarmConfigDataService`锛歚Config/AlarmConfigs.json` 鐨勮鍙/淇濆瓨銆佸鍒犳敼 + +--- + +## 1. 鏁版嵁妯″瀷锛歚ChargeStation` + +鏂囦欢锛歚Charge/ChargeStation.cs` + +### 1.1 閰嶇疆/璁$畻瀛楁璇存槑锛堟寜 `JsonIgnore` 鍖哄垎锛 + +`ChargeStation` 鐨勪笅鍒楀瓧娈电敤浜庘滃厖鐢垫々閰嶇疆鈥濓紝鍦 JSON 閲屼細琚簭鍒楀寲锛堝嵆锛氭湭鏍囨敞 `JsonIgnore`锛夛細 + +- `StationId`锛氬厖鐢垫々缂栧彿锛堝敮涓鏍囪瘑锛 +- `Name`锛氬厖鐢垫々鍚嶇О +- `Type`锛氬厖鐢垫々绫诲瀷锛坄ChargeStationType`锛 +- `ChargeMethod`锛氬厖鐢垫柟寮忥紙`ChargeMethodType`锛 +- `IpAddress`锛欼P 鍦板潃 +- `Port`锛氱鍙e彿 +- `CommunicationType`锛氶氳绫诲瀷锛堝睘鎬у垵濮嬪间负 `"TCP"`锛屼絾鏋勯犲嚱鏁颁細瑕嗙洊涓 `"UDP"`锛 +- `SetVoltage`锛氶瀹氱數鍘嬶紙V锛 +- `SetElectricCurrent`锛氶瀹氱數娴侊紙A锛 +- `Enabled`锛氭槸鍚﹀惎鐢 +- `GroupCarType`锛氬仠闈犺溅杈嗙被鍨嬶紙`ChargeStationCarType`锛 +- `SiteId`锛氬叧鑱旂珯鐐 ID锛堝彲閫夛級 +- `ShieldSiteMechanismStatus`锛氬睆钄芥満鏋勭姸鎬佷氦浜 +- `Remarks`锛氬娉 +- `CreatedTime`锛氬垱寤烘椂闂 +- `ModifiedTime`锛氭渶鍚庝慨鏀规椂闂 +- `Power`锛氳绠楀睘鎬э紙`SetVoltage * SetElectricCurrent`锛夛紝鏍囨敞浜 `[JsonIgnore]`锛屼笉浼氬啓鍏 JSON + +### 1.2 杩愯鏃剁姸鎬佸瓧娈碉紙涓嶄細琚寔涔呭寲鍒 JSON锛 + +浠ヤ笅瀛楁鏍囨敞浜 `[JsonIgnore]`锛屽洜姝や笉浼氬啓鍏 `Config/ChargeStations.json`锛堥噸鍚悗杩欎簺杩愯鏃剁姸鎬侀氬父浼氫涪澶憋級锛 + +- `RealTimeVoltage`銆乣RealTimeCurrent`锛氬疄鏃剁數鍘/鐢垫祦 +- `LastSendTime`銆乣LastReceiveTime`锛氭渶鍚庡彂閫/鎺ユ敹鏃堕棿 +- `HasAlarm`銆乣AlarmMessage`銆乣AlarmLevel`锛氭姤璀︽爣璁/鎶ヨ鏂囨湰/鎶ヨ绾у埆 +- `CommStatus`銆乣LastCommunicationTime`锛氶氳鐘舵/鏈鍚庨氳鏃堕棿锛堟敞鎰忥細褰撳墠浠g爜閲岄氳鐘舵佸瓧娈电殑鏇存柊璺緞涓嶅湪鏈妭灞曞紑锛 +- `MechanismStatus`锛氭満鏋勪几缂╃姸鎬 +- `CurrentVehicle`锛氬綋鍓嶅厖鐢佃溅杈嗙紪鍙 +- `BatteryLevel`锛氬綋鍓嶇數閲忕櫨鍒嗘瘮 +- `ChargeCommandStatus`锛氬彂閫佸厖鐢垫寚浠ょ姸鎬侊紙鍋滄/鍚姩锛 +- `Status`锛氬厖鐢垫々鐘舵侊紙绌洪棽/鍏呯數涓/鎶ヨ涓/AGV鐢垫睜宸叉帴鍏ワ級 + +### 1.3 鏍¢獙锛歚IsValid(out errorMessage)` + +`ChargeStation.IsValid()` 绾︽潫锛 + +- `StationId`銆乣Name`銆乣IpAddress` 涓嶈兘涓虹┖ +- `IpAddress` 闇涓哄彲瑙f瀽鐨 IP +- `Port` 蹇呴』鍦 `1-65535` +- `SetVoltage` 蹇呴』鍦 `(0, 64]` +- `SetElectricCurrent` 蹇呴』鍦 `(0, 101]` + +--- + +## 2. 鏁版嵁鏈嶅姟锛歚ChargeStationDataService` + +鏂囦欢锛歚Charge/ChargeStationDataService.cs` + +### 2.1 鍗曚緥涓庢寔涔呭寲鏂囦欢 + +- 鍗曚緥锛歚ChargeStationDataService.Instance` +- 鍐呴儴鏁版嵁锛歚private List chargeStations` +- JSON 鏂囦欢璺緞锛氬熀浜庤繍琛岀洰褰曞啓鍏 + - `AppDomain.CurrentDomain.BaseDirectory/Config/ChargeStations.json` +- 鏋勯犲嚱鏁颁細纭繚 `Config/` 鐩綍瀛樺湪锛屽苟鎵ц `LoadData()` + +### 2.2 璇诲彇锛歚LoadData()` + +琛屼负锛 + +- 鑻ユ枃浠跺瓨鍦細璇诲彇鏂囨湰骞 `JsonConvert.DeserializeObject>(json)` +- 鑻ユ枃浠朵笉瀛樺湪锛氬垵濮嬪寲涓虹┖鍒楄〃锛堝苟涓嶄細鑷姩鐢熸垚榛樿鏍蜂緥锛 +- 寮傚父锛氳褰曡瘖鏂棩蹇楀苟鍥為鍒扮┖鍒楄〃 + +### 2.3 淇濆瓨锛歚SaveData()` + +琛屼负锛 + +- 鍦ㄩ攣 `lockObj` 涓嬪簭鍒楀寲鏁翠釜 `chargeStations` 鍒楄〃 +- 鍐欏叆鏂囦欢 `Config/ChargeStations.json`锛坄Formatting.Indented`锛 +- 淇濆瓨澶辫触锛氳繑鍥 `false` 骞剁敱璋冪敤鏂瑰洖婊氬唴瀛樼姸鎬侊紙閮ㄥ垎鏂规硶浼氬洖婊氾級 + +### 2.4 鏌ヨ API + +- `List GetAllStations()`锛氳繑鍥炲垪琛ㄥ壇鏈紙鎷疯礉锛 +- `ChargeStation GetStationById(string stationId)`锛氭寜 `StationId` 鏌ユ壘 +- `ChargeStation GetStationByIp(string ipAddress, int port)`锛氭寜 `IpAddress + Port` 鏌ユ壘 +- `List GetIdleStations()`锛氳繃婊 `Enabled && Status == Idle` +- `int GetChargingCount()`锛氱粺璁 `Status == Charging` +- `void Reload()`锛氶噸鏂版墽琛 `LoadData()` + +### 2.5 鏂板锛歚AddStation(ChargeStation station, out string errorMessage)` + +鍏抽敭鐐癸細 + +- `station == null` 杩斿洖澶辫触 +- 鍏堟墽琛 `station.IsValid(out errorMessage)` +- 鍞竴鎬ф牎楠岋細 + - `StationId` 涓嶅彲閲嶅 + - `IpAddress + Port` 缁勫悎涓嶅彲閲嶅 +- 鍐欏叆瀛楁锛 + - 璁剧疆 `CreatedTime` / `ModifiedTime` 涓哄綋鍓嶆椂闂 +- 鎴愬姛鍚庯細`SaveData()`锛涘け璐ュ垯灏嗘柊澧炲璞′粠鍐呭瓨绉婚櫎 + +### 2.6 鏇存柊锛歚UpdateStation(ChargeStation station, out string errorMessage, bool isSave = false)` + +璇ユ柟娉曞悓鏃惰鐢ㄤ綔鈥滈厤缃洿鏂扳濅笌鈥滆繍琛屾椂鐘舵佸悎骞跺悗鍐嶈惤鐩樷濈殑鍏ュ彛涔嬩竴锛堜笉鍚岃皟鐢ㄦ柟浼氱敤涓嶅悓鐨 `isSave` 鍊硷級銆 + +鏍稿績娴佺▼锛 + +- 鏍¢獙锛歚station.IsValid(out errorMessage)` +- 鎵惧埌鍘熷璞★細`existingStation = chargeStations.FirstOrDefault(s => s.StationId == station.StationId)` +- 鍐茬獊鏍¢獙锛歚IpAddress + Port` 涓嶈兘琚叾瀹冪珯鐐瑰崰鐢 +- 鏃堕棿澶勭悊锛 + - 淇濈暀 `existingStation.CreatedTime` + - 鏇存柊 `station.ModifiedTime = DateTime.Now` +- 璧嬪肩瓥鐣ュ彇鍐充簬 `isSave`锛 + - `isSave == true`锛氫粎灏嗏滈厤缃被瀛楁鈥濇嫹璐濆埌 `existingStation`锛堝苟浠 `station = existingStation`锛 + - `isSave == false`锛氫笉杩涜瀛楁绾ф嫹璐濓紝鐩存帴鐢ㄤ紶鍏ョ殑 `station` 鏇挎崲鍒楄〃閲岀殑瀵瑰簲椤 +- 涔嬪悗鏃犺 `isSave` 涓轰綍閮戒細鎵ц `SaveData()` 骞惰惤鐩樻暣涓垪琛 +- 淇濆瓨澶辫触锛氬洖婊氫负 `existingStation` + +鎸佷箙鍖栬竟鐣屾彁閱掞紙缁撳悎 `ChargeStation` 鐨 `JsonIgnore`锛夛細 + +- 鍥犱负 `Status / Alarm / 瀹炴椂鐢靛帇鐢垫祦 绛夎繍琛屾椂瀛楁` 閮芥槸 `JsonIgnore`锛屽嵆浣 `UpdateStation` 琚敤浜庡悎骞惰繍琛屾椂瀛楁锛岄噸鍚悗杩欎簺杩愯鏃跺瓧娈典粛涓嶄細鍑虹幇鍦 JSON 涓 +- 浣 `ModifiedTime`锛堟湭 `JsonIgnore`锛変細琚啓鍏ワ紝鍥犳浼氬嚭鐜扳滈氫俊涓婃姤棰戠箒瀵艰嚧 JSON 鏂囦欢 `ModifiedTime` 鍒锋柊鈥濈殑鐜拌薄 + +### 2.7 鍒犻櫎锛歚DeleteStation(string stationId, out string errorMessage)` + +- 鎸 `stationId` 鎵惧埌瀵硅薄骞剁Щ闄 +- 鎴愬姛鍚庝繚瀛橈紱澶辫触鍒欏皢瀵硅薄閲嶆柊鍔犲叆鍐呭瓨 +- 浠g爜涓師鏈湁鈥滃鏋滄鍦ㄥ厖鐢靛垯绂佹鍒犻櫎鈥濈殑妫鏌ワ紝浣嗚娉ㄩ噴鎺変簡 + +### 2.8 鐘舵佹洿鏂帮紙杩愯鏃讹級锛歚UpdateStationStatus(string stationId, ChargeStationStatus status)` + +- 淇敼鍐呭瓨瀵硅薄鐨 `Status` 涓 `ModifiedTime` +- 鐒跺悗 `SaveData()` +- 鐢变簬 `Status` 鏍囨敞浜 `JsonIgnore`锛屽洜姝ら噸鍚悗绔欑偣 `Status` 閫氬父涓嶄細浠 JSON 鎭㈠锛堜絾 `ModifiedTime` 浼氭洿鏂帮級 + +--- + +## 3. 鏁版嵁妯″瀷锛歚AlarmConfig` + +鏂囦欢锛歚Charge/AlarmConfig.cs` + +### 3.1 瀛楁鍚箟锛堜細琚寔涔呭寲锛 + +`AlarmConfig` 娌℃湁 `JsonIgnore`锛屽洜姝や互涓嬪瓧娈甸兘鑳藉啓鍏 `Config/AlarmConfigs.json`锛 + +- `AlarmId`锛氭姤璀︾紪鍙凤紙鏋勯犲嚱鏁拌嚜鍔ㄧ敓鎴愶紝鏍煎紡绫讳技 `ALMyyyyMMddHHmmssxxx`锛 +- `AlarmCode`锛氭姤璀︾紪鐮佸硷紙int锛 +- `AlarmContent`锛氭姤璀﹀唴瀹规弿杩帮紙鏂囨湰锛 +- `Level`锛氭姤璀︾骇鍒紙`AlarmLevel`锛歂one/Low/Medium/High/Critical锛 +- `Enabled`锛氭槸鍚﹀惎鐢 +- `Remarks`锛氬娉 +- `CreatedTime` / `ModifiedTime`锛氬垱寤轰笌淇敼鏃堕棿 + +### 3.2 鏍¢獙锛歚IsValid(out errorMessage)` + +- `AlarmId` 涓嶈兘涓虹┖ +- `AlarmCode >= 0` +- `AlarmContent` 涓嶈兘涓虹┖ + +--- + +## 4. 鏁版嵁鏈嶅姟锛歚AlarmConfigDataService` + +鏂囦欢锛歚Charge/AlarmConfigDataService.cs` + +### 4.1 鍗曚緥涓庢寔涔呭寲鏂囦欢 + +- 鍗曚緥锛歚AlarmConfigDataService.Instance` +- 鏁版嵁鏂囦欢璺緞锛歚AppDomain.CurrentDomain.BaseDirectory/Config/AlarmConfigs.json` +- 鏋勯犲嚱鏁拌皟鐢 `LoadData()`锛涜嫢鐩綍涓嶅瓨鍦ㄥ垯鍒涘缓 + +### 4.2 璇诲彇锛歚LoadData()` + +琛屼负锛 + +- 鏂囦欢瀛樺湪锛氳鍙栧苟鍙嶅簭鍒楀寲涓 `List` + - 鑻ュ弽搴忓垪鍖栫粨鏋滀负 `null`锛屽洖閫涓虹┖鍒楄〃 +- 鏂囦欢涓嶅瓨鍦細鍒濆鍖栭粯璁ゆ姤璀﹂厤缃 `InitializeDefaultAlarms()`锛岄殢鍚 `SaveData()` +- 寮傚父锛氳褰 `Debug.WriteLine`锛屽洖閫鍒扮┖鍒楄〃骞跺垵濮嬪寲榛樿鎶ヨ閰嶇疆 + +榛樿鎶ヨ鍖呭惈锛堢ず渚嬶級锛 + +- 1001锛氱數鍘嬭繃楂 +- 1002锛氱數鍘嬭繃浣 +- 1003锛氱數娴佽繃澶 +- 2001锛氭俯搴﹀紓甯 +- 3001锛氶氳瓒呮椂 +- 3002锛氳繛鎺ユ柇寮 + +### 4.3 淇濆瓨锛歚SaveData()` + +- 搴忓垪鍖栨暣涓 `_alarmConfigs` 骞跺啓鍏 `AlarmConfigs.json` +- 淇濆瓨澶辫触浼氭姏鍑哄紓甯革紙涓嶅彧鏄繑鍥 `false`锛 + +### 4.4 鏌ヨ API + +- `List GetAllAlarmConfigs()`锛氳繑鍥炲垪琛ㄥ壇鏈 +- `AlarmConfig GetAlarmConfig(string alarmId)`锛氭寜 `AlarmId` 鏌ユ壘 +- `AlarmConfig GetAlarmConfigByCode(int alarmCode)`锛氭寜 `AlarmCode` 鏌ユ壘 + +### 4.5 鏂板锛歚AddAlarmConfig(AlarmConfig alarmConfig, out string errorMessage)` + +- 鍏堟墽琛 `alarmConfig.IsValid(out errorMessage)` +- 鏍¢獙 `AlarmCode` 鍞竴鎬э紙涓嶅厑璁搁噸澶嶏級 +- 娣诲姞鍒板垪琛ㄥ悗 `SaveData()` + +### 4.6 鏇存柊锛歚UpdateAlarmConfig(AlarmConfig alarmConfig, out string errorMessage)` + +- 鏍¢獙锛歚IsValid` +- 鏌ユ壘鐩爣锛氭寜 `AlarmId` 鎵惧埌绱㈠紩锛涗笉瀛樺湪鍒欏け璐 +- 鍐茬獊鏍¢獙锛歚AlarmCode` 涓嶈兘琚叾瀹冩姤璀﹂厤缃崰鐢 +- 璁剧疆 `alarmConfig.ModifiedTime = DateTime.Now` +- 鏇挎崲鍒楄〃椤瑰苟 `SaveData()` + +### 4.7 鍒犻櫎锛歚DeleteAlarmConfig(string alarmId, out string errorMessage)` + +- 鎸 `AlarmId` 鎵惧埌骞剁Щ闄 +- 鐒跺悗 `SaveData()` + +### 4.8 閲嶆柊鍔犺浇锛歚Reload()` + +- 鍦ㄩ攣涓嬮噸鏂版墽琛 `LoadData()` + +--- + +## 5. 涓庘滄洿鏂拌矾寰勨濈殑鍏崇郴锛堜负浣曡繍琛屾椂鍙樺寲涔熶細瑙﹀彂钀界洏锛 + +铏界劧鏈〉涓昏璁 DataService锛屼絾涓轰簡璇存槑鈥滃摢浜涘瓧娈典細/涓嶄細鍑虹幇鍦 JSON 閲屸濓紝闇瑕佺偣鍒拌皟鐢ㄥ叧绯伙細 + +- 閫氳灞傦紙`Charge/CommunicationMessageService.cs`锛夊湪瑙f瀽鍙戦/鎺ユ敹鎶ユ枃鍚庯紝浼氾細 + - 鏇存柊 `ChargeStation` 鐨勮繍琛屾椂瀛楁锛堜緥濡 `HasAlarm`銆乣Status`銆乣RealTimeVoltage/Current` 绛夛級 + - 鐒跺悗璋冪敤 `ChargeStationDataService.UpdateStation(station, out errorMessage)`锛堜娇鐢ㄩ粯璁 `isSave=false`锛 +- UI 淇濆瓨绔欑偣閰嶇疆锛坄Charge/ChargeStationManagementForm.cs`锛夊湪鈥滀繚瀛/淇敼閰嶇疆鈥濇椂浼氳皟鐢細 + - `ChargeStationDataService.UpdateStation(station, out errorMessage, true)` +- 鎶ヨ閰嶇疆鐨 UI 澧炲垹鏀癸紙`Charge/AlarmConfigManagementForm.cs`锛夌洿鎺ヨ皟鐢細 + - `AddAlarmConfig / UpdateAlarmConfig / DeleteAlarmConfig` + +鍥犳浣犱細瑙傚療鍒帮細 + +- `ChargeStations.json` 涓殑鈥滆繍琛屾椂瀛楁鈥濅笉浼氳鍐欏叆锛堝洜涓哄畠浠甫 `JsonIgnore`锛 +- 浣 `ModifiedTime` 杩欑被鏈拷鐣ュ瓧娈典細琚啓鍏ワ紝鎵浠ユ枃浠朵粛浼氶绻佸彉鍖 + +--- +## 6. 閫氳鎶ユ枃瑙f瀽锛歚CommunicationMessageService` 濡備綍鏇存柊 `ChargeStation` + +鏈妭閲嶇偣瑙i噴 `Charge/CommunicationMessageService.cs` 涓滄姤鏂囪В鏋 -> 鏇存柊鍏呯數妗╄繍琛屾椂瀛楁鈥濈殑瀹屾暣閾捐矾锛堝苟璇存槑褰撳墠瀹炵幇閲屽摢浜涘瓧娈垫病鏈夎鐪熸钀藉埌 `ChargeStation`锛夈 + +### 6.1 鍏ュ彛涓庣珯鐐瑰尮閰嶈鍒 + +`CommunicationMessageService` 閫氳繃涓や釜鍏ュ彛鎺ユ敹澶栭儴鎶ユ枃锛屽苟鍦ㄥ唴閮ㄥ畬鎴愨滆В鏋 + 鏇存柊 + 钀界洏锛堥氳繃 DataService锛夆濓細 + +- 鍙戦佹姤鏂囧叆鍙o細`AddSendMessage(ipAddress, port, rawData, type, stationId)` +- 鎺ユ敹鎶ユ枃鍏ュ彛锛歚AddReceiveMessage(ipAddress, port, rawData, type, stationId)` + +涓ゆ潯閾捐矾鍦ㄨВ鏋愬墠閮戒細鍋氬悓鏍风殑绔欑偣鍖归厤锛 + +- 鍏堟嬁鍒板崟渚嬶細`ChargeStationDataService.Instance` +- 閫氳繃 `GetStationByIp(ipAddress, port)` 鎵惧埌瀵瑰簲 `ChargeStation` +- 鎵句笉鍒扮珯鐐圭洿鎺ヨ繑鍥烇紙姝ゆ椂鍙細璁板綍鎶ユ枃锛屼笉浼氭洿鏂拌绔欑偣杩愯鏃跺瓧娈碉級 + +瑙f瀽鎴愬姛鍚庢墠浼氳皟鐢細 + +- `ChargeStationDataService.UpdateStation(station, out errorMessage)`锛堣璋冪敤鍦ㄥ綋鍓嶄唬鐮侀噷浣跨敤榛樿鍙傛暟锛屾渶缁堜細钀界洏鏁翠釜 `ChargeStations.json`锛涗絾鐢变簬杩愯鏃跺瓧娈靛涓 `JsonIgnore`锛岄噸鍚悗杩欎簺杩愯鏃跺间笉浼氭仮澶嶏級 + +寮傚父澶勭悊鏂归潰锛 + +- `ParseSendDataAndUpdateStation` / `ParseReceiveDataAndUpdateStation` 閮戒娇鐢 `try/catch` 骞垛滈潤榛樺悶鎺夊紓甯糕濓紝鍥犳瑙f瀽澶辫触閫氬父琛ㄧ幇涓猴細鎶ユ枃鍒楄〃鏈夎褰曪紝浣嗗厖鐢垫々瀛楁娌℃湁鍙樺寲銆 + +### 6.2 鍙戦佹姤鏂囪В鏋愪笌瀛楁鏇存柊锛坄UpdateStationFromSendData`锛 + +鍙戦佹姤鏂囧畬鏁磋皟鐢ㄩ摼濡備笅锛 + +`AddSendMessage` -> `ParseSendDataAndUpdateStation` + -> `ParseSendRawData(rawData, type)` + -> `UpdateStationFromSendData(station, parsedData)` + -> `ChargeStationDataService.UpdateStation(...)` + +#### 6.2.1 `ParseSendRawData` 杈撳叆鏍煎紡涓 `type` 鏀寔 + +`ParseSendRawData` 鐨勮緭鍏ヨ姹傦細 + +- `rawData` 浠ョ┖鏍煎垎闅斿瓧鑺 token锛堜緥濡傦細`"BB 01 42 ..."`锛 +- 姣忎釜 token 浼氭寜鍗佸叚杩涘埗瑙f瀽锛歚byte.TryParse(token, NumberStyles.HexNumber, ...)` +- 鍙戦佹姤鏂囨渶灏 token 鏁帮細`parts.Length >= 10` + +褰撳墠瀹炵幇閲岋紝`type` 浠呭浠ヤ笅涓ょ鏈夋槑纭瓧鑺備綅鏄犲皠锛 + +- `FRLDShort` +- `FRLDTall` + +鍏朵粬 `type`锛堜緥濡 `MuXing`锛変笉浼氬懡涓槧灏勫垎鏀紝姝ゆ椂瑙f瀽鍑烘潵鐨勬暟鍊间繚鎸侀粯璁ゅ硷紝鐒跺悗浠嶅彲鑳借Е鍙 `UpdateStationFromSendData` 鐨勨滈粯璁よ鐩栤濋昏緫锛堣涓嬫枃鈥滃凡鐭ラ檺鍒垛濓級銆 + +#### 6.2.2 浠庡彂閫佹姤鏂囧啓鍏ュ摢浜 `ChargeStation` 瀛楁 + +`UpdateStationFromSendData` 瀹為檯鏇存柊鐨勫瓧娈靛涓嬶紙鐩存帴瀵瑰簲浠g爜璧嬪硷級锛 + +- `station.LastSendTime = parsedData.SendTime` +- `station.ChargeCommandStatus` + - `parsedData.ChargeCommand == 1` -> `ChargeCommandStatus.Started` + - `parsedData.ChargeCommand == 0` -> `ChargeCommandStatus.Stopped` +- `station.BatteryLevel = parsedData.BatteryLevel` +- `station.CurrentVehicle = parsedData.CurrentVehicleId.ToString()` + +娉ㄦ剰锛 + +- `UpdateStationFromSendData` 閲 `SetVoltage` / `SetElectricCurrent` 鐨勮祴鍊艰娉ㄩ噴鎺変簡锛堝嵆锛氬彂閫佹姤鏂囦笉浼氭洿鏂 `ChargeStation.SetVoltage` / `ChargeStation.SetElectricCurrent` 鐨勯厤缃洰鏍囧硷級銆 + +### 6.3 鎺ユ敹鎶ユ枃瑙f瀽涓庡瓧娈垫洿鏂帮紙`UpdateStationFromReceiveData`锛 + +鎺ユ敹鎶ユ枃瀹屾暣璋冪敤閾惧涓嬶細 + +`AddReceiveMessage` -> `ParseReceiveDataAndUpdateStation` + -> `ParseReceiveRawData(rawData, type)` + -> `UpdateStationFromReceiveData(station, parsedData)` + -> `ChargeStationDataService.UpdateStation(...)` + +#### 6.3.1 `ParseReceiveRawData` 杈撳叆鏍煎紡涓 `type` 鏀寔 + +`ParseReceiveRawData` 鐨勮緭鍏ヨ姹傦細 + +- `rawData` 浠ョ┖鏍煎垎闅斿瓧鑺 token锛坄rawData.Split(' ')`锛 +- 鎺ユ敹鎶ユ枃鏈灏 token 鏁帮細`parts.Length >= 30` +- 姣忎釜 token 鐨勮В鏋愪娇鐢ㄧ殑鏄 `byte.TryParse(parts[i], out bytes[i])`锛堟病鏈夋樉寮 `NumberStyles.HexNumber`锛 + +鍥犳褰 `rawData` token 褰㈠鍗佸叚杩涘埗瀛楄妭锛堜緥濡 `0A`銆乣FF`锛夋椂锛屽彲鑳藉嚭鐜拌В鏋愬け璐ュ鑷 `parsedData == null`锛堜粠鑰屼笉浼氭洿鏂扮珯鐐瑰瓧娈碉級鐨勬儏鍐点 + +`type` 鐨勫瓧鑺備綅鏄犲皠鍚屾牱鍙疄鐜颁簡涓ょ锛 + +- `FRLDShort` +- `FRLDTall` + +#### 6.3.2 浠庢帴鏀舵姤鏂囧啓鍏ュ摢浜 `ChargeStation` 瀛楁 + +`UpdateStationFromReceiveData` 瀹為檯鏇存柊鐨勫瓧娈靛涓嬶細 + +- `station.LastReceiveTime = parsedData.ReceiveTime` +- `station.MechanismStatus = parsedData.MechanismStatus` +- `station.RealTimeVoltage = parsedData.RealTimeVoltage` +- `station.RealTimeCurrent = parsedData.RealTimeCurrent` +- `station.Status = parsedData.Status` +- `station.HasAlarm = parsedData.HasAlarm` +- `station.AlarmLevel = parsedData.AlarmLevel` +- `station.AlarmMessage` + - `parsedData.HasAlarm == true` -> `鎶ヨ绾у埆: {GetAlarmLevelText(parsedData.AlarmLevel)}` + - 鍚﹀垯 -> `string.Empty` + +涓庢姤璀︾浉鍏崇殑鏄犲皠锛 + +- `ParseReceiveRawData` 閲 `HasAlarm = chargeStationStatus == 2` +- `ParseStationStatus` 灏 `statusByte == 2` 鏄犲皠涓 `ChargeStationStatus.Fault` + +褰撳墠瀹炵幇閲 `AlarmLevel` 鐨勬潵婧愭湁涓涓槑鏄鹃檺鍒讹細 + +- `ParseReceiveRawData` 涓 `AlarmLevel = ParseAlarmLevel(bytes[20])` 琚敞閲婃帀浜 +- 鍥犳 `parsedData.AlarmLevel` 澶氬崐淇濇寔榛樿鍊硷紙`AlarmLevel.None`锛夛紝浣嗗彧瑕 `HasAlarm == true`锛宍AlarmMessage` 浠嶄細鎸夐粯璁 `AlarmLevel` 鐢熸垚鏂囨湰 + +鍚屾椂锛宍ParsedReceiveData` 涓殑浠ヤ笅瀛楁铏界劧浼氳В鏋愬嚭鏉ワ紝浣 `UpdateStationFromReceiveData` 娌℃湁鎶婂畠浠啓鍏 `ChargeStation`锛 + +- `ParsedReceiveData.CommStatus` +- `ParsedReceiveData.ChargeCommandStatus` +- `ParsedReceiveData.ChargeID` +- `ParsedReceiveData.BatteryAH` + +### 6.4 宸茬煡闄愬埗/琛屼负鎬荤粨锛堝奖鍝嶁滃瓧娈垫槸鍚︽洿鏂扳濓級 + +1. 瑙f瀽澶辫触鍙奖鍝嶁滃瓧娈垫洿鏂扳濓紝涓嶅奖鍝嶁滄姤鏂囪褰曚笌 UI 鍒楄〃灞曠ず鈥 + - 鎶ユ枃涓瀹氫細鍏堣繘鍏 `_messages`锛堝苟瑙﹀彂 `MessageAdded`锛 + - 浣嗚В鏋愬嚱鏁拌繑鍥 `null` / 绔欑偣鎵句笉鍒 / 寮傚父鏃讹紝瀛楁鏇存柊涓嶄細鍙戠敓 + +2. 绔欑偣鍖归厤浣跨敤 `IP + Port` + - `GetStationByIp(ipAddress, port)` 鎵句笉鍒板搴 `ChargeStation` 鏃讹紝涓嶄細鏇存柊璇ョ珯鐐硅繍琛屾椂瀛楁 + +3. `type` 鍙 `FRLDShort` / `FRLDTall` 瀹屾垚浜嗘槧灏 + - 鍙戦佷晶瀵规湭鐭 `type` 浠嶄細杩斿洖榛樿 `ParsedSendData`锛屼粠鑰屽彲鑳借鐩 `ChargeCommandStatus` / `BatteryLevel` / `CurrentVehicle` 涓洪粯璁ゅ + - 鎺ユ敹渚ф湭鐭 `type` 涔熷彲鑳戒骇鐢熼粯璁 `ParsedReceiveData`锛屼絾鍓嶆彁鏄 `rawData.Split(' ')` 鍚庝粛婊¤冻 `parts.Length >= 30` + +4. 鎺ユ敹渚 token 瑙f瀽鏂瑰紡鍙兘涓庤緭鍏ュ崄鍏繘鍒舵牸寮忎笉涓鑷 + - `ParseReceiveRawData` 鏈娇鐢 `NumberStyles.HexNumber` + - 濡傛灉 `rawData` token 鏄崄鍏繘鍒跺瓧鑺傦紙濡 `0A`锛夛紝鍙兘瀵艰嚧 `parsedData == null`锛岃繘鑰屼笉鏇存柊瀹炴椂瀛楁 + + + +## 7. 杩愯鏃跺厖鐢典笟鍔★細StandardChargeMission + +鏈妭鑱氱劍 `Charge/StandardChargeMission.cs` 涓殑鈥滃厖鐢佃繘绋嬪惎鍔 + 500ms 鍏呯數涓氬姟寰幆鈥濓紝骞惰窡韪 `SendToChargeStation(...)` 鐨勭湡瀹炶皟鐢ㄨ矾寰勫埌鍏蜂綋鍏呯數妗╁疄鐜扮被銆 + +### 7.1 鍚姩鍏ュ彛锛歚Execute()` + +`StandardChargeMission.Execute()` 璐熻矗鍚姩鍏呯數杩涚▼锛屾牳蹇冩祦绋嬶細 + +- 璁剧疆杩涚▼鐘舵侊細`status.status = "宸插惎鍔"` +- 闃查噸澶嶅惎鍔細閫氳繃 `myStarted` 鍒ゆ柇锛岄伩鍏嶉噸澶嶅垱寤虹嚎绋 +- 鍒濆鍖栬繍琛屾椂瀛楀吀锛歚ChargeStations = new Dictionary()` +- 鍒涘缓鍚庡彴绾跨▼锛歚ChargeThread = new Thread(() => { ... })` + - 鍦ㄧ嚎绋嬪唴閮ㄥ畬鎴愨滃厖鐢电珯鍒濆鍖 + 500ms 涓氬姟寰幆鈥 +- 鍚姩杈呭姪浠诲姟锛氬畾鏈熶笂浼犲甫 `unavailable` 鏍囩鐨勭珯鐐瑰埌杩锋瘋绯荤粺锛堝悓鏍锋槸 `Thread.Sleep(500)` 鍛ㄦ湡锛 +- 鏈鍚庤皟鐢 `base.Execute()`锛岃鍩虹被璋冨害/鑱旈攣閫昏緫缁х画宸ヤ綔 + +### 7.2 鍒濆鍖栵細鍚庡彴绾跨▼ Step1锛堝垱寤/閲嶅缓 `AbstractChargeStation`锛 + +鍦 `ChargeThread` 鐨 `while (true)` 鍐呴儴锛屾瘡涓杞兘浼氬厛鎵ц鈥滄楠1锛氬垵濮嬪寲鍏呯數绔欌濓細 + +- 璇诲彇閰嶇疆锛歚ChargeStationHelper.GetAllStationConfigs()` + - 搴曞眰鏉ヨ嚜 `ChargeStationDataService.Instance.GetAllStations()` +- 閬嶅巻姣忎釜鍏呯數妗╅厤缃」锛屾墽琛屾牎楠屼笌鍒涘缓锛 + - `Enabled == false`锛氳烦杩 + - 鏍¢獙 `SiteId > 0`銆乣IpAddress` 鍙В鏋愩乣Port` 鍦 `1-65535` + - 鑻ュ瓧鍏搁噷宸插瓨鍦ㄧ浉鍚 `siteId` 鐨勭珯鐐癸細 + - 褰 IP/Port 鍙戠敓鍙樺寲锛歚existingStation.CloseCommunication()` 鍚庢洿鏂 `Ip/Port` 骞堕噸鏂 `CreateCommunication(...)` + - IP/Port 鏈彉鍖栵細鐩存帴 `continue`锛堝鐢ㄥ師杩炴帴锛 + - 鑻ヤ笉瀛樺湪锛 + - 浣跨敤 `GetChargeTypeString(stationConfig.Type)` 鏄犲皠鍒板叿浣撶珯鐐圭被鍚嶏細 + - `FRLDTall` -> `FLChargeStation` + - `FRLDShort` -> `PCBChargeStation` + - `MuXing` -> `MuXingChargeStation` + - 榛樿鍥為 -> `PCBChargeStation` + - `Activator.CreateInstance(type)` 鍒涘缓瀵硅薄锛岃缃細 + - `SiteId / Ip / Port` + - `CommunicationType`锛歚FRLDShort` 寮哄埗 `UDP`锛屽叾瀹冧娇鐢ㄩ厤缃噷鐨 `CommunicationType` + - 璋冪敤 `CreateCommunication(ipAddress, port)` 寤虹珛閫氫俊杩炴帴 + - 鏀惧叆瀛楀吀锛歚ChargeStations.Add(siteId, stationInstance)` + +鍚屾椂锛岀嚎绋嬪唴閮ㄨ繕浼氬仛 UDP 鏈嶅姟鍒濆鍖栵細 + +- 鑻ュ瓨鍦ㄤ换鎰忕珯鐐 `CommunicationType == "UDP"`锛 + - `UdpService ??= new ChargeUdpService();` + +### 7.3 500ms 涓氬姟寰幆锛氬悗鍙扮嚎绋 Step3 + `SendToChargeStation(...)` + +`ChargeThread` 鐨勪富寰幆缁撴瀯锛堢畝鍖栵級锛 + +1. 璇诲彇浜掗攣寮鍏筹細`var shieldInterLock = ((StandardChargeMissionStatus)status).ShieldInterLock` +2. 鏇存柊/娓呯悊閰嶇疆缁戝畾锛 + - 鑻 `ChargeStationHelper.GetStationBySiteId(siteId) == null`锛氫粠 `ChargeStations` 绉婚櫎璇ョ珯鐐 + - 瀵 `SimpleLib.GetAllSites()` 涓粛甯 `fields["Charge"]` 浣嗕笉鍦 `ChargeStations` 閰嶇疆閲岀殑绔欑偣锛 + - 绉婚櫎 `Charge / setVoltage / setElectricCurrent / group` 绛夊瓧娈 +3. 閬嶅巻姣忎釜绔欑偣锛屾墽琛屸滆溅杈嗘悳绱 -> openCharge 璁$畻 -> 涓嬪彂鈥濓細 + - 鍙栫珯鐐归厤缃細`chargeStationSetting = ChargeStationHelper.GetStationBySiteId(siteId)` + - 鑻 `!chargeStationSetting.Enabled`锛氳烦杩 + - 灏嗙珯鐐归厤缃粦瀹氬洖 `site.fields`锛 + - `site.fields["Charge"] = "True"` + - `site.fields["setVoltage"] = chargeStationSetting.SetVoltage.ToString("0.0")` + - `site.fields["setElectricCurrent"] = chargeStationSetting.SetElectricCurrent.ToString("0.0")` + - `site.fields["group"]`锛氬惎鐢ㄦ椂鍐 `GroupCarType`锛岀鐢ㄦ椂鍐 `"绂佺敤"` + - 璁剧疆绔欑偣杩涘叆/绂诲紑鏉冮檺锛 + - `ChargeMethodType.Side` 鍒嗘敮锛歚SetAllowEnter / SetAllowExit` 涓 `ShieldSiteMechanismStatus` / `MechanismStatus == Retracted` 鑱斿姩 + - 闈 `Side`锛氱洿鎺 `SetAllowEnter(true) / SetAllowExit(true) / SetAcknowledgeLeave(true)` + - 鏌ユ壘涓庤绔欑偣鐩稿叧鐨勮溅杈嗭紙鍦ㄧ珯/鑾峰彇閿/鎸佹湁閿侊級锛 + - `GetLastSite() == siteId` 鎴 `aquiringLock == siteId` 鎴 `holdingLocks.Contains(siteId)` + - 璁$畻 `openCharge`锛 + - 榛樿 `0` + - 浠呭綋杞﹁締瀛樺湪涓 `Commons.GetVehicleStatus((Car)car) == VehicleStatus.Normal` + - 骞朵笖婊¤冻鍏呯數鏉′欢锛 + - `charging` 鏍囪瀛樺湪 + - 鏈鍗犵敤锛歚!car.tags.Contains("occupied")` + - 閿佺姸鎬佸尮閰嶏細`holdingLocks.Length == 1` 涓 `pendingLocks.Length == 0` + - 鍒 `openCharge = 1` + - 浜掗攣闂ㄦ帶鍚庝笅鍙戞寚浠わ細 + - 鑻 `!shieldInterLock`锛 + - `chargeStation.SendToChargeStation(openCharge, (Car)car);` +4. 寰幆灏鹃儴鍥哄畾鑺傛媿锛歚Thread.Sleep(500)` + +### 7.4 `SendToChargeStation` 璋冪敤閾撅紙涓嬪彂璺緞锛 + +鍦 500ms 寰幆涓紝涓嬪彂鐨勮皟鐢ㄨ矾寰勬槸锛 + +`StandardChargeMission(ChargeThread 500ms loop)` +-> `AbstractChargeStation` 瀛愮被 `SendToChargeStation(int isCharge, Car car)` +-> 瀛愮被鍐呴儴缁勫寘 + 璁板綍鍙戦佹姤鏂囷細`CommunicationMessageService.Instance.AddSendMessage(...)` +-> 閫氳繃 TCP/UDP 閫氶亾鐪熸鍙戦佹姤鏂 + +鍚勭珯鐐瑰疄鐜扮被鐨勨滃彂閫佺鈥濆叧閿偣锛 + +- `FLChargeStation.SendToChargeStation` + - 渚濊禆 `IsConnected && Client != null`锛屽惁鍒欎笉鍙戦 + - 璇诲彇 `Car` 鐨 `Soc/Voltage/ElectricCurrent`锛屽苟鍙鐩 `site.fields["setVoltage"]/["setElectricCurrent"]` + - `AddSendMessage(..., "FRLDTall", site?.name)` 鍚 `Client.Send(msg)` + +- `MuXingChargeStation.SendToChargeStation` + - 璁$畻 `openChargePort = (isCharge == 1 ? 2 : 3)` 骞剁粍鍖咃紙鍖呭惈鏃堕棿鎴充笌 CRC锛 + - `AddSendMessage(..., "MuXing")` 鍚庡啓鍏 TCP `stream` + +- `PCBChargeStation.SendToChargeStation`锛坄FRLDShort`锛 + - 浣跨敤 `UdpClient` 鍙戦 + - `AddSendMessage(..., "FRLDShort", site?.name)` 鍚 `udpClient.SendAsync(msg, msg.Length, _endPoint)` + +```mermaid +flowchart TD + A[StandardChargeMission.Execute\n鍚姩 ChargeThread] --> B[ChargeThread while(true)] + B --> C[Step1 鍒濆鍖/閲嶅缓 ChargeStations] + B --> D[Step3 閬嶅巻姣忎釜绔欑偣] + D --> E[璁$畻 openCharge(0/1)] + E --> F{!ShieldInterLock} + F -->|false| Z[璺宠繃涓嬪彂] + F -->|true| G[chargeStation.SendToChargeStation(openCharge, car)] + G --> H[绔欑偣瀛愮被缁勫寘] + H --> I[CommunicationMessageService.AddSendMessage] + I --> J[TCP/UDP 鍙戦佹姤鏂嘳 +``` + diff --git a/StandardScene.Core/Docs/Charge/鍏呯數妗╁疄鏃舵暟鎹姛鑳借鏄.md b/StandardScene.Core/Docs/Charge/鍏呯數妗╁疄鏃舵暟鎹姛鑳借鏄.md new file mode 100644 index 0000000..0776487 --- /dev/null +++ b/StandardScene.Core/Docs/Charge/鍏呯數妗╁疄鏃舵暟鎹姛鑳借鏄.md @@ -0,0 +1,414 @@ +# 馃攲 鍏呯數妗╁疄鏃舵暟鎹姛鑳借鏄 + +## 鉁 宸插畬鎴愮殑淇敼 + +### 1. 鏁版嵁妯″瀷鏇存柊锛圕hargeStation.cs锛 + +娣诲姞浜嗗疄鏃剁數鍘嬪拰鐢垫祦瀛楁锛 + +```csharp +/// +/// 瀹炴椂鐢靛帇 (V) - 褰撳墠鍏呯數鏃剁殑瀹為檯鐢靛帇 +/// +[DisplayName("瀹炴椂鐢靛帇(V)")] +public double RealTimeVoltage { get; set; } + +/// +/// 瀹炴椂鐢垫祦 (A) - 褰撳墠鍏呯數鏃剁殑瀹為檯鐢垫祦 +/// +[DisplayName("瀹炴椂鐢垫祦(A)")] +public double RealTimeCurrent { get; set; } +``` + +### 2. 鍒楄〃鏄剧ず鏇存柊 + +#### 鉂 绉婚櫎鐨勫垪锛 +- **鍔熺巼(W)** - 鍔熺巼鍒楀凡绉婚櫎 + +#### 鉁 鏂板鐨勫垪锛 +- **瀹炴椂鐢靛帇(V)** - 鏄剧ず鍏呯數妗╁綋鍓嶅疄闄呯數鍘 +- **瀹炴椂鐢垫祦(A)** - 鏄剧ず鍏呯數妗╁綋鍓嶅疄闄呯數娴 + +#### 鍒楄〃缁撴瀯锛堟洿鏂板悗锛夛細 +``` +鈹屸攢鈹鈹鈹鈹鈹鈹鈹鈹攢鈹鈹鈹鈹鈹鈹攢鈹鈹鈹鈹鈹鈹鈹鈹攢鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹攢鈹鈹鈹鈹攢鈹鈹鈹鈹鈹鈹鈹鈹攢鈹鈹鈹鈹鈹鈹鈹鈹攢鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹攢鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹攢鈹鈹鈹鈹攢鈹鈹鈹鈹攢鈹鈹鈹鈹鈹鈹攢鈹鈹鈹鈹 +鈹 缂栧彿 鈹 鍚嶇О 鈹 绫诲瀷 鈹 IP鍦板潃 鈹傜鍙b攤 鐢靛帇(V)鈹 鐢垫祦(A)鈹傚疄鏃剁數鍘(V)鈹傚疄鏃剁數娴(A)鈹傜姸鎬佲攤鍚敤鈹傜珯鐐笽D鈹傚娉ㄢ攤 +鈹溾攢鈹鈹鈹鈹鈹鈹鈹鈹尖攢鈹鈹鈹鈹鈹鈹尖攢鈹鈹鈹鈹鈹鈹鈹鈹尖攢鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹尖攢鈹鈹鈹鈹尖攢鈹鈹鈹鈹鈹鈹鈹鈹尖攢鈹鈹鈹鈹鈹鈹鈹鈹尖攢鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹尖攢鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹尖攢鈹鈹鈹鈹尖攢鈹鈹鈹鈹尖攢鈹鈹鈹鈹鈹鈹尖攢鈹鈹鈹鈹 +鈹侰S12345 鈹1鍙锋々 鈹傛爣鍑 鈹192.168..鈹502 鈹220.0 鈹32.0 鈹215.5 鈹28.3 鈹傚厖鐢碘攤鏄 鈹1001 鈹... 鈹 +鈹侰S12346 鈹2鍙锋々 鈹傚揩閫 鈹192.168..鈹502 鈹380.0 鈹63.0 鈹0.0 鈹0.0 鈹傜┖闂测攤鏄 鈹1002 鈹... 鈹 +鈹斺攢鈹鈹鈹鈹鈹鈹鈹鈹粹攢鈹鈹鈹鈹鈹鈹粹攢鈹鈹鈹鈹鈹鈹鈹鈹粹攢鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹粹攢鈹鈹鈹鈹粹攢鈹鈹鈹鈹鈹鈹鈹鈹粹攢鈹鈹鈹鈹鈹鈹鈹鈹粹攢鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹粹攢鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹粹攢鈹鈹鈹鈹粹攢鈹鈹鈹鈹粹攢鈹鈹鈹鈹鈹鈹粹攢鈹鈹鈹鈹 +``` + +### 3. 鐣岄潰鏇存柊 + +#### 鉂 绉婚櫎鐨勬寜閽細 +- **鏂板鎸夐挳** - 宸蹭粠鐣岄潰绉婚櫎 + +#### 鉁 淇濈暀鐨勬寜閽紙閲嶆柊鎺掑垪锛夛細 +- **淇濆瓨** - 浣嶇疆璋冩暣鍒版渶宸︿晶锛20, 20锛夛紝灏哄 100脳50 +- **鍒犻櫎** - 浣嶇疆璋冩暣鍒颁腑闂达紙150, 20锛夛紝灏哄 100脳50 +- **鍙栨秷** - 浣嶇疆璋冩暣鍒板彸渚э紙280, 20锛夛紝灏哄 100脳50 + +``` +鈹屸攢鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹 +鈹 鈹 +鈹 [ 淇濆瓨 ] [ 鍒犻櫎 ] [ 鍙栨秷 ]鈹 +鈹 (钃濊壊) (绾㈣壊) (榛樿) 鈹 +鈹 鈹 +鈹斺攢鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹 +``` + +### 4. 缂栬緫鍖轰繚鐣欏姛鑳 + +**璁剧疆鐢靛帇鍜岀數娴佸姛鑳藉畬鍏ㄤ繚鐣**锛 + +``` +鈹屸攢鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹 +鈹 鍏呯數妗╀俊鎭 鈹 +鈹溾攢鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹 +鈹 鈹 +鈹 鍚嶇О锛 [1鍙峰厖鐢垫々 ] 鈹 +鈹 绫诲瀷锛 [鏍囧噯鍏呯數妗 鈻糫 鈹 +鈹 IP鍦板潃锛歔192.168.1.100 ] 鈹 +鈹 绔彛锛 [502 鈻测柤] 鈹 +鈹 鈹 +鈹 鐢靛帇(V)锛歔220.0 鈻测柤] 鈹 鈫 棰濆畾鐢靛帇锛堣缃硷級 +鈹 鐢垫祦(A)锛歔32.0 鈻测柤] 鈹 鈫 棰濆畾鐢垫祦锛堣缃硷級 +鈹 鈹 +鈹 鐘舵侊細 [绌洪棽 鈻糫 鈹 +鈹 鈽 鍚敤鍏呯數妗 鈹 +鈹 ... 鈹 +鈹斺攢鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹 +``` + +--- + +## 馃搳 瀛楁璇存槑 + +### 鐢靛帇鍜岀數娴佺殑鍖哄埆 + +| 瀛楁 | 绫诲瀷 | 璇存槑 | 鐢ㄩ | +|------|------|------|------| +| **Voltage** | 棰濆畾鐢靛帇 | 鍏呯數妗╃殑璁捐鐢靛帇锛堝浐瀹氬硷級 | 鍏呯數妗╁弬鏁伴厤缃 | +| **Current** | 棰濆畾鐢垫祦 | 鍏呯數妗╃殑璁捐鐢垫祦锛堝浐瀹氬硷級 | 鍏呯數妗╁弬鏁伴厤缃 | +| **RealTimeVoltage** | 瀹炴椂鐢靛帇 | 褰撳墠瀹為檯宸ヤ綔鐢靛帇锛堝姩鎬佸硷級 | 瀹炴椂鐩戞帶鏄剧ず | +| **RealTimeCurrent** | 瀹炴椂鐢垫祦 | 褰撳墠瀹為檯宸ヤ綔鐢垫祦锛堝姩鎬佸硷級 | 瀹炴椂鐩戞帶鏄剧ず | + +### 鍏稿瀷鍦烘櫙绀轰緥 + +#### 鍦烘櫙1锛氬厖鐢垫々绌洪棽鏃 +``` +棰濆畾鐢靛帇锛220.0V +棰濆畾鐢垫祦锛32.0A +瀹炴椂鐢靛帇锛0.0V 鈫 鏈湪鍏呯數锛屽疄鏃跺间负0 +瀹炴椂鐢垫祦锛0.0A 鈫 鏈湪鍏呯數锛屽疄鏃跺间负0 +鐘舵侊細绌洪棽 +``` + +#### 鍦烘櫙2锛氬厖鐢垫々鍏呯數涓 +``` +棰濆畾鐢靛帇锛220.0V +棰濆畾鐢垫祦锛32.0A +瀹炴椂鐢靛帇锛215.5V 鈫 瀹為檯鍏呯數鐢靛帇 +瀹炴椂鐢垫祦锛28.3A 鈫 瀹為檯鍏呯數鐢垫祦 +鐘舵侊細鍏呯數涓 +瀹炴椂鍔熺巼锛6098.65W (215.5V 脳 28.3A) +``` + +#### 鍦烘櫙3锛氬厖鐢垫々鏁呴殰 +``` +棰濆畾鐢靛帇锛220.0V +棰濆畾鐢垫祦锛32.0A +瀹炴椂鐢靛帇锛180.2V 鈫 鐢靛帇寮傚父鍋忎綆 +瀹炴椂鐢垫祦锛5.1A 鈫 鐢垫祦寮傚父鍋忎綆 +鐘舵侊細鏁呴殰 +鍛婅锛氱數鍘嬩綆浜庨瀹氬20% +``` + +--- + +## 馃捇 浠g爜瀹炵幇 + +### 1. 鍒涘缓鍏呯數妗╂椂鍒濆鍖 + +```csharp +var station = new ChargeStation +{ + Name = "1鍙峰厖鐢垫々", + Type = ChargeStationType.Standard, + IpAddress = "192.168.1.100", + Port = 502, + + // 棰濆畾鍙傛暟锛堝浐瀹氾級 + Voltage = 220.0, + Current = 32.0, + + // 瀹炴椂鍙傛暟锛堝垵濮嬩负0锛 + RealTimeVoltage = 0.0, + RealTimeCurrent = 0.0, + + Status = ChargeStationStatus.Idle +}; +``` + +### 2. 鏇存柊瀹炴椂鏁版嵁锛堟ā鎷烶LC鏁版嵁锛 + +```csharp +/// +/// 鏇存柊鍏呯數妗╁疄鏃舵暟鎹 +/// +public void UpdateRealTimeData(string stationId, double voltage, double current) +{ + var dataService = ChargeStationDataService.Instance; + var station = dataService.GetStationById(stationId); + + if (station != null) + { + station.RealTimeVoltage = voltage; + station.RealTimeCurrent = current; + + dataService.UpdateStation(station, out string errorMsg); + + // 妫鏌ュ紓甯 + CheckVoltageCurrentAbnormal(station); + } +} + +/// +/// 妫鏌ョ數鍘嬬數娴佹槸鍚﹀紓甯 +/// +private void CheckVoltageCurrentAbnormal(ChargeStation station) +{ + // 鍏呯數涓墠妫鏌 + if (station.Status == ChargeStationStatus.Charging) + { + // 鐢靛帇鍋忓樊瓒呰繃20% + double voltageDiff = Math.Abs(station.RealTimeVoltage - station.Voltage) / station.Voltage; + if (voltageDiff > 0.2) + { + Diagnosis.Log($"鍏呯數妗 {station.Name} 鐢靛帇寮傚父: " + + $"棰濆畾{station.Voltage}V, 瀹炴椂{station.RealTimeVoltage}V", + "ChargeStation", true); + } + + // 鐢垫祦鍋忓樊瓒呰繃20% + double currentDiff = Math.Abs(station.RealTimeCurrent - station.Current) / station.Current; + if (currentDiff > 0.2) + { + Diagnosis.Log($"鍏呯數妗 {station.Name} 鐢垫祦寮傚父: " + + $"棰濆畾{station.Current}A, 瀹炴椂{station.RealTimeCurrent}A", + "ChargeStation", true); + } + } +} +``` + +### 3. 鍏呯數寮濮嬫椂璁剧疆瀹炴椂鏁版嵁 + +```csharp +/// +/// 寮濮嬪厖鐢 +/// +public void StartCharging(string stationId, int carId) +{ + var dataService = ChargeStationDataService.Instance; + var station = dataService.GetStationById(stationId); + + if (station != null) + { + // 鏇存柊鐘舵 + station.Status = ChargeStationStatus.Charging; + + // 鍒濆鍖栧疄鏃舵暟鎹紙鍒濆鍊肩害涓洪瀹氬肩殑90%锛 + station.RealTimeVoltage = station.Voltage * 0.9; + station.RealTimeCurrent = station.Current * 0.9; + + dataService.UpdateStation(station, out _); + + Diagnosis.Log($"杞﹁締 {carId} 寮濮嬪厖鐢: " + + $"鍏呯數妗 {station.Name}, " + + $"瀹炴椂鐢靛帇 {station.RealTimeVoltage:F1}V, " + + $"瀹炴椂鐢垫祦 {station.RealTimeCurrent:F1}A", + "ChargeStation", true); + } +} +``` + +### 4. 鍏呯數缁撴潫鏃舵竻闆跺疄鏃舵暟鎹 + +```csharp +/// +/// 鍋滄鍏呯數 +/// +public void StopCharging(string stationId, int carId) +{ + var dataService = ChargeStationDataService.Instance; + var station = dataService.GetStationById(stationId); + + if (station != null) + { + // 鏇存柊鐘舵 + station.Status = ChargeStationStatus.Idle; + + // 娓呴浂瀹炴椂鏁版嵁 + station.RealTimeVoltage = 0.0; + station.RealTimeCurrent = 0.0; + + dataService.UpdateStation(station, out _); + + Diagnosis.Log($"杞﹁締 {carId} 鍏呯數瀹屾垚: 鍏呯數妗 {station.Name}", + "ChargeStation", true); + } +} +``` + +### 5. 浠嶱LC璇诲彇瀹炴椂鏁版嵁 + +```csharp +/// +/// 浠嶱LC璇诲彇鍏呯數妗╁疄鏃舵暟鎹 +/// +public void ReadRealTimeDataFromPLC() +{ + var dataService = ChargeStationDataService.Instance; + var stations = dataService.GetAllStations() + .Where(s => s.Status == ChargeStationStatus.Charging) + .ToList(); + + foreach (var station in stations) + { + try + { + // 浠嶱LC璇诲彇瀹炴椂鐢靛帇鍜岀數娴 + // 杩欓噷闇瑕佹牴鎹疄闄匬LC閫氫俊鍗忚瀹炵幇 + double voltage = ReadVoltageFromPLC(station.IpAddress, station.Port); + double current = ReadCurrentFromPLC(station.IpAddress, station.Port); + + // 鏇存柊瀹炴椂鏁版嵁 + station.RealTimeVoltage = voltage; + station.RealTimeCurrent = current; + + dataService.UpdateStation(station, out _); + } + catch (Exception ex) + { + Diagnosis.Log($"璇诲彇鍏呯數妗 {station.Name} 瀹炴椂鏁版嵁澶辫触: {ex.Message}", + "ChargeStation", true); + } + } +} + +// 杩欎簺鏂规硶闇瑕佹牴鎹疄闄匬LC鍗忚瀹炵幇 +private double ReadVoltageFromPLC(string ip, int port) +{ + // TODO: 瀹炵幇PLC閫氫俊璇诲彇鐢靛帇 + return 0.0; +} + +private double ReadCurrentFromPLC(string ip, int port) +{ + // TODO: 瀹炵幇PLC閫氫俊璇诲彇鐢垫祦 + return 0.0; +} +``` + +--- + +## 馃攧 鏁版嵁鏇存柊娴佺▼ + +### 瀹屾暣鍏呯數娴佺▼ + +``` +1. 杞﹁締鍒拌揪鍏呯數绔 + 鈹斺攢> 鍒嗛厤绌洪棽鍏呯數妗 + 鈹斺攢> 鐘舵: Idle 鈫 Reserved + +2. 寮濮嬪厖鐢 + 鈹斺攢> 鐘舵: Reserved 鈫 Charging + 鈹斺攢> 璁剧疆瀹炴椂鏁版嵁鍒濆鍊 + 鈹溾攢> RealTimeVoltage = Voltage * 0.9 + 鈹斺攢> RealTimeCurrent = Current * 0.9 + +3. 鍏呯數涓紙瀹氭椂鏇存柊锛 + 鈹斺攢> 姣3-5绉掍粠PLC璇诲彇瀹炴椂鏁版嵁 + 鈹溾攢> 鏇存柊 RealTimeVoltage + 鈹溾攢> 鏇存柊 RealTimeCurrent + 鈹斺攢> 妫鏌ュ紓甯稿苟鍛婅 + +4. 鍏呯數瀹屾垚 + 鈹斺攢> 鐘舵: Charging 鈫 Idle + 鈹斺攢> 娓呴浂瀹炴椂鏁版嵁 + 鈹溾攢> RealTimeVoltage = 0.0 + 鈹斺攢> RealTimeCurrent = 0.0 +``` + +--- + +## 馃搵 JSON鏁版嵁鏍煎紡 + +淇濆瓨鍒版枃浠剁殑鏁版嵁鍖呭惈瀹炴椂瀛楁锛 + +```json +{ + "StationId": "CS20240115123456", + "Name": "1鍙峰厖鐢垫々", + "Type": 0, + "IpAddress": "192.168.1.100", + "Port": 502, + "Voltage": 220.0, + "Current": 32.0, + "RealTimeVoltage": 215.5, + "RealTimeCurrent": 28.3, + "Status": 1, + "Enabled": true, + "SiteId": 1001, + "Remarks": "鍗楀尯1鍙峰厖鐢垫々", + "CreatedTime": "2024-01-15T12:34:56", + "ModifiedTime": "2024-01-15T14:20:30" +} +``` + +--- + +## 鉁 浣跨敤妫鏌ユ竻鍗 + +- [x] 鏁版嵁妯″瀷娣诲姞瀹炴椂鐢靛帇鍜岀數娴佸瓧娈 +- [x] 鍒楄〃绉婚櫎鍔熺巼鍒 +- [x] 鍒楄〃娣诲姞瀹炴椂鐢靛帇鍜屽疄鏃剁數娴佸垪 +- [x] 鐣岄潰绉婚櫎鏂板鎸夐挳 +- [x] 鎸夐挳閲嶆柊鎺掑垪 +- [x] 淇濈暀璁剧疆鐢靛帇鍜岀數娴佸姛鑳 +- [x] 鍒楄〃姝g‘鏄剧ず瀹炴椂鏁版嵁 +- [x] 鏃犵紪璇戦敊璇 + +--- + +## 馃摑 鎬荤粨 + +### 鉁 瀹屾垚鐨勫姛鑳 + +1. **鏁版嵁妯″瀷** - 娣诲姞瀹炴椂鐢靛帇鍜岀數娴佸瓧娈 +2. **鍒楄〃鏄剧ず** - 绉婚櫎鍔熺巼鍒楋紝娣诲姞瀹炴椂鏁版嵁鍒 +3. **鐣岄潰浼樺寲** - 绉婚櫎鏂板鎸夐挳锛岄噸鏂版帓鍒楀叾浠栨寜閽 +4. **鍔熻兘淇濈暀** - 璁剧疆鐢靛帇鍜岀數娴佸姛鑳藉畬鍏ㄤ繚鐣 + +### 馃挕 鍚庣画闆嗘垚寤鸿 + +1. **涓嶱LC閫氫俊闆嗘垚** + - 瀹炵幇浠嶱LC璇诲彇瀹炴椂鐢靛帇鍜岀數娴 + - 瀹氭椂鏇存柊瀹炴椂鏁版嵁锛堝缓璁3-5绉掞級 + +2. **寮傚父鐩戞帶** + - 瀹炴椂鐩戞帶鐢靛帇鐢垫祦鍋忓樊 + - 瓒呰繃闃堝兼椂瑙﹀彂鍛婅 + +3. **鏁版嵁缁熻** + - 璁板綍鍏呯數杩囩▼鐨勭數鍘嬬數娴佹洸绾 + - 鍒嗘瀽鍏呯數鏁堢巼鍜屽紓甯告儏鍐 + +4. **鍙鍖栧睍绀** + - 瀹炴椂鏁版嵁鍥捐〃鏄剧ず + - 鍘嗗彶鏁版嵁瓒嬪娍鍒嗘瀽 + +**鐜板湪鎮ㄥ彲浠ュ湪鍏呯數妗╃鐞嗙晫闈腑鏌ョ湅瀹炴椂鐢靛帇鍜岀數娴佹暟鎹簡锛** 馃帀 + diff --git a/StandardScene.Core/Docs/Charge/鍏呯數妗╂姤鏂囪嚜鍔ㄦ洿鏂拌鏄.md b/StandardScene.Core/Docs/Charge/鍏呯數妗╂姤鏂囪嚜鍔ㄦ洿鏂拌鏄.md new file mode 100644 index 0000000..8f9c790 --- /dev/null +++ b/StandardScene.Core/Docs/Charge/鍏呯數妗╂姤鏂囪嚜鍔ㄦ洿鏂拌鏄.md @@ -0,0 +1,309 @@ +# 鍏呯數妗╂姤鏂囪嚜鍔ㄦ洿鏂板姛鑳借鏄 + +## 鍔熻兘姒傝堪 + +绯荤粺鐜板凡鏀寔鍙戦佸拰鎺ユ敹鍏呯數妗︰DP鎶ユ枃锛**鍒嗗埆瑙f瀽鍚庡悎骞舵洿鏂**鍏呯數妗╃鐞嗗垪琛ㄤ腑鐨勫疄鏃舵暟鎹 + +## 宸ヤ綔娴佺▼ + +``` +鍙戦佹柟鍚: 搴旂敤绋嬪簭 鈫 鍙戦佹姤鏂 鈫 CommunicationMessageService(瀛樺偍+瑙f瀽鍙戦佹暟鎹) 鈫 鏇存柊璁惧畾鍊 +鎺ユ敹鏂瑰悜: 鍏呯數妗╄澶 鈫 UDP鎶ユ枃(40001绔彛) 鈫 ChargeUdpService 鈫 CommunicationMessageService(瀛樺偍+瑙f瀽鎺ユ敹鏁版嵁) 鈫 鏇存柊瀹炴椂鏁版嵁 +鍚堝苟缁撴灉: 鍙戦佹暟鎹 + 鎺ユ敹鏁版嵁 鈫 鏁版嵁鏈嶅姟 鈫 绠$悊鐣岄潰鑷姩鍒锋柊 +``` + +## 鏍稿績缁勪欢 + +### 1. CommunicationMessageService锛堥氳鎶ユ枃鏈嶅姟锛 + +**鏂囦欢浣嶇疆**: `Charge/CommunicationMessageService.cs` + +**涓昏鍔熻兘**: +- 瀛樺偍鎵鏈夊彂閫佸拰鎺ユ敹鐨勬姤鏂囷紙鏈澶氫繚鐣100鏉★級 +- **鍒嗗紑瑙f瀽鍙戦佸拰鎺ユ敹鐨勬姤鏂囨暟鎹** +- 鏍规嵁IP鍦板潃鍖归厤瀵瑰簲鐨勫厖鐢垫々骞舵洿鏂版暟鎹 +- 鎻愪緵鎶ユ枃鏌ヨ鍜岀瓫閫夊姛鑳 + +**鍙戦佹姤鏂囪В鏋愮殑鏁版嵁**: +- 鉁 鍏呯數鎸囦护锛堝惎鍔/鍋滄锛 +- 鉁 璁惧畾鐢靛帇锛圴锛 +- 鉁 璁惧畾鐢垫祦锛圓锛 +- 鉁 鍙戦佹椂闂 + +**鎺ユ敹鎶ユ枃瑙f瀽鐨勬暟鎹**: +- 鉁 閫氳鐘舵侊紙姝e父/閿欒锛 +- 鉁 鍏呯數鎸囦护鐘舵侊紙鍚姩/鍋滄锛 +- 鉁 鏈烘瀯鐘舵侊紙浼稿嚭/缂╁洖/浼稿嚭涓/缂╁洖涓/鏁呴殰锛 +- 鉁 瀹炴椂鐢靛帇锛圴锛 +- 鉁 瀹炴椂鐢垫祦锛圓锛 +- 鉁 鐢甸噺鐧惧垎姣旓紙%锛 +- 鉁 鎶ヨ鐘舵佸拰绾у埆 +- 鉁 鍏呯數妗╃姸鎬侊紙绌洪棽/鍏呯數涓/鏁呴殰/绂荤嚎锛 +- 鉁 褰撳墠鍏呯數杞﹁締缂栧彿 +- 鉁 鎺ユ敹鏃堕棿 + +### 2. ChargeUdpService锛圲DP鐩戝惉鏈嶅姟锛 + +**鏂囦欢浣嶇疆**: `Charge/ChargeUdpService.cs` + +**鐩戝惉绔彛**: 40001 + +**宸ヤ綔娴佺▼**: +1. 鎺ユ敹UDP鎶ユ枃 +2. 璋冪敤 `CommunicationMessageService.AddReceiveMessage()` 璁板綍鎶ユ枃 +3. `CommunicationMessageService` 鑷姩瑙f瀽鎺ユ敹鎶ユ枃骞舵洿鏂板厖鐢垫々鏁版嵁 +4. 淇濇寔鍘熸湁浠诲姟澶勭悊閫昏緫鐨勫吋瀹规 + +### 3. 鍙戦佹姤鏂囧鐞 + +**鍙戦佷綅缃**: +- `ChargeStationType/MuXingChargeStation.cs` +- `ChargeStationType/FLChargeStation.cs` +- `ChargeStationType/PCBChargeStation.cs` + +**宸ヤ綔娴佺▼**: +1. 鍙戦乁DP鎶ユ枃鍒板厖鐢垫々 +2. 璋冪敤 `CommunicationMessageService.AddSendMessage()` 璁板綍鎶ユ枃 +3. `CommunicationMessageService` 鑷姩瑙f瀽鍙戦佹姤鏂囧苟鏇存柊鍏呯數妗╄瀹氬 + +### 4. ChargeStationDataService锛堟暟鎹湇鍔★級 + +**鏂板鏂规硶**: `GetStationByIp(string ipAddress)` + +**鍔熻兘**: 鏍规嵁IP鍦板潃蹇熸煡鎵惧搴旂殑鍏呯數妗╄褰 + +### 5. ChargeStationManagementForm锛堢鐞嗙晫闈級 + +**鏂板鍔熻兘**: 鑷姩鍒锋柊 + +**鍒锋柊闂撮殧**: 2绉 + +**鐗圭偣**: +- 鑷姩鏇存柊鍒楄〃鏄剧ず +- 涓嶅奖鍝嶇敤鎴风殑缂栬緫鎿嶄綔 +- 绐椾綋鍏抽棴鏃惰嚜鍔ㄥ仠姝㈠埛鏂 + +## 鎶ユ枃鏍煎紡璇存槑 + +### 褰撳墠鏀寔鐨勬姤鏂囨牸寮 + +鎶ユ枃閲囩敤閫楀彿鍒嗛殧鐨勫瓧鑺傛暟缁勬牸寮忥紝渚嬪锛 +``` +1,2,3,4,5,...,28,29,30 +``` + +### 鍙戦佹姤鏂囧瓧鑺備綅缃畾涔夛紙绀轰緥锛 + +| 瀛楄妭浣嶇疆 | 鏁版嵁鍐呭 | 璇存槑 | +|---------|---------|------| +| 5 | 鍏呯數鎸囦护 | 0=鍋滄, 1=鍚姩 | +| 6-7 | 璁惧畾鐢靛帇 | 楂樹綆瀛楄妭锛屽崟浣0.1V | +| 8-9 | 璁惧畾鐢垫祦 | 楂樹綆瀛楄妭锛屽崟浣0.1A | + +### 鎺ユ敹鎶ユ枃瀛楄妭浣嶇疆瀹氫箟锛堢ず渚嬶級 + +| 瀛楄妭浣嶇疆 | 鏁版嵁鍐呭 | 璇存槑 | +|---------|---------|------| +| 10 | 鍏呯數鎸囦护鐘舵 | 0=鍋滄, 1=鍚姩 | +| 11 | 鏈烘瀯鐘舵 | 0=鏈煡, 1=浼稿嚭, 2=缂╁洖, 3=浼稿嚭涓, 4=缂╁洖涓, 5=鏁呴殰 | +| 12-13 | 瀹炴椂鐢靛帇 | 楂樹綆瀛楄妭锛屽崟浣0.1V | +| 14-15 | 瀹炴椂鐢垫祦 | 楂樹綆瀛楄妭锛屽崟浣0.1A | +| 16 | 鐢甸噺鐧惧垎姣 | 0-100 | +| 20 | 鎶ヨ绾у埆 | 0=鏃, 1-2=浣, 3-5=涓, 6-8=楂, 9+=涓ラ噸 | +| 25 | 鍏呯數妗╃姸鎬 | 0=绌洪棽, 1=鍏呯數涓, 2=鏁呴殰, 3=绂荤嚎 | +| 26-29 | 杞﹁締缂栧彿 | 4瀛楄妭鏁存暟 | +| 28 | 閫氳鐘舵 | 1=姝e父, 鍏朵粬=閿欒 | + +**鈿狅笍 娉ㄦ剰**: 浠ヤ笂瀛楄妭浣嶇疆涓虹ず渚嬶紝闇瑕佹牴鎹疄闄呴氳鍗忚杩涜璋冩暣銆 + +## 濡備綍璋冩暣鎶ユ枃瑙f瀽瑙勫垯 + +鎵撳紑 `CommunicationMessageService.cs` 鏂囦欢锛屽垎鍒慨鏀瑰彂閫佸拰鎺ユ敹鎶ユ枃鐨勮В鏋愭柟娉曪細 + +### 璋冩暣鍙戦佹姤鏂囪В鏋 + +淇敼 `ParseSendRawData` 鏂规硶涓殑瀛楄妭浣嶇疆锛 + +```csharp +private ParsedSendData ParseSendRawData(string rawData) +{ + // ... 瀛楄妭鏁扮粍杞崲浠g爜 ... + + var parsed = new ParsedSendData + { + // 鏍规嵁瀹為檯鍗忚淇敼瀛楄妭浣嶇疆 + ChargeCommand = bytes.Length > 5 ? bytes[5] : (byte)0, + SetVoltage = bytes.Length > 7 ? (bytes[6] << 8 | bytes[7]) / 10.0 : 0, + SetCurrent = bytes.Length > 9 ? (bytes[8] << 8 | bytes[9]) / 10.0 : 0, + SendTime = DateTime.Now + }; + + return parsed; +} +``` + +### 璋冩暣鎺ユ敹鎶ユ枃瑙f瀽 + +淇敼 `ParseReceiveRawData` 鏂规硶涓殑瀛楄妭浣嶇疆锛 + +```csharp +private ParsedReceiveData ParseReceiveRawData(string rawData) +{ + // ... 瀛楄妭鏁扮粍杞崲浠g爜 ... + + var parsed = new ParsedReceiveData + { + // 鏍规嵁瀹為檯鍗忚淇敼瀛楄妭浣嶇疆 + CommStatus = bytes[28] == 1 ? CommunicationStatus.Normal : CommunicationStatus.Error, + ChargeCommandStatus = bytes[10] == 1 ? ChargeCommandStatus.Started : ChargeCommandStatus.Stopped, + // ... 鍏朵粬瀛楁 ... + ReceiveTime = DateTime.Now + }; + + return parsed; +} +``` + +## 浣跨敤绀轰緥 + +### 1. 鍚姩UDP鐩戝惉 + +```csharp +// 鍦ㄧ▼搴忓惎鍔ㄦ椂鍒涘缓UDP鏈嶅姟 +var udpService = new ChargeUdpService(); +``` + +### 2. 娣诲姞鍏呯數妗 + +鍦ㄥ厖鐢垫々绠$悊鐣岄潰涓坊鍔犲厖鐢垫々锛岀‘淇滻P鍦板潃涓庡疄闄呰澶囦竴鑷达細 + +``` +鍏呯數妗╃紪鍙: 1 +鍚嶇О: 1鍙峰厖鐢垫々 +IP鍦板潃: 192.168.1.101 鈫 蹇呴』涓庤澶嘔P涓鑷 +绔彛: 502 +``` + +### 3. 鑷姩鏇存柊 + +**鍙戦佹姤鏂囨椂**锛 +1. 搴旂敤绋嬪簭鍙戦佸厖鐢垫寚浠ゅ埌鍏呯數妗 +2. 绯荤粺璁板綍鍙戦佹姤鏂 +3. 瑙f瀽鍙戦佹姤鏂囨暟鎹紙璁惧畾鐢靛帇銆佺數娴佺瓑锛 +4. 鏍规嵁IP鍦板潃鍖归厤鍏呯數妗 +5. 鏇存柊鍏呯數妗╃殑璁惧畾鍊 + +**鎺ユ敹鎶ユ枃鏃**锛 +1. 绯荤粺鑷姩鎺ユ敹UDP鎶ユ枃锛堢鍙40001锛 +2. 璁板綍鎺ユ敹鎶ユ枃 +3. 瑙f瀽鎺ユ敹鎶ユ枃鏁版嵁锛堝疄鏃剁姸鎬併佺數鍘嬨佺數娴佺瓑锛 +4. 鏍规嵁IP鍦板潃鍖归厤鍏呯數妗 +5. 鏇存柊鍏呯數妗╃殑瀹炴椂鏁版嵁 + +**鐣岄潰鏄剧ず**锛 +- 绠$悊鐣岄潰姣2绉掕嚜鍔ㄥ埛鏂版樉绀 +- 鍚屾椂鏄剧ず璁惧畾鍊硷紙鏉ヨ嚜鍙戦佹姤鏂囷級鍜屽疄鏃跺硷紙鏉ヨ嚜鎺ユ敹鎶ユ枃锛 + +## 璋冭瘯淇℃伅 + +绯荤粺浼氬湪鏃ュ織涓緭鍑轰互涓嬩俊鎭細 + +``` +[UDP杩斿洖鎶ユ枃淇℃伅] ChargeStation ADD:[1,2,3,4,...] +[ChargeStation] 鏇存柊鍏呯數妗╂垚鍔: [1] 1鍙峰厖鐢垫々 (192.168.1.101:502) - Charging +``` + +**鏌ョ湅鎶ユ枃璁板綍**锛 +- 鎵撳紑閫氳鐩戞帶鐣岄潰鍙互鏌ョ湅鎵鏈夊彂閫佸拰鎺ユ敹鐨勬姤鏂 +- 鎶ユ枃鎸夋椂闂村掑簭鎺掑垪锛堟渶鏂扮殑鍦ㄦ渶鍓嶉潰锛 +- 鏈澶氫繚鐣100鏉℃姤鏂囪褰 + +## 甯歌闂 + +### Q1: 鎶ユ枃鎺ユ敹浜嗕絾鏁版嵁娌℃洿鏂帮紵 + +**妫鏌ラ」**: +1. 鍏呯數妗╃殑IP鍦板潃鏄惁鍦ㄧ鐞嗗垪琛ㄤ腑 +2. 鎶ユ枃鏍煎紡鏄惁姝g‘锛堣嚦灏30瀛楄妭锛 +3. 鏌ョ湅鏃ュ織涓槸鍚︽湁瑙f瀽閿欒淇℃伅 + +### Q2: 濡備綍淇敼鍒锋柊闂撮殧锛 + +鍦 `ChargeStationManagementForm.cs` 鐨 `InitializeAutoRefresh` 鏂规硶涓慨鏀癸細 + +```csharp +autoRefreshTimer.Interval = 2000; // 鏀逛负浣犻渶瑕佺殑姣鏁 +``` + +### Q3: 濡備綍鍏抽棴鑷姩鍒锋柊锛 + +```csharp +// 鍦↖nitializeAutoRefresh鏂规硶涓敞閲婃帀杩欒 +// autoRefreshTimer.Start(); +``` + +## 鎵╁睍鍔熻兘 + +### 娣诲姞鏂扮殑瑙f瀽瀛楁 + +**鍙戦佹姤鏂囨柊瀛楁**锛 +1. 鍦 `ParsedSendData` 绫讳腑娣诲姞鏂板睘鎬 +2. 鍦 `ParseSendRawData` 鏂规硶涓В鏋愭柊瀛楁 +3. 鍦 `UpdateStationFromSendData` 鏂规硶涓洿鏂板埌鍏呯數妗╁璞 + +**鎺ユ敹鎶ユ枃鏂板瓧娈**锛 +1. 鍦 `ParsedReceiveData` 绫讳腑娣诲姞鏂板睘鎬 +2. 鍦 `ParseReceiveRawData` 鏂规硶涓В鏋愭柊瀛楁 +3. 鍦 `UpdateStationFromReceiveData` 鏂规硶涓洿鏂板埌鍏呯數妗╁璞 + +### 鏀寔鍏朵粬閫氳鍗忚 + +鍦 `CommunicationMessageService.cs` 涓彲浠ユ牴鎹鍙e彿鎴栧叾浠栫壒寰佸垽鏂崗璁被鍨嬶細 + +```csharp +public void AddReceiveMessage(string ipAddress, int port, string rawData, string stationId = null) +{ + // ... 娣诲姞鎶ユ枃璁板綍 ... + + // 鏍规嵁绔彛鍒ゆ柇鍗忚绫诲瀷 + if (port == 40001) + { + ParseReceiveDataAndUpdateStation(ipAddress, rawData); // UDP鍗忚 + } + else if (port == 502) + { + ParseModbusReceiveAndUpdate(ipAddress, rawData); // Modbus鍗忚 + } +} + +public void AddSendMessage(string ipAddress, int port, string rawData, string stationId = null) +{ + // ... 娣诲姞鎶ユ枃璁板綍 ... + + // 鏍规嵁绔彛鍒ゆ柇鍗忚绫诲瀷 + if (port == 40001) + { + ParseSendDataAndUpdateStation(ipAddress, rawData); // UDP鍗忚 + } + else if (port == 502) + { + ParseModbusSendAndUpdate(ipAddress, rawData); // Modbus鍗忚 + } +} +``` + +## 鎶鏈壒鐐 + +鉁 **瀹炴椂鎬**: UDP鎶ユ枃鎺ユ敹鍚庣珛鍗宠В鏋愭洿鏂 +鉁 **鑷姩鍖**: 鏃犻渶鎵嬪姩鍒锋柊锛屾暟鎹嚜鍔ㄥ悓姝 +鉁 **鍒嗙瑙f瀽**: 鍙戦佸拰鎺ユ敹鎶ユ枃鍒嗗紑瑙f瀽锛屾暟鎹洿鍑嗙‘ +鉁 **鍚堝苟鏇存柊**: 鑷姩鍚堝苟鍙戦佸拰鎺ユ敹鏁版嵁鍒板厖鐢垫々绠$悊鐣岄潰 +鉁 **鍙墿灞**: 鏀寔鑷畾涔夋姤鏂囨牸寮忓拰瑙f瀽瑙勫垯 +鉁 **鍏煎鎬**: 淇濈暀鍘熸湁浠诲姟澶勭悊閫昏緫 +鉁 **绋冲畾鎬**: 寮傚父澶勭悊瀹屽杽锛屼笉褰卞搷绯荤粺杩愯 + +## 鐗堟湰鍘嗗彶 + +- **v1.1** (2026-01-18): 鍙戦佸拰鎺ユ敹鎶ユ枃鍒嗗紑瑙f瀽锛屽悎骞舵洿鏂板埌鍏呯數妗╃鐞嗙晫闈 +- **v1.0** (2026-01-18): 鍒濆鐗堟湰锛屾敮鎸乁DP鎶ユ枃鑷姩瑙f瀽鍜屾洿鏂 + diff --git a/StandardScene.Core/Docs/Charge/鍏呯數绔橦MI&绠℃帶鎺ュ彛-pcb鏉挎洿鏂癡8-鏇存柊IP&AP璁惧畾2023骞9鏈27鏃..docx b/StandardScene.Core/Docs/Charge/鍏呯數绔橦MI&绠℃帶鎺ュ彛-pcb鏉挎洿鏂癡8-鏇存柊IP&AP璁惧畾2023骞9鏈27鏃..docx new file mode 100644 index 0000000..9747998 Binary files /dev/null and b/StandardScene.Core/Docs/Charge/鍏呯數绔橦MI&绠℃帶鎺ュ彛-pcb鏉挎洿鏂癡8-鏇存柊IP&AP璁惧畾2023骞9鏈27鏃..docx differ diff --git a/StandardScene.Core/Docs/Charge/鍏呯數绔欑鎺ф帴鍙-鏂板鎶ヨ鐮240113.xlsx b/StandardScene.Core/Docs/Charge/鍏呯數绔欑鎺ф帴鍙-鏂板鎶ヨ鐮240113.xlsx new file mode 100644 index 0000000..c1451f5 Binary files /dev/null and b/StandardScene.Core/Docs/Charge/鍏呯數绔欑鎺ф帴鍙-鏂板鎶ヨ鐮240113.xlsx differ diff --git a/StandardScene.Core/Docs/Charge/鍏呯數绛栫暐閰嶇疆璇存槑.md b/StandardScene.Core/Docs/Charge/鍏呯數绛栫暐閰嶇疆璇存槑.md new file mode 100644 index 0000000..01b50d6 --- /dev/null +++ b/StandardScene.Core/Docs/Charge/鍏呯數绛栫暐閰嶇疆璇存槑.md @@ -0,0 +1,265 @@ +# 鍏呯數绛栫暐閰嶇疆璇存槑 + +## 鍔熻兘姒傝堪 + +鍏呯數绛栫暐閰嶇疆鐣岄潰鐢ㄤ簬绠$悊鍜岄厤缃厖鐢电郴缁熺殑鍚勯」鍙傛暟锛屽寘鎷 SOC 闃堝笺佹椂闂村弬鏁般佷换鍔″弬鏁板拰寮鍏冲弬鏁般傞厤缃繚瀛樺湪 JSON 鏂囦欢涓紝绯荤粺鍚姩鏃惰嚜鍔ㄥ姞杞姐 + +## 鎵撳紑閰嶇疆鐣岄潰 + +鍦**鍏呯數妗╃鐞嗙晫闈**鐐瑰嚮 **"绛栫暐閰嶇疆"** 鎸夐挳锛堢豢鑹叉寜閽級鍗冲彲鎵撳紑閰嶇疆鐣岄潰銆 + +## 閰嶇疆鍙傛暟璇存槑 + +### 1. SOC 鍙傛暟锛堢數閲忕櫨鍒嗘瘮锛 + +| 鍙傛暟鍚嶇О | 榛樿鍊 | 璇存槑 | 鍙栧艰寖鍥 | +|---------|-------|------|---------| +| 蹇呭厖鐢甸噺 | 20% | 浣庝簬姝ょ數閲忓繀椤诲厖鐢 | 0-100% | +| 绌洪棽鍏呯數鐢甸噺 | 90% | 杞﹁締绌洪棽鏃跺紑濮嬪厖鐢电殑鐢甸噺闃堝 | 0-100% | +| 浠诲姟鍙敤鐢甸噺 | 60% | 鍙互鎵ц浠诲姟鐨勬渶浣庣數閲 | 0-100% | +| 婊$數鐢甸噺 | 90% | 鍏呯數鐩爣鐢甸噺 | 0-100% | +| 鍏佽涓柇鐢甸噺 | 45% | 鍏佽涓柇鍏呯數浠诲姟鐨勬渶浣庣數閲 | 0-100% | + +**閫昏緫鍏崇郴**锛 +- 蹇呭厖鐢甸噺 < 绌洪棽鍏呯數鐢甸噺 +- 浠诲姟鍙敤鐢甸噺 > 蹇呭厖鐢甸噺 +- 婊$數鐢甸噺 鈮 绌洪棽鍏呯數鐢甸噺 +- 鍏佽涓柇鐢甸噺 > 蹇呭厖鐢甸噺 + +### 2. 鏃堕棿鍙傛暟 + +| 鍙傛暟鍚嶇О | 榛樿鍊 | 璇存槑 | 鍗曚綅 | +|---------|-------|------|-----| +| 绌洪棽鍏呯數鏃堕棿 | 30 | 杞﹁締绌洪棽澶氫箙鍚庡紑濮嬪厖鐢 | 绉 (sec) | +| 绌洪棽鏃堕棿 | 5 | 鍒ゆ柇杞﹁締绌洪棽鐨勬椂闂撮槇鍊 | 绉 (sec) | +| 蹇呭厖鏃堕棿 | 60 | 蹇呴』鍏呯數鐨勬寔缁椂闂 | 绉 (sec) | +| 琛ョ數鏃堕棿 | 5 | 琛ョ數鎿嶄綔鐨勬寔缁椂闂 | 鍒嗛挓 (min) | + +### 3. 浠诲姟鍙傛暟 + +| 鍙傛暟鍚嶇О | 榛樿鍊 | 璇存槑 | +|---------|-------|------| +| 鍏佽绌洪棽杞﹀厖鐢电殑鏈灏忎换鍔℃暟 | 0 | 褰撲换鍔℃暟閲忓ぇ浜庢鍊兼椂锛屽厑璁哥┖闂茶溅杈嗗厖鐢 | + +### 4. 寮鍏冲弬鏁 + +| 鍙傛暟鍚嶇О | 榛樿鍊 | 璇存槑 | +|---------|-------|------| +| 鍏佽涓柇鍏呯數浠诲姟 | 鍚 | 鏄惁鍏佽涓柇姝e湪杩涜鐨勫厖鐢典换鍔 | +| 浼樺厛浣跨敤浣庣數閲忚溅杈嗗厖鐢 | 鏄 | 浼樺厛閫夋嫨鐢甸噺杈冧綆鐨勮溅杈嗚繘琛屽厖鐢 | +| 鍚敤鍏呯數閿欒妫娴 | 鍚 | 鏄惁鍚敤鍏呯數杩囩▼涓殑閿欒妫娴 | +| 浣跨敤鍏呯數绔欑偣绛涢 | 鍚 | 鏄惁鏍规嵁绔欑偣绛涢夊厖鐢垫々 | + +## 鎿嶄綔璇存槑 + +### 淇濆瓨閰嶇疆 + +1. 淇敼鎵闇鍙傛暟 +2. 鐐瑰嚮 **"淇濆瓨"** 鎸夐挳 +3. 绯荤粺浼氶獙璇佸弬鏁扮殑鏈夋晥鎬 +4. 楠岃瘉閫氳繃鍚庝繚瀛樺埌閰嶇疆鏂囦欢 + +閰嶇疆鏂囦欢浣嶇疆锛歚Config/ChargeStrategyConfig.json` + +### 搴旂敤閰嶇疆 + +鐐瑰嚮 **"搴旂敤"** 鎸夐挳鍙互淇濆瓨閰嶇疆浣嗕笉鍏抽棴绐楀彛锛屾柟渚跨户缁皟鏁村弬鏁般 + +### 鎭㈠榛樿閰嶇疆 + +1. 鐐瑰嚮 **"鎭㈠榛樿"** 鎸夐挳 +2. 纭鎭㈠鎿嶄綔 +3. 鎵鏈夊弬鏁版仮澶嶄负榛樿鍊 +4. **娉ㄦ剰**锛氭仮澶嶅悗闇瑕佺偣鍑"淇濆瓨"鎵嶄細鐢熸晥 + +### 鍙栨秷淇敼 + +鐐瑰嚮 **"鍙栨秷"** 鎸夐挳鍏抽棴绐楀彛锛屼笉淇濆瓨浠讳綍淇敼銆 + +## 閰嶇疆鏂囦欢鏍煎紡 + +閰嶇疆浠 JSON 鏍煎紡淇濆瓨锛岀ず渚嬶細 + +```json +{ + "MustChargeSoc": 20.0, + "IdleChargeSoc": 90.0, + "TaskAvailableSoc": 60.0, + "FullChargeSoc": 90.0, + "AllowInterruptSoc": 45.0, + "IdleChargeSeconds": 30.0, + "IdleSeconds": 5.0, + "MustChargeSeconds": 60.0, + "TopUpMinutes": 5.0, + "MinAllowFreeCarToChargeTaskCnt": 0, + "AllowInterruptTask": false, + "UseLowerSocForCharge": true, + "EnableErrorChargeDetection": false, + "UseChargeSiteFilter": false +} +``` + +## 鍙傛暟楠岃瘉瑙勫垯 + +绯荤粺浼氬湪淇濆瓨鏃惰嚜鍔ㄩ獙璇侀厤缃殑鏈夋晥鎬э細 + +### SOC 鍙傛暟楠岃瘉 +- 鉁 鎵鏈 SOC 鍊煎繀椤诲湪 0-100 涔嬮棿 +- 鉁 蹇呭厖鐢甸噺 < 绌洪棽鍏呯數鐢甸噺 +- 鉁 浠诲姟鍙敤鐢甸噺 > 蹇呭厖鐢甸噺 +- 鉁 婊$數鐢甸噺 鈮 绌洪棽鍏呯數鐢甸噺 +- 鉁 鍏佽涓柇鐢甸噺 > 蹇呭厖鐢甸噺 + +### 鏃堕棿鍙傛暟楠岃瘉 +- 鉁 鎵鏈夋椂闂村煎繀椤 鈮 0 + +### 浠诲姟鍙傛暟楠岃瘉 +- 鉁 鏈灏忎换鍔℃暟蹇呴』 鈮 0 + +## 浣跨敤鍦烘櫙绀轰緥 + +### 鍦烘櫙 1锛氱揣鎬ヤ换鍔℃ā寮 +閫傜敤浜庝换鍔$揣鎬ワ紝闇瑕佸揩閫熷懆杞溅杈嗙殑鎯呭喌銆 + +``` +蹇呭厖鐢甸噺: 15% +绌洪棽鍏呯數鐢甸噺: 80% +浠诲姟鍙敤鐢甸噺: 50% +婊$數鐢甸噺: 85% +鍏佽涓柇鍏呯數浠诲姟: 鏄 +``` + +### 鍦烘櫙 2锛氳妭鑳芥ā寮 +閫傜敤浜庝换鍔′笉绱фワ紝浼樺厛淇濊瘉鐢垫睜瀵垮懡鐨勬儏鍐点 + +``` +蹇呭厖鐢甸噺: 25% +绌洪棽鍏呯數鐢甸噺: 95% +浠诲姟鍙敤鐢甸噺: 70% +婊$數鐢甸噺: 95% +鍏佽涓柇鍏呯數浠诲姟: 鍚 +``` + +### 鍦烘櫙 3锛氬钩琛℃ā寮忥紙榛樿锛 +骞宠 浠诲姟鏁堢巼鍜岀數姹犲鍛姐 + +``` +蹇呭厖鐢甸噺: 20% +绌洪棽鍏呯數鐢甸噺: 90% +浠诲姟鍙敤鐢甸噺: 60% +婊$數鐢甸噺: 90% +鍏佽涓柇鍏呯數浠诲姟: 鍚 +``` + +## 閰嶇疆鐢熸晥鏃舵満 + +- **绔嬪嵆鐢熸晥**锛氫繚瀛橀厤缃悗绔嬪嵆鐢熸晥 +- **鑷姩鍔犺浇**锛氱郴缁熷惎鍔ㄦ椂鑷姩鍔犺浇閰嶇疆 +- **瀹炴椂鏇存柊**锛氬厖鐢甸昏緫浼氬疄鏃惰鍙栨渶鏂伴厤缃 + +## 甯歌闂 + +### Q1: 淇敼閰嶇疆鍚庢病鏈夌敓鏁堬紵 + +**妫鏌ラ」**锛 +1. 纭宸茬偣鍑"淇濆瓨"鎸夐挳 +2. 妫鏌ョ姸鎬佹爮鏄惁鏄剧ず"閰嶇疆淇濆瓨鎴愬姛" +3. 鏌ョ湅閰嶇疆鏂囦欢鏄惁宸叉洿鏂 + +### Q2: 閰嶇疆鏂囦欢涓㈠け鎬庝箞鍔烇紵 + +绯荤粺浼氳嚜鍔ㄥ垱寤洪粯璁ら厤缃枃浠讹紝鏃犻渶鎷呭績銆 + +### Q3: 濡備綍澶囦唤閰嶇疆锛 + +閰嶇疆鏂囦欢浣嶄簬 `Config/ChargeStrategyConfig.json`锛岀洿鎺ュ鍒舵鏂囦欢鍗冲彲澶囦唤銆 + +### Q4: 鍙傛暟楠岃瘉澶辫触鎬庝箞鍔烇紵 + +鏍规嵁閿欒鎻愮ず璋冩暣鍙傛暟锛岀‘淇濇弧瓒虫墍鏈夐獙璇佽鍒欍 + +### Q5: 鍙互鎵嬪姩缂栬緫閰嶇疆鏂囦欢鍚楋紵 + +鍙互锛屼絾寤鸿浣跨敤閰嶇疆鐣岄潰锛屽洜涓虹晫闈細鑷姩楠岃瘉鍙傛暟鏈夋晥鎬с + +## 鎶鏈粏鑺 + +### 閰嶇疆鏈嶅姟锛堝崟渚嬫ā寮忥級 + +```csharp +var configService = ChargeStrategyConfigService.Instance; +var config = configService.LoadConfig(); +configService.SaveConfig(config); +``` + +### 閰嶇疆妯″瀷 + +```csharp +public class ChargeStrategyConfig +{ + // SOC 鍙傛暟 + public double MustChargeSoc { get; set; } + public double IdleChargeSoc { get; set; } + // ... 鍏朵粬鍙傛暟 + + // 楠岃瘉鏂规硶 + public bool Validate(out string errorMessage); +} +``` + +### 閰嶇疆鏂囦欢璺緞 + +- **Windows**: `搴旂敤绋嬪簭鐩綍\Config\ChargeStrategyConfig.json` +- **鑷姩鍒涘缓**: 棣栨杩愯鏃惰嚜鍔ㄥ垱寤洪厤缃洰褰曞拰榛樿閰嶇疆鏂囦欢 + +## 鐣岄潰甯冨眬 + +``` +鈹屸攢鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹 +鈹 鍏呯數绛栫暐閰嶇疆 鈹 +鈹溾攢鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹 +鈹 鈹屸攢 SOC 鍙傛暟 鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹 鈹 +鈹 鈹 蹇呭厖鐢甸噺: [20.0] % 绌洪棽鍏呯數鐢甸噺: [90.0] % 鈹 鈹 +鈹 鈹 浠诲姟鍙敤鐢甸噺: [60.0] % 婊$數鐢甸噺: [90.0] % 鈹 鈹 +鈹 鈹 鍏佽涓柇鐢甸噺: [45.0] % 鈹 鈹 +鈹 鈹斺攢鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹 鈹 +鈹 鈹 +鈹 鈹屸攢 鏃堕棿鍙傛暟 鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹 鈹 +鈹 鈹 绌洪棽鍏呯數鏃堕棿: [30.0] 绉 绌洪棽鏃堕棿: [5.0] 绉 鈹 鈹 +鈹 鈹 蹇呭厖鏃堕棿: [60.0] 绉 琛ョ數鏃堕棿: [5.0] 鍒嗛挓 鈹 鈹 +鈹 鈹斺攢鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹 鈹 +鈹 鈹 +鈹 鈹屸攢 浠诲姟鍙傛暟 鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹 鈹 +鈹 鈹 鍏佽绌洪棽杞﹀厖鐢电殑鏈灏忎换鍔℃暟: [0] 鈹 鈹 +鈹 鈹斺攢鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹 鈹 +鈹 鈹 +鈹 鈹屸攢 寮鍏冲弬鏁 鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹 鈹 +鈹 鈹 鈽 鍏佽涓柇鍏呯數浠诲姟 鈽 浼樺厛浣跨敤浣庣數閲忚溅杈嗗厖鐢 鈹 鈹 +鈹 鈹 鈽 鍚敤鍏呯數閿欒妫娴 鈽 浣跨敤鍏呯數绔欑偣绛涢 鈹 鈹 +鈹 鈹斺攢鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹 鈹 +鈹溾攢鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹 +鈹 灏辩华... [鎭㈠榛樿] [淇濆瓨] [搴旂敤] [鍙栨秷] 鈹 +鈹斺攢鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹 +``` + +## 娉ㄦ剰浜嬮」 + +鈿狅笍 **鍙傛暟璋冩暣寤鸿**锛 +1. 涓嶅缓璁绻佷慨鏀归厤缃 +2. 淇敼鍓嶅缓璁浠藉綋鍓嶉厤缃 +3. 淇敼鍚庤瀵熺郴缁熻繍琛屾儏鍐 +4. 鏍规嵁瀹為檯鎯呭喌閫愭璋冩暣鍙傛暟 + +鈿狅笍 **瀹夊叏鎻愮ず**锛 +1. 蹇呭厖鐢甸噺涓嶅疁璁剧疆杩囦綆锛堝缓璁 鈮 15%锛 +2. 婊$數鐢甸噺涓嶅疁璁剧疆杩囬珮锛堝缓璁 鈮 95%锛 +3. 鍏佽涓柇浠诲姟闇璋ㄦ厧寮鍚 + +## 鐗堟湰鍘嗗彶 + +- **v1.0** (2026-01-25): 鍒濆鐗堟湰锛屾敮鎸佹墍鏈夊厖鐢电瓥鐣ュ弬鏁伴厤缃 + +--- + +**鎻愮ず**锛氶厤缃晫闈㈡彁渚涗簡瀹屾暣鐨勫弬鏁伴獙璇佸拰榛樿鍊兼仮澶嶅姛鑳斤紝寤鸿閫氳繃鐣岄潰杩涜閰嶇疆绠$悊銆 + diff --git a/StandardScene.Core/Docs/Charge/瀹屾暣鏂囦欢娓呭崟.md b/StandardScene.Core/Docs/Charge/瀹屾暣鏂囦欢娓呭崟.md new file mode 100644 index 0000000..0f1204f --- /dev/null +++ b/StandardScene.Core/Docs/Charge/瀹屾暣鏂囦欢娓呭崟.md @@ -0,0 +1,527 @@ +# 馃攲 鍏呯數妗╃鐞嗙郴缁 - 瀹屾暣鏂囦欢娓呭崟 + +## 馃搧 宸插垱寤虹殑鏂囦欢 + +### 鏍稿績鏂囦欢锛堝繀闇锛 + +| 鏂囦欢鍚 | 绫诲瀷 | 璇存槑 | 琛屾暟 | +|--------|------|------|------| +| **ChargeStation.cs** | 鏁版嵁妯″瀷 | 鍏呯數妗╁疄浣撶被锛屽寘鍚墍鏈夊睘鎬у拰楠岃瘉閫昏緫 | ~150 | +| **ChargeStationDataService.cs** | 鏁版嵁鏈嶅姟 | 鍗曚緥妯″紡鏁版嵁绠$悊绫伙紝璐熻矗澧炲垹鏀规煡鍜屾寔涔呭寲 | ~300 | +| **ChargeStationManagementForm.cs** | UI涓荤被 | 绠$悊绐楀彛鐨勪笟鍔¢昏緫鍜屼簨浠跺鐞 | ~350 | +| **ChargeStationManagementForm.Designer.cs** | UI璁捐 | 绐楀彛鎺т欢鐨勫垵濮嬪寲鍜屽竷灞浠g爜 | ~550 | + +### 杈呭姪鏂囦欢锛堝彲閫変絾鎺ㄨ崘锛 + +| 鏂囦欢鍚 | 绫诲瀷 | 璇存槑 | 琛屾暟 | +|--------|------|------|------| +| **ChargeStationHelper.cs** | 宸ュ叿绫 | 鎻愪緵闈欐佽緟鍔╂柟娉曪紝绠鍖栬皟鐢 | ~350 | +| **ChargeStationManagementExample.cs** | 绀轰緥浠g爜 | 10涓娇鐢ㄧず渚嬶紝鍖呭惈瀹屾暣鐨勮皟鐢ㄤ唬鐮 | ~400 | + +### 鏂囨。鏂囦欢 + +| 鏂囦欢鍚 | 绫诲瀷 | 璇存槑 | +|--------|------|------| +| **README_ChargeStationManagement.md** | 瀹屾暣鏂囨。 | 璇︾粏鐨勫姛鑳借鏄庛丄PI鏂囨。鍜屼娇鐢ㄦ寚鍗 | +| **QUICKSTART.md** | 蹇熷紑濮 | 5鍒嗛挓蹇熶笂鎵嬫寚鍗楋紝鍖呭惈闆嗘垚姝ラ | +| **INTERFACE_LAYOUT.txt** | 鐣岄潰璇存槑 | ASCII鑹烘湳鏍煎紡鐨勭晫闈㈠竷灞鍜屾搷浣滆鏄 | +| **瀹屾暣鏂囦欢娓呭崟.md** | 鏈枃浠 | 鎵鏈夋枃浠剁殑娓呭崟鍜屼娇鐢ㄨ鏄 | + +--- + +## 馃幆 鏂囦欢鍔熻兘璇﹁В + +### 1. ChargeStation.cs - 鍏呯數妗╂暟鎹ā鍨 + +**鍔熻兘**锛 +- 瀹氫箟鍏呯數妗╃殑鎵鏈夊睘鎬э紙缂栧彿銆佸悕绉般両P銆佺鍙c佺數鍘嬨佺數娴佺瓑锛 +- 鎻愪緵鏁版嵁楠岃瘉鏂规硶 `IsValid()` +- 鑷姩璁$畻鍔熺巼 `Power` +- 鑷姩鐢熸垚鍞竴缂栧彿 `GenerateStationId()` + +**鍏抽敭灞炴**锛 +```csharp +public string StationId { get; set; } // 鍞竴缂栧彿 +public string Name { get; set; } // 鍚嶇О +public string IpAddress { get; set; } // IP鍦板潃 +public int Port { get; set; } // 绔彛 +public double Voltage { get; set; } // 鐢靛帇(V) +public double Current { get; set; } // 鐢垫祦(A) +public ChargeStationStatus Status { get; set; } // 鐘舵 +public bool Enabled { get; set; } // 鏄惁鍚敤 +public int? SiteId { get; set; } // 绔欑偣ID +public double Power => Voltage * Current; // 鍔熺巼(W) +``` + +**鐘舵佹灇涓**锛 +```csharp +public enum ChargeStationStatus { + Idle = 0, // 绌洪棽 + Charging = 1, // 鍏呯數涓 + Fault = 2, // 鏁呴殰 + Offline = 3, // 绂荤嚎 + Maintenance = 4, // 缁存姢涓 + Reserved = 5 // 棰勭害涓 +} +``` + +--- + +### 2. ChargeStationDataService.cs - 鏁版嵁鏈嶅姟 + +**鍔熻兘**锛 +- 鍗曚緥妯″紡锛屽叏灞鍞竴瀹炰緥 +- 鏁版嵁鎸佷箙鍖栵紙JSON鏍煎紡锛 +- 绾跨▼瀹夊叏锛堜娇鐢ㄩ攣鏈哄埗锛 +- CRUD鎿嶄綔锛堝鍒犳敼鏌ワ級 + +**鏍稿績鏂规硶**锛 +```csharp +// 鍗曚緥鑾峰彇 +ChargeStationDataService.Instance + +// 鏌ヨ +List GetAllStations() +ChargeStation GetStationById(string stationId) +List GetIdleStations() +int GetChargingCount() + +// 娣诲姞 +bool AddStation(ChargeStation station, out string errorMessage) + +// 鏇存柊 +bool UpdateStation(ChargeStation station, out string errorMessage) +bool UpdateStationStatus(string stationId, ChargeStationStatus status) + +// 鍒犻櫎 +bool DeleteStation(string stationId, out string errorMessage) + +// 鍒锋柊 +void Reload() +``` + +**鏁版嵁瀛樺偍浣嶇疆**锛 +``` +椤圭洰鏍圭洰褰/Data/ChargeStations.json +``` + +--- + +### 3. ChargeStationManagementForm.cs - 绠$悊绐楀彛 + +**鍔熻兘**锛 +- 鍏呯數妗╁垪琛ㄦ樉绀猴紙DataGridView锛 +- 瀹炴椂鎼滅储 +- 娣诲姞/缂栬緫/鍒犻櫎鍏呯數妗 +- 鏁版嵁瀵煎嚭锛圝SON/CSV锛 +- 缁熻淇℃伅鏄剧ず +- 鐘舵侀鑹叉爣璇 + +**涓昏鏂规硶**锛 +```csharp +private void LoadStations() // 鍔犺浇鏁版嵁鍒板垪琛 +private void UpdateStatistics() // 鏇存柊缁熻淇℃伅 +private void btnAdd_Click() // 鏂板鎸夐挳 +private void btnSave_Click() // 淇濆瓨鎸夐挳 +private void btnDelete_Click() // 鍒犻櫎鎸夐挳 +private void btnRefresh_Click() // 鍒锋柊鎸夐挳 +private void btnExport_Click() // 瀵煎嚭鎸夐挳 +private void dgvStations_CellDoubleClick() // 鍙屽嚮缂栬緫 +private void txtSearch_TextChanged() // 鎼滅储 +``` + +**鐣岄潰甯冨眬**锛 +- 宸︿晶锛氬厖鐢垫々鍒楄〃 + 鎼滅储 + 缁熻 +- 鍙充晶锛氱紪杈戝尯 + 鎿嶄綔鎸夐挳 +- 灏哄锛1200脳700锛堝彲璋冩暣锛屾渶灏1000脳600锛 + +--- + +### 4. ChargeStationManagementForm.Designer.cs - UI璁捐鏂囦欢 + +**鍔熻兘**锛 +- 鑷姩鐢熸垚鐨勮璁″櫒浠g爜 +- 鍖呭惈鎵鏈夋帶浠剁殑鍒濆鍖 +- 涓嶅缓璁墜鍔ㄤ慨鏀 + +**涓昏鎺т欢**锛 +```csharp +SplitContainer splitContainer // 鍒嗗壊瀹瑰櫒 +DataGridView dgvStations // 鏁版嵁琛ㄦ牸 +TextBox txtSearch // 鎼滅储妗 +TextBox txtName, txtIpAddress // 鏂囨湰妗 +NumericUpDown numPort, numVoltage // 鏁板瓧杈撳叆妗 +ComboBox cmbStatus // 涓嬫媺妗 +CheckBox chkEnabled // 澶嶉夋 +Button btnAdd, btnSave, btnDelete // 鎸夐挳 +Label lblStatistics, lblPower // 鏍囩 +``` + +--- + +### 5. ChargeStationHelper.cs - 杈呭姪宸ュ叿绫 + +**鍔熻兘**锛 +- 鎻愪緵闈欐佽緟鍔╂柟娉 +- 绠鍖栧父鐢ㄦ搷浣 +- 灏佽澶嶆潅閫昏緫 + +**甯哥敤鏂规硶**锛 +```csharp +// 鎵撳紑绠$悊绐楀彛 +ChargeStationHelper.OpenManagementWindow() + +// 鑾峰彇鍏呯數妗 +ChargeStation station = ChargeStationHelper.GetStationBySiteId(1001) +ChargeStation station = ChargeStationHelper.GetStationByIp("192.168.1.100") + +// 妫鏌ュ彲鐢ㄦ +bool available = ChargeStationHelper.IsSiteHasAvailableChargeStation(1001) + +// 鍏呯數鎺у埗 +bool success = ChargeStationHelper.StartCharging("CS123456", carId) +bool success = ChargeStationHelper.StopCharging("CS123456", carId) + +// 鏁呴殰鏍囪 +bool success = ChargeStationHelper.MarkAsFault("CS123456", "閫氫俊瓒呮椂") + +// 鑾峰彇鐘舵佹憳瑕 +string summary = ChargeStationHelper.GetStatusSummary() +// 杈撳嚭: "鎬绘暟:10 | 绌洪棽:6 | 鍏呯數涓:3 | 鏁呴殰:1 | 绂荤嚎:0" + +// 鏌ユ壘鏈杩戠殑绌洪棽鍏呯數妗 +ChargeStation station = ChargeStationHelper.FindNearestIdleStation(currentSiteId) + +// 蹇熷垱寤哄厖鐢垫々 +bool success = ChargeStationHelper.QuickAddStation("1鍙峰厖鐢垫々", "192.168.1.100", 1001) + +// 鏄剧ず閫夋嫨瀵硅瘽妗 +ChargeStation selected = ChargeStationHelper.ShowStationSelectionDialog(ChargeStationStatus.Idle) + +// 鎵归噺鏇存柊鍦ㄧ嚎鐘舵 +int updatedCount = ChargeStationHelper.UpdateOnlineStatus(timeout: 3000) +``` + +--- + +### 6. ChargeStationManagementExample.cs - 绀轰緥浠g爜 + +**鍔熻兘**锛 +- 鎻愪緵10涓畬鏁寸殑浣跨敤绀轰緥 +- 姣忎釜鏂规硶閮藉甫鏈 `[MethodMember]` 灞炴э紝鍙湪绯荤粺涓洿鎺ヨ皟鐢 + +**绀轰緥鍒楄〃**锛 + +| 鏂规硶鍚 | 璇存槑 | +|--------|------| +| `OpenManagementForm()` | 鎵撳紑鍏呯數妗╃鐞嗙獥鍙 | +| `InitializeTestData()` | 鍒濆鍖4涓祴璇曞厖鐢垫々 | +| `ShowIdleStations()` | 鏄剧ず鎵鏈夌┖闂插厖鐢垫々 | +| `ShowChargeStationStatistics()` | 鏄剧ず缁熻淇℃伅 | +| `AssignChargeStationToCar()` | 涓鸿溅杈嗗垎閰嶅厖鐢垫々 | +| `StartCharging()` | 寮濮嬪厖鐢 | +| `StopCharging()` | 缁撴潫鍏呯數 | +| `CheckChargeStationOnlineStatus()` | 妫鏌ュ湪绾跨姸鎬 | +| `ExportChargeStationData()` | 瀵煎嚭鏁版嵁 | +| `ClearAllChargeStationData()` | 娓呯┖鎵鏈夋暟鎹 | + +--- + +## 馃摉 浣跨敤鎸囧崡 + +### 鏂瑰紡1锛氬揩閫熷紑濮嬶紙鎺ㄨ崘鏂版墜锛 + +1. **闃呰蹇熷紑濮嬫枃妗** + ``` + 鎵撳紑: QUICKSTART.md + ``` + +2. **鍦ㄤ富绐楀彛娣诲姞鑿滃崟椤** + ```csharp + var menuItem = new ToolStripMenuItem("鍏呯數妗╃鐞"); + menuItem.Click += (s, e) => { + ChargeStationHelper.OpenManagementWindow(); + }; + ``` + +3. **鍒濆鍖栨祴璇曟暟鎹** + ```csharp + ChargeStationManagementExample.InitializeTestData(); + ``` + +4. **鎵撳紑绠$悊绐楀彛娴嬭瘯** + - 鐐瑰嚮鑿滃崟椤 + - 鏌ョ湅娴嬭瘯鏁版嵁 + - 灏濊瘯娣诲姞/缂栬緫/鍒犻櫎 + +### 鏂瑰紡2锛氶泦鎴愬埌鐜版湁浠g爜锛堟帹鑽愰珮绾х敤鎴凤級 + +1. **闃呰瀹屾暣鏂囨。** + ``` + 鎵撳紑: README_ChargeStationManagement.md + ``` + +2. **鍦ㄥ厖鐢典换鍔′腑闆嗘垚** + ```csharp + // 鍦 AbstractChargeMission.cs 涓 + using StandardScene.Charge; + + // 閫夋嫨鍏呯數绔欑偣鏃 + var dataService = ChargeStationDataService.Instance; + var idleStations = dataService.GetIdleStations(); + + // 鍒拌揪鍏呯數绔欐椂 + ChargeStationHelper.StartCharging(stationId, carId); + + // 绂诲紑鍏呯數绔欐椂 + ChargeStationHelper.StopCharging(stationId, carId); + ``` + +3. **娣诲姞瀹炴椂鐩戞帶** + ```csharp + // 鍦ㄤ富绐楀彛娣诲姞瀹氭椂鍣 + private Timer statusTimer = new Timer { Interval = 3000 }; + statusTimer.Tick += (s, e) => { + lblStatus.Text = ChargeStationHelper.GetStatusSummary(); + }; + statusTimer.Start(); + ``` + +### 鏂瑰紡3锛氬弬鑰冪ず渚嬩唬鐮侊紙鎺ㄨ崘瀛︿範锛 + +1. **鏌ョ湅绀轰緥浠g爜** + ``` + 鎵撳紑: ChargeStationManagementExample.cs + ``` + +2. **杩愯绀轰緥鏂规硶** + ```csharp + // 鐩存帴璋冪敤绀轰緥鏂规硶 + ChargeStationManagementExample.ShowIdleStations(); + ChargeStationManagementExample.ShowChargeStationStatistics(); + ``` + +3. **鏍规嵁闇姹備慨鏀** + - 澶嶅埗绀轰緥浠g爜 + - 鏍规嵁瀹為檯闇姹傝皟鏁 + - 闆嗘垚鍒伴」鐩腑 + +--- + +## 馃敡 閰嶇疆璇存槑 + +### 鏁版嵁鏂囦欢閰嶇疆 + +**浣嶇疆**锛 +``` +椤圭洰鏍圭洰褰/Data/ChargeStations.json +``` + +**鏍煎紡**锛 +```json +[ + { + "StationId": "CS20240115123456", + "Name": "1鍙峰厖鐢垫々", + "IpAddress": "192.168.1.100", + "Port": 502, + "Voltage": 220.0, + "Current": 32.0, + "Status": 0, + "Enabled": true, + "SiteId": 1001, + "Remarks": "鍗楀尯1鍙峰厖鐢垫々", + "CreatedTime": "2024-01-15T12:34:56", + "ModifiedTime": "2024-01-15T14:20:30" + } +] +``` + +### 鏉冮檺瑕佹眰 + +- `Data` 鏂囦欢澶归渶瑕**璇诲啓鏉冮檺** +- 濡傛灉淇濆瓨澶辫触锛屾鏌ユ枃浠跺す鏉冮檺 + +### 鎬ц兘閰嶇疆 + +- 鏁版嵁閲 < 100涓厖鐢垫々锛氭棤闇浼樺寲 +- 鏁版嵁閲 > 100涓厖鐢垫々锛氳冭檻鍒嗛〉鏄剧ず +- 鎼滅储鎬ц兘锛氬疄鏃舵悳绱紝鏃犻渶浼樺寲 + +--- + +## 馃帹 鐣岄潰瀹氬埗 + +### 淇敼绐楀彛澶у皬 + +鍦 `ChargeStationManagementForm.Designer.cs` 涓細 +```csharp +this.Size = new Size(1400, 800); // 淇敼涓轰綘闇瑕佺殑灏哄 +``` + +### 淇敼鎸夐挳棰滆壊 + +```csharp +btnAdd.BackColor = Color.LightGreen; +btnSave.BackColor = Color.LightBlue; +btnDelete.BackColor = Color.LightCoral; +``` + +### 淇敼瀛椾綋 + +```csharp +this.Font = new Font("寰蒋闆呴粦", 10F); +``` + +--- + +## 馃悰 鏁呴殰鎺掗櫎 + +### 闂1锛氱獥鍙f墦涓嶅紑 + +**鍘熷洜**锛氬懡鍚嶇┖闂村紩鐢ㄩ敊璇 + +**瑙e喅**锛 +```csharp +using StandardScene.Charge; +``` + +### 闂2锛氭暟鎹繚瀛樺け璐 + +**鍘熷洜**锛氭枃浠跺す鏉冮檺涓嶈冻 + +**瑙e喅**锛 +1. 鍙抽敭 `Data` 鏂囦欢澶 +2. 灞炴 鈫 瀹夊叏 +3. 纭繚褰撳墠鐢ㄦ埛鏈"鍐欏叆"鏉冮檺 + +### 闂3锛氭壘涓嶅埌鏁版嵁 + +**鍘熷洜**锛氶娆¤繍琛屾湭鍒濆鍖 + +**瑙e喅**锛 +```csharp +ChargeStationManagementExample.InitializeTestData(); +``` + +### 闂4锛氱紪璇戦敊璇 + +**鍘熷洜**锛氱己灏戜緷璧栭」 + +**瑙e喅**锛 +- 纭繚瀹夎 `Newtonsoft.Json` NuGet 鍖 +- 妫鏌ラ」鐩紩鐢 + +--- + +## 馃搳 绯荤粺瑕佹眰 + +### 杞欢瑕佹眰 +- .NET Framework 4.5 鎴栨洿楂樼増鏈 +- Windows Forms +- Newtonsoft.Json锛圢uGet锛 + +### 纭欢瑕佹眰 +- 鍐呭瓨锛氭暟鎹噺灏忥紝鍑犱箮鏃犲奖鍝 +- 纾佺洏锛氭瘡涓厖鐢垫々绾 1KB 鏁版嵁 +- CPU锛歎I鎿嶄綔锛屽嚑涔庢棤褰卞搷 + +### 鍏煎鎬 +- Windows 7/8/10/11 +- 涓庣幇鏈 AGV 绯荤粺瀹屽叏鍏煎 +- 涓嶅奖鍝嶇幇鏈夊姛鑳 + +--- + +## 馃殌 涓嬩竴姝ヨ鍒 + +### 宸插畬鎴愬姛鑳 鉁 +- [x] 鍏呯數妗╂暟鎹ā鍨 +- [x] 鏁版嵁鎸佷箙鍖栵紙JSON锛 +- [x] 鍙鍖栫鐞嗙晫闈 +- [x] 澧炲垹鏀规煡鍔熻兘 +- [x] 鎼滅储鍜岃繃婊 +- [x] 鏁版嵁瀵煎嚭 +- [x] 杈呭姪宸ュ叿绫 +- [x] 瀹屾暣鏂囨。鍜岀ず渚 + +### 鍙墿灞曞姛鑳 馃挕 +- [ ] 鍏呯數妗╁疄鏃剁洃鎺э紙閫氫俊鐘舵侊級 +- [ ] 鍏呯數鍘嗗彶璁板綍 +- [ ] 鍏呯數鏇茬嚎鍥捐〃 +- [ ] 鍏呯數璁¤垂绠$悊 +- [ ] 鍏呯數妗╁垎缁勭鐞 +- [ ] 鏉冮檺鎺у埗锛堜笉鍚岀敤鎴蜂笉鍚屾潈闄愶級 +- [ ] 杩滅▼鎺у埗锛堝惎鍔/鍋滄鍏呯數锛 +- [ ] 鍛婅鎺ㄩ侊紙鏁呴殰/绂荤嚎锛 +- [ ] 鏁版嵁鍒嗘瀽锛堝厖鐢垫晥鐜囩粺璁★級 +- [ ] 涓庣幇鏈 PLC 绯荤粺闆嗘垚 + +--- + +## 馃摓 鎶鏈敮鎸 + +### 鏂囨。浣嶇疆 +- **瀹屾暣鏂囨。**: `README_ChargeStationManagement.md` +- **蹇熷紑濮**: `QUICKSTART.md` +- **鐣岄潰璇存槑**: `INTERFACE_LAYOUT.txt` +- **鏈枃浠**: `瀹屾暣鏂囦欢娓呭崟.md` + +### 绀轰緥浠g爜 +- **宸ュ叿绫**: `ChargeStationHelper.cs` +- **绀轰緥浠g爜**: `ChargeStationManagementExample.cs` + +### 鏃ュ織璋冭瘯 +```csharp +// 鏌ョ湅鍏呯數妗╃浉鍏虫棩蹇 +// 鏃ュ織鏍囩: "ChargeStation" +``` + +--- + +## 鉁 妫鏌ユ竻鍗 + +鍦ㄩ儴缃插埌鐢熶骇鐜鍓嶏紝璇风‘璁わ細 + +- [ ] 鎵鏈夋枃浠堕兘宸叉坊鍔犲埌椤圭洰 +- [ ] `Newtonsoft.Json` NuGet 鍖呭凡瀹夎 +- [ ] `Data` 鏂囦欢澶规湁璇诲啓鏉冮檺 +- [ ] 宸插湪涓荤獥鍙f坊鍔犺彍鍗曢」鎴栨寜閽 +- [ ] 宸插垵濮嬪寲娴嬭瘯鏁版嵁骞舵祴璇 +- [ ] 绠$悊绐楀彛鍙互姝e父鎵撳紑 +- [ ] 澧炲垹鏀规煡鍔熻兘姝e父 +- [ ] 鏁版嵁淇濆瓨鍜屽姞杞芥甯 +- [ ] 鎼滅储鍔熻兘姝e父 +- [ ] 瀵煎嚭鍔熻兘姝e父 +- [ ] 宸查槄璇诲畬鏁存枃妗 +- [ ] 宸叉祴璇曚笌鐜版湁绯荤粺鐨勯泦鎴 + +--- + +## 馃摑 鐗堟湰淇℃伅 + +**褰撳墠鐗堟湰**: v1.0.0 +**鍙戝竷鏃ユ湡**: 2024-01-15 +**寮鍙戣**: MDCS System +**璁稿彲**: 鍐呴儴浣跨敤 + +--- + +## 馃帀 鎭枩锛 + +鎮ㄥ凡缁忚幏寰椾簡涓涓畬鏁寸殑鍏呯數妗╃鐞嗙郴缁燂紒 + +**蹇熷紑濮**锛 +1. 鎵撳紑 `QUICKSTART.md` +2. 鎸夌収姝ラ鎿嶄綔 +3. 5鍒嗛挓鍐呭嵆鍙紑濮嬩娇鐢 + +**闇瑕佸府鍔╋紵** +- 鏌ョ湅鏂囨。 +- 杩愯绀轰緥浠g爜 +- 妫鏌ユ棩蹇楄緭鍑 + +绁濇偍浣跨敤鎰夊揩锛 馃殌 + + + diff --git a/StandardScene.Core/Docs/Charge/鎶ユ枃瑙f瀽浣跨敤绀轰緥.md b/StandardScene.Core/Docs/Charge/鎶ユ枃瑙f瀽浣跨敤绀轰緥.md new file mode 100644 index 0000000..d510c2e --- /dev/null +++ b/StandardScene.Core/Docs/Charge/鎶ユ枃瑙f瀽浣跨敤绀轰緥.md @@ -0,0 +1,320 @@ +# 鍏呯數妗╂姤鏂囧垎绂昏В鏋愪娇鐢ㄧず渚 + +## 姒傝堪 + +绯荤粺鐜板湪鏀寔**鍙戦佹姤鏂**鍜**鎺ユ敹鎶ユ枃**鍒嗗紑瑙f瀽锛岀劧鍚庤嚜鍔ㄥ悎骞舵洿鏂板埌鍏呯數妗╃鐞嗙晫闈€ + +## 鏁版嵁娴佸悜鍥 + +``` +鈹屸攢鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹 +鈹 搴旂敤绋嬪簭 鈹 +鈹斺攢鈹鈹鈹鈹鈹鈹鈹鈹攢鈹鈹鈹鈹鈹鈹鈹鈹 + 鈹 鍙戦佸厖鐢垫寚浠 + 鈻 +鈹屸攢鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹 +鈹 CommunicationMessageService 鈹 +鈹 鈹屸攢鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹 鈹 +鈹 鈹 AddSendMessage() 鈹 鈹 +鈹 鈹 鈹溾攢 璁板綍鍙戦佹姤鏂 鈹 鈹 +鈹 鈹 鈹斺攢 ParseSendDataAndUpdateStation() 鈹 鈹 +鈹 鈹 鈹溾攢 瑙f瀽璁惧畾鐢靛帇 鈹 鈹 +鈹 鈹 鈹溾攢 瑙f瀽璁惧畾鐢垫祦 鈹 鈹 +鈹 鈹 鈹斺攢 鏇存柊鍏呯數妗╄瀹氬 鈹 鈹 +鈹 鈹斺攢鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹 鈹 +鈹斺攢鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹攢鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹 + 鈹 + 鈻 + 鈹屸攢鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹 + 鈹 ChargeStation 鈹 鈼勨攢鈹鈹 璁惧畾鍊煎凡鏇存柊 + 鈹 SetVoltage 鈹 + 鈹 SetCurrent 鈹 + 鈹斺攢鈹鈹鈹鈹鈹鈹鈹鈹攢鈹鈹鈹鈹鈹鈹鈹 + 鈹 + 鈹 鍚屾椂... + 鈹 +鈹屸攢鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈻尖攢鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹 +鈹 ChargeUdpService (鐩戝惉绔彛40001) 鈹 +鈹 鈹溾攢 鎺ユ敹鍏呯數妗╄繑鍥炵殑UDP鎶ユ枃 鈹 +鈹 鈹斺攢 璋冪敤 AddReceiveMessage() 鈹 +鈹斺攢鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹攢鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹 + 鈹 + 鈻 +鈹屸攢鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹 +鈹 CommunicationMessageService 鈹 +鈹 鈹屸攢鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹 鈹 +鈹 鈹 AddReceiveMessage() 鈹 鈹 +鈹 鈹 鈹溾攢 璁板綍鎺ユ敹鎶ユ枃 鈹 鈹 +鈹 鈹 鈹斺攢 ParseReceiveDataAndUpdateStation()鈹 鈹 +鈹 鈹 鈹溾攢 瑙f瀽瀹炴椂鐢靛帇 鈹 鈹 +鈹 鈹 鈹溾攢 瑙f瀽瀹炴椂鐢垫祦 鈹 鈹 +鈹 鈹 鈹溾攢 瑙f瀽鍏呯數鐘舵 鈹 鈹 +鈹 鈹 鈹溾攢 瑙f瀽鏈烘瀯鐘舵 鈹 鈹 +鈹 鈹 鈹斺攢 鏇存柊鍏呯數妗╁疄鏃舵暟鎹 鈹 鈹 +鈹 鈹斺攢鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹 鈹 +鈹斺攢鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹攢鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹 + 鈹 + 鈻 + 鈹屸攢鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹 + 鈹 ChargeStation 鈹 鈼勨攢鈹鈹 瀹炴椂鍊煎凡鏇存柊 + 鈹 RealTimeVoltage鈹 + 鈹 RealTimeCurrent鈹 + 鈹 Status 鈹 + 鈹 MechanismStatus鈹 + 鈹斺攢鈹鈹鈹鈹鈹鈹鈹鈹攢鈹鈹鈹鈹鈹鈹鈹 + 鈹 + 鈻 + 鈹屸攢鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹 + 鈹 ChargeStationManagementForm 鈹 + 鈹 (姣2绉掕嚜鍔ㄥ埛鏂) 鈹 + 鈹 鏄剧ず锛 鈹 + 鈹 - 璁惧畾鐢靛帇 vs 瀹炴椂鐢靛帇 鈹 + 鈹 - 璁惧畾鐢垫祦 vs 瀹炴椂鐢垫祦 鈹 + 鈹 - 鍏呯數鐘舵 鈹 + 鈹 - 鏈烘瀯鐘舵 鈹 + 鈹斺攢鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹鈹 +``` + +## 浠g爜绀轰緥 + +### 1. 鍙戦佸厖鐢垫寚浠わ紙鑷姩瑙f瀽鍙戦佹姤鏂囷級 + +```csharp +// 鍦ㄥ厖鐢垫々绫讳腑鍙戦佸厖鐢垫寚浠 +public void StartCharging(double voltage, double current) +{ + // 鏋勯犲彂閫佹姤鏂 + byte[] sendData = new byte[10]; + sendData[5] = 1; // 鍏呯數鎸囦护锛氬惎鍔 + sendData[6] = (byte)((int)(voltage * 10) >> 8); // 璁惧畾鐢靛帇楂樺瓧鑺 + sendData[7] = (byte)((int)(voltage * 10) & 0xFF); // 璁惧畾鐢靛帇浣庡瓧鑺 + sendData[8] = (byte)((int)(current * 10) >> 8); // 璁惧畾鐢垫祦楂樺瓧鑺 + sendData[9] = (byte)((int)(current * 10) & 0xFF); // 璁惧畾鐢垫祦浣庡瓧鑺 + + // 鍙戦乁DP鎶ユ枃 + udpClient.Send(sendData, sendData.Length, endPoint); + + // 璁板綍鍙戦佹姤鏂囷紙鑷姩瑙f瀽骞舵洿鏂拌瀹氬硷級 + var messageService = CommunicationMessageService.Instance; + messageService.AddSendMessage( + ipAddress: endPoint.Address.ToString(), + port: endPoint.Port, + rawData: string.Join(",", sendData), + stationId: this.StationId + ); + + // 鉁 姝ゆ椂鍏呯數妗╃殑 SetVoltage 鍜 SetCurrent 宸茶嚜鍔ㄦ洿鏂 +} +``` + +### 2. 鎺ユ敹鍏呯數妗╁弽棣堬紙鑷姩瑙f瀽鎺ユ敹鎶ユ枃锛 + +```csharp +// 鍦 ChargeUdpService 涓帴鏀舵姤鏂 +private static async void ListenerProcess() +{ + var messageService = CommunicationMessageService.Instance; + using (UdpClient udpListener = new UdpClient(40001)) + { + while (true) + { + var result = await udpListener.ReceiveAsync(); + var remoteEndPoint = result.RemoteEndPoint; + var message = result.Buffer; + + // 璁板綍鎺ユ敹鎶ユ枃锛堣嚜鍔ㄨВ鏋愬苟鏇存柊瀹炴椂鏁版嵁锛 + messageService.AddReceiveMessage( + ipAddress: remoteEndPoint.Address.ToString(), + port: 40001, + rawData: string.Join(",", message) + ); + + // 鉁 姝ゆ椂鍏呯數妗╃殑瀹炴椂鏁版嵁宸茶嚜鍔ㄦ洿鏂帮細 + // - RealTimeVoltage锛堝疄鏃剁數鍘嬶級 + // - RealTimeCurrent锛堝疄鏃剁數娴侊級 + // - Status锛堝厖鐢电姸鎬侊級 + // - MechanismStatus锛堟満鏋勭姸鎬侊級 + // - BatteryLevel锛堢數閲忕櫨鍒嗘瘮锛 + // - 绛夌瓑... + } + } +} +``` + +### 3. 鍦ㄥ厖鐢垫々绠$悊鐣岄潰鏌ョ湅鍚堝苟鍚庣殑鏁版嵁 + +```csharp +// 鍦 ChargeStationManagementForm 涓樉绀烘暟鎹 +private void LoadStations() +{ + var stations = ChargeStationDataService.Instance.GetAllStations(); + + foreach (var station in stations) + { + // 鏄剧ず璁惧畾鍊硷紙鏉ヨ嚜鍙戦佹姤鏂囪В鏋愶級 + Console.WriteLine($"璁惧畾鐢靛帇: {station.SetVoltage}V"); + Console.WriteLine($"璁惧畾鐢垫祦: {station.SetElectricCurrent}A"); + + // 鏄剧ず瀹炴椂鍊硷紙鏉ヨ嚜鎺ユ敹鎶ユ枃瑙f瀽锛 + Console.WriteLine($"瀹炴椂鐢靛帇: {station.RealTimeVoltage}V"); + Console.WriteLine($"瀹炴椂鐢垫祦: {station.RealTimeCurrent}A"); + Console.WriteLine($"鍏呯數鐘舵: {GetStatusText(station.Status)}"); + Console.WriteLine($"鏈烘瀯鐘舵: {GetMechanismStatusText(station.MechanismStatus)}"); + Console.WriteLine($"鐢甸噺: {station.BatteryLevel}%"); + + // 鏄剧ず閫氳鏃堕棿 + Console.WriteLine($"鏈鍚庡彂閫: {station.LastSendTime}"); + Console.WriteLine($"鏈鍚庢帴鏀: {station.LastReceiveTime}"); + } +} +``` + +## 瑙f瀽娴佺▼璇﹁В + +### 鍙戦佹姤鏂囪В鏋愭祦绋 + +``` +AddSendMessage() + 鈫 +ParseSendDataAndUpdateStation() + 鈫 +ParseSendRawData() 鈫 瑙f瀽鍙戦佹姤鏂 + 鈹溾攢 ChargeCommand (瀛楄妭5) + 鈹溾攢 SetVoltage (瀛楄妭6-7) + 鈹斺攢 SetCurrent (瀛楄妭8-9) + 鈫 +UpdateStationFromSendData() 鈫 鏇存柊璁惧畾鍊 + 鈹溾攢 station.SetVoltage = parsedData.SetVoltage + 鈹溾攢 station.SetElectricCurrent = parsedData.SetCurrent + 鈹溾攢 station.LastSendTime = parsedData.SendTime + 鈹斺攢 station.ChargeCommandStatus = ... +``` + +### 鎺ユ敹鎶ユ枃瑙f瀽娴佺▼ + +``` +AddReceiveMessage() + 鈫 +ParseReceiveDataAndUpdateStation() + 鈫 +ParseReceiveRawData() 鈫 瑙f瀽鎺ユ敹鎶ユ枃 + 鈹溾攢 CommStatus (瀛楄妭28) + 鈹溾攢 ChargeCommandStatus (瀛楄妭10) + 鈹溾攢 MechanismStatus (瀛楄妭11) + 鈹溾攢 RealTimeVoltage (瀛楄妭12-13) + 鈹溾攢 RealTimeCurrent (瀛楄妭14-15) + 鈹溾攢 BatteryLevel (瀛楄妭16) + 鈹溾攢 HasAlarm (瀛楄妭20) + 鈹溾攢 Status (瀛楄妭25) + 鈹斺攢 CurrentVehicleId (瀛楄妭26-29) + 鈫 +UpdateStationFromReceiveData() 鈫 鏇存柊瀹炴椂鍊 + 鈹溾攢 station.RealTimeVoltage = parsedData.RealTimeVoltage + 鈹溾攢 station.RealTimeCurrent = parsedData.RealTimeCurrent + 鈹溾攢 station.Status = parsedData.Status + 鈹溾攢 station.MechanismStatus = parsedData.MechanismStatus + 鈹溾攢 station.BatteryLevel = parsedData.BatteryLevel + 鈹溾攢 station.LastReceiveTime = parsedData.ReceiveTime + 鈹斺攢 ... +``` + +## 鏁版嵁瀵规瘮绀轰緥 + +| 鏁版嵁椤 | 鏉ユ簮 | 鏇存柊鏃舵満 | 鐢ㄩ | +|-------|------|---------|------| +| SetVoltage | 鍙戦佹姤鏂 | 鍙戦佸厖鐢垫寚浠ゆ椂 | 鏄剧ず璁惧畾鐨勭洰鏍囩數鍘 | +| RealTimeVoltage | 鎺ユ敹鎶ユ枃 | 鎺ユ敹鍏呯數妗╁弽棣堟椂 | 鏄剧ず褰撳墠瀹為檯鐢靛帇 | +| SetElectricCurrent | 鍙戦佹姤鏂 | 鍙戦佸厖鐢垫寚浠ゆ椂 | 鏄剧ず璁惧畾鐨勭洰鏍囩數娴 | +| RealTimeCurrent | 鎺ユ敹鎶ユ枃 | 鎺ユ敹鍏呯數妗╁弽棣堟椂 | 鏄剧ず褰撳墠瀹為檯鐢垫祦 | +| ChargeCommandStatus | 鍙戦+鎺ユ敹 | 鍙戦佹寚浠ゆ椂鏇存柊锛屾帴鏀跺弽棣堟椂纭 | 鏄剧ず鍏呯數鎸囦护鎵ц鐘舵 | +| Status | 鎺ユ敹鎶ユ枃 | 鎺ユ敹鍏呯數妗╁弽棣堟椂 | 鏄剧ず鍏呯數妗╁綋鍓嶇姸鎬 | +| MechanismStatus | 鎺ユ敹鎶ユ枃 | 鎺ユ敹鍏呯數妗╁弽棣堟椂 | 鏄剧ず鏈烘瀯浼哥缉鐘舵 | +| BatteryLevel | 鎺ユ敹鎶ユ枃 | 鎺ユ敹鍏呯數妗╁弽棣堟椂 | 鏄剧ず鐢垫睜鐢甸噺鐧惧垎姣 | +| LastSendTime | 鍙戦佹姤鏂 | 鍙戦佸厖鐢垫寚浠ゆ椂 | 鏄剧ず鏈鍚庡彂閫佹椂闂 | +| LastReceiveTime | 鎺ユ敹鎶ユ枃 | 鎺ユ敹鍏呯數妗╁弽棣堟椂 | 鏄剧ず鏈鍚庢帴鏀舵椂闂 | + +## 浼樺娍 + +鉁 **鏁版嵁鍒嗙**锛氬彂閫佸拰鎺ユ敹鏁版嵁鍚勮嚜鐙珛锛屼笉浼氫簰鐩歌鐩 +鉁 **瀹屾暣璁板綍**锛氬悓鏃朵繚鐣欒瀹氬煎拰瀹炴椂鍊硷紝渚夸簬瀵规瘮鍒嗘瀽 +鉁 **鑷姩鍚堝苟**锛氱郴缁熻嚜鍔ㄥ皢涓ょ鏁版嵁鍚堝苟鍒板悓涓涓厖鐢垫々瀵硅薄 +鉁 **瀹炴椂鏇存柊**锛氱晫闈㈡瘡2绉掕嚜鍔ㄥ埛鏂帮紝鏄剧ず鏈鏂版暟鎹 +鉁 **鏄撲簬璋冭瘯**锛氬彲浠ユ竻妤氱湅鍒板彂閫佺殑鎸囦护鍜屾帴鏀剁殑鍙嶉 + +## 璋冭瘯鎶宸 + +### 1. 鏌ョ湅鎶ユ枃璁板綍 + +```csharp +var messageService = CommunicationMessageService.Instance; + +// 鏌ョ湅鎵鏈夋姤鏂 +var allMessages = messageService.GetAllMessages(); + +// 鏌ョ湅鏌愪釜IP鐨勬姤鏂 +var ipMessages = messageService.GetMessagesByIp("192.168.1.101"); + +// 鏌ョ湅鏌愪釜鍏呯數妗╃殑鎶ユ枃 +var stationMessages = messageService.GetMessagesByStationId("1"); + +foreach (var msg in allMessages) +{ + Console.WriteLine($"[{msg.Direction}] {msg.IpAddress}:{msg.Port}"); + Console.WriteLine($"鏃堕棿: {msg.Timestamp}"); + Console.WriteLine($"鏁版嵁: {msg.RawData}"); + Console.WriteLine("---"); +} +``` + +### 2. 瀵规瘮璁惧畾鍊间笌瀹炴椂鍊 + +```csharp +var station = ChargeStationDataService.Instance.GetStationByIp("192.168.1.101"); + +if (station != null) +{ + // 鐢靛帇瀵规瘮 + double voltageDiff = Math.Abs(station.SetVoltage - station.RealTimeVoltage); + Console.WriteLine($"鐢靛帇鍋忓樊: {voltageDiff}V"); + + // 鐢垫祦瀵规瘮 + double currentDiff = Math.Abs(station.SetElectricCurrent - station.RealTimeCurrent); + Console.WriteLine($"鐢垫祦鍋忓樊: {currentDiff}A"); + + // 閫氳寤惰繜 + if (station.LastSendTime != null && station.LastReceiveTime != null) + { + var delay = station.LastReceiveTime.Value - station.LastSendTime.Value; + Console.WriteLine($"閫氳寤惰繜: {delay.TotalMilliseconds}ms"); + } +} +``` + +### 3. 鐩戞帶瑙f瀽閿欒 + +濡傛灉鏁版嵁娌℃湁鏇存柊锛屾鏌ヤ互涓嬪嚑鐐癸細 + +1. **IP鍦板潃鏄惁鍖归厤**锛氱‘淇濆厖鐢垫々鐨処P鍦板潃鍦ㄧ鐞嗗垪琛ㄤ腑 +2. **鎶ユ枃闀垮害鏄惁瓒冲**锛氬彂閫佹姤鏂囪嚦灏10瀛楄妭锛屾帴鏀舵姤鏂囪嚦灏30瀛楄妭 +3. **瀛楄妭浣嶇疆鏄惁姝g‘**锛氭牴鎹疄闄呭崗璁皟鏁村瓧鑺備綅缃 +4. **鏁版嵁绫诲瀷鏄惁姝g‘**锛氭鏌ラ珮浣庡瓧鑺傞『搴忋佸崟浣嶆崲绠楃瓑 + +## 娉ㄦ剰浜嬮」 + +鈿狅笍 **瀛楄妭浣嶇疆**锛氱ず渚嬩腑鐨勫瓧鑺備綅缃粎渚涘弬鑰冿紝璇锋牴鎹疄闄呴氳鍗忚璋冩暣 +鈿狅笍 **鎶ユ枃鏍煎紡**锛氱‘淇濆彂閫佸拰鎺ユ敹鐨勬姤鏂囨牸寮忎笌瑙f瀽瑙勫垯涓鑷 +鈿狅笍 **寮傚父澶勭悊**锛氳В鏋愬け璐ヤ笉浼氬奖鍝嶆姤鏂囪褰曪紝浣嗘暟鎹笉浼氭洿鏂 +鈿狅笍 **绾跨▼瀹夊叏**锛歚CommunicationMessageService` 浣跨敤鍗曚緥妯″紡锛屽唴閮ㄥ凡鍋氱嚎绋嬪悓姝 + +## 鎬荤粨 + +閫氳繃鍒嗙瑙f瀽鍙戦佸拰鎺ユ敹鎶ユ枃锛岀郴缁熷彲浠ワ細 +- 鍑嗙‘璁板綍姣忔鍙戦佺殑鎸囦护鍙傛暟 +- 鍑嗙‘鑾峰彇鍏呯數妗╃殑瀹炴椂鍙嶉 +- 鑷姩鍚堝苟涓ょ鏁版嵁鍒板厖鐢垫々绠$悊鐣岄潰 +- 渚夸簬瀵规瘮鍒嗘瀽鍜屾晠闅滆瘖鏂 + +杩欑璁捐浣垮緱鍏呯數妗╃鐞嗘洿鍔犵簿纭拰鍙潬锛 + + + diff --git a/StandardScene.Core/Docs/Charge/娴嬭瘯鏁版嵁.txt b/StandardScene.Core/Docs/Charge/娴嬭瘯鏁版嵁.txt new file mode 100644 index 0000000..74f537f --- /dev/null +++ b/StandardScene.Core/Docs/Charge/娴嬭瘯鏁版嵁.txt @@ -0,0 +1,26 @@ + +UDP测试数据 +[2026/01/16-16:11:55.679] >PCBsendChargeSite: 3075 IP:192.168.100.72: BB F6 01 00 00 03 20 00 00 01 22 00 1E 00 05 4D FF FF FD EE 00 00 01 0E 00 00 00 00 00 00 F6 EE +[2026/01/16-16:11:56.260] >PCBsendChargeSite: 3075 IP:192.168.100.72: BB F7 01 00 00 03 20 00 00 01 22 00 1E 00 05 4D FF FF FD EE 00 00 01 0E 00 00 00 00 00 80 F5 EE +[2026/01/16-16:11:56.835] >PCBsendChargeSite: 3075 IP:192.168.100.72: BB F8 01 00 00 03 20 00 00 01 22 00 1E 00 05 4D FF FF FD EE 00 00 01 0E 00 00 00 00 00 80 EE EE +[2026/01/16-16:11:57.394] >PCBsendChargeSite: 3075 IP:192.168.100.72: BB F9 01 00 00 03 20 00 00 01 22 00 1E 00 05 4D FF FF FD EE 00 00 01 0E 00 00 00 00 00 00 ED EE + +[2026/01/16-16:14:49.300] >PCBsendChargeSite: 3075 IP:192.168.100.72: BB 28 00 00 00 03 20 00 00 01 22 00 1E 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 04 38 EE +[2026/01/16-16:14:49.843] >PCBsendChargeSite: 3075 IP:192.168.100.72: BB 29 00 00 00 03 20 00 00 01 22 00 1E 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 84 3B EE +[2026/01/16-16:14:50.410] >PCBsendChargeSite: 3075 IP:192.168.100.72: BB 2A 00 00 00 03 20 00 00 01 22 00 1E 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 44 3D EE +[2026/01/16-16:14:50.943] >PCBsendChargeSite: 3075 IP:192.168.100.72: BB 2B 00 00 00 03 20 00 00 01 22 00 1E 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 C4 3E EE + +[2026/01/19-17:35:58.294] >IsSafe:False ChargeIP: 192.168.100.74: BB 74 00 00 03 28 00 00 01 22 00 00 00 00 01 29 00 0B 00 00 00 00 00 00 00 00 00 00 02 00 00 EE +[2026/01/19-17:35:58.885] >IsSafe:False ChargeIP: 192.168.100.74: BB 75 00 00 03 28 00 00 01 22 00 00 00 00 01 29 00 0B 00 00 00 00 00 00 00 00 00 00 02 00 00 EE +[2026/01/19-17:35:59.465] >IsSafe:False ChargeIP: 192.168.100.74: BB 76 00 00 03 28 00 00 01 22 00 00 00 00 01 29 00 0B 00 00 00 00 00 00 00 00 00 00 02 00 00 EE +[2026/01/19-17:36:00.020] >IsSafe:False ChargeIP: 192.168.100.74: BB 77 00 00 03 28 00 00 01 22 00 00 00 00 01 29 00 0B 00 00 00 00 00 00 00 00 00 00 02 00 00 EE + + +TCP 发送指令 +BB 00 42 48 00 00 42 5C 00 00 03 E8 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 EE // 启动 +BB 00 42 48 00 00 42 5C 00 00 03 E8 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 EE // 停止 +TCP返回指令 +BB 00 00 00 00 00 00 00 00 00 00 00 00 00 03 00 00 00 00 00 00 00 00 00 00 00 00 00 01 00 00 EE //充电 +BB 00 00 00 00 00 00 00 00 00 00 00 00 00 02 00 00 00 00 00 00 00 00 00 00 00 00 00 02 00 00 EE //缩回 +BB 00 00 00 00 00 00 00 00 00 00 00 00 00 02 00 00 00 00 00 00 00 00 00 00 00 00 00 03 00 00 EE //充电 + diff --git a/StandardScene.Core/Docs/CoderSettings姹囨.md b/StandardScene.Core/Docs/CoderSettings姹囨.md new file mode 100644 index 0000000..dd8e571 --- /dev/null +++ b/StandardScene.Core/Docs/CoderSettings姹囨.md @@ -0,0 +1,254 @@ +# 脚本生成特性汇总表 + +> 本文档汇总了工程中所有的 `TemplateTrackCoderSettings`、`TemplateSiteCoderSettings` 和 `ProgramTrackCoderSettings` 特性配置。 + +--- + +## 关键参数说明 + +| 参数 | 说明 | +|------|------| +| **priority** | 优先级,数值越大则脚本生成顺序越靠前 | +| **useVerb** | 判断条件,为 true 时生成 templateString | +| **blockVerb** | 如果为 true,且 useVerb 通过,则低于此 priority 的脚本不再继续判断 | +| **templateString** | 生成的脚本模板,支持 `${变量}` 语法 | +| **siteFields** | 站点字段类型定义 | +| **trackFields** | 路径字段类型定义 | +| **planFields** | 计划字段类型定义 | + +--- + +## 执行流程说明 + +1. 按 **priority 从大到小** 依次检查每个 CoderSettings +2. 如果 **useVerb** 条件为 true,则生成 **templateString** 脚本 +3. 如果 **blockVerb** 为 true 且 useVerb 通过,则 **阻止低优先级脚本继续执行** +4. ProgramTrackCoderSettings 使用自定义的 `ITrackCoder` 程序来生成脚本 + +--- + +## 一、TemplateTrackCoderSettings(路径脚本) + +### 1. MultiWheelLifterCar.cs - 多舵轮顶升车 + +| Priority | useVerb | blockVerb | templateString | 说明 | +|----------|---------|-----------|----------------|------| +| 30 | `track.SleepTime!=0` | false | `agv.Wait();agv.Sleep(${track.SleepTime});agv.Wait();` | 路径上休眠 | +| 30 | `track.TrayTarget!=0` | false | `agv.Wait();agv.TrayControl(${track.TrayTarget});agv.Wait();` | 托盘控制 | +| 27 | `track.LidarArea != -2` | - | `agv.Queue(()=>{},()=>{ agv.SwitchLidarArea(${track.LidarArea}); });` | 切换激光避障区域 | +| 20 | (无条件) | - | `agv.Queue(()=>{},()=>{ agv.ChangeAvoidanceDistance(${track.StopDistance},${track.SlowDistance}); });` | 更改避障距离 | +| 20 | `track.IOArea != -1` | - | `agv.Queue(()=>{},()=>{ agv.SwitchIoArea(${track.IOArea}); });` | 切换IO区域 | +| 20 | `dst.ChangeAvoidanceParam==true` | - | `agv.Queue(()=>{},()=>{ agv.ChangeAvoidanceParam(${dst.CarLength},${dst.CarWidth},${dst.CarCenterX},${dst.CarCenterY}); });` | 切换避障尺寸 | +| 20 | `track.BiasAlarmThresh >0 \|\| track.DthAlarmThresh > 0` | - | `agv.Queue(()=>{},()=>{ agv.ChangeTrackingErrThresh(${track.BiasAlarmThresh},${track.DthAlarmThresh}); });` | 更改跟踪误差阈值 | +| 19 | `dst.tag>0 && src.tag>0` | true | `agv.QrGo(${src.x},${src.y},${src.id},${src.tag},${dst.x},${dst.y},${dst.id},${dst.tag},${track.id},...);` | 二维码导航 | + +--- + +### 2. Kiva.cs - Kiva车 + +| Priority | useVerb | blockVerb | templateString | 说明 | +|----------|---------|-----------|----------------|------| +| 30 | `dst.tag>0 && src.tag>0` | true | `agv.QrGo(...);` | 二维码导航 | +| 22 | `src.Shelf && plan.curSeg==1` | true | `agv.LeaveShelf(...);` | 离开货架 | +| 20 | `plan.action=='fetch' && dst.Shelf && plan.curSeg==plan.segN-2` | true | `agv.Fetch(...);` | 取货 | +| 20 | `plan.action=='put' && dst.Shelf && plan.curSeg==plan.segN-2` | true | `agv.Put(...);` | 放货 | +| 20 | (无条件) | - | `agv.Queue(()=>{},()=>{ agv.ChangeAvoidanceDistance(...); });` | 更改避障距离 | +| 20 | (无条件) | - | `agv.Queue(()=>{},()=>{ agv.ChangeAvoidanceParam(${dst.CarLength},${dst.CarWidth}); });` | 切换避障尺寸 | +| 20 | `track.IOArea != -1` | - | `agv.Queue(()=>{},()=>{ agv.SwitchIoArea(${track.IOArea}); });` | 切换IO区域 | +| 20 | `track.BiasAlarmThresh >0 \|\| track.DthAlarmThresh > 0` | - | `agv.Queue(()=>{},()=>{ agv.ChangeTrackingErrThresh(...); });` | 更改跟踪误差阈值 | +| 17 | `track.LidarArea != -2` | - | `agv.Queue(()=>{},()=>{ agv.SwitchLidarArea(${track.LidarArea}); });` | 切换激光避障区域 | +| 10 | `track.CalibrateWheelEncoder && track.ReverseDst != dst.id` | true | `agv.CalibrateWheelEncoder(...);` | 标定轮里程计 | + +--- + +### 3. Forklift.cs - 叉车 + +| Priority | useVerb | blockVerb | templateString | 说明 | +|----------|---------|-----------|----------------|------| +| 22 | `src.Shelf` | true | `agv.LeaveShelf(...);agv.Wait();` | 离开货架 | +| 20 | `track.LidarArea != -2` | - | `agv.Queue(()=>{},()=>{ agv.SwitchLidarArea(${track.LidarArea}); });` | 切换激光避障区域 | +| 20 | `track.IOArea != -1` | - | `agv.Queue(()=>{},()=>{ agv.SwitchIoArea(${track.IOArea}); });` | 切换IO区域 | +| 20 | `dst.CarLength != -1 && dst.CarWidth != -1` | - | `agv.Queue(()=>{},()=>{ agv.ChangeAvoidanceParam(${dst.CarLength},${dst.CarWidth}); });` | 切换避障尺寸 | +| 20 | `plan.action=='fetch' && dst.Shelf` | true | `agv.Wait();agv.Fetch(...);agv.Wait();` | 取货 | +| 20 | `plan.action=='put' && dst.Shelf` | true | `agv.Wait();agv.Put(...);agv.Wait();` | 放货 | +| 20 | `track.BiasAlarmThresh >0 \|\| track.DthAlarmThresh > 0` | - | `agv.Queue(()=>{},()=>{ agv.ChangeTrackingErrThresh(...); });` | 更改跟踪误差阈值 | + +--- + +### 4. MultiWheelForkLifter.cs - 多舵轮叉车 + +| Priority | useVerb | blockVerb | templateString | 说明 | +|----------|---------|-----------|----------------|------| +| 20 | `plan.action=='fetch' && dst.Shelf && plan.curSeg==plan.segN-2` | true | `agv.Wait();agv.Fetch(...);agv.Wait();` | 取货 | +| 20 | `plan.action=='put' && dst.Shelf && plan.curSeg==plan.segN-2` | true | `agv.Wait();agv.Put(...);agv.Wait();` | 放货 | + +--- + +## 二、TemplateSiteCoderSettings(站点脚本) + +### 1. Kiva.cs + +| Priority | useVerb | blockVerb | templateString | 说明 | +|----------|---------|-----------|----------------|------| +| 25 | `plan.action=='fetch' && plan.segN == 1 && dst.Shelf` | true | `agv.FetchInPlace(...);agv.Wait();` | 原地取货 | + +### 2. Forklift.cs + +| Priority | useVerb | blockVerb | templateString | 说明 | +|----------|---------|-----------|----------------|------| +| 25 | `plan.action=='fetch' && plan.segN==1 && dst.Shelf` | true | `agv.FetchInPlace(...);agv.Wait();` | 原地取货 | + +### 3. ArmCar.cs - 机械臂车 + +| Priority | useVerb | blockVerb | templateString | 说明 | +|----------|---------|-----------|----------------|------| +| 5 | `plan.action=='pickFull' && plan.curSeg==plan.segN-1` | true | `agv.Wait();agv.PickFull("${dst.name}");agv.Wait();` | 满盘取货 | +| 5 | `plan.action=='pickEmpty' && plan.curSeg==plan.segN-1` | true | `agv.Wait();agv.PickEmpty("${dst.name}");agv.Wait();` | 空盘取货 | +| 5 | `plan.action=='putFull' && plan.curSeg==plan.segN-1` | true | `agv.Wait();agv.PutFull("${dst.name}");agv.Wait();` | 满盘放货 | +| 5 | `plan.action=='putEmpty' && plan.curSeg==plan.segN-1` | true | `agv.Wait();agv.PutEmpty("${dst.name}");agv.Wait();` | 空盘放货 | +| 5 | `plan.action=='MoveArm' && plan.curSeg==plan.segN-1` | true | `agv.Wait();agv.MoveArm(${dst.MoveDirection});agv.Wait();` | 移动机械臂 | + +--- + +## 三、ProgramTrackCoderSettings(程序化路径脚本) + +| 车型 | Priority | Program | 说明 | +|------|----------|---------|------| +| MultiWheelLifterCar | 19 | `MagTrackCoder` | 磁导航路径编码 | +| Kiva | 19 | `AllCarMagTrackCoder` | 磁导航路径编码 | +| Kiva | 5 | `KivaCarTrackCoder` | Kiva旋转控制 | + +--- + +## 四、字段类型定义 + +### BasicCarFields +```csharp +public float MagSlowSpeed = 0; +public float MagFullSpeed = 0; +``` + +### BasicSiteFields +```csharp +public bool Shelf = false; +public float CarLength = -1; +public float CarWidth = -1; +public float CarCenterX = 0; +public float CarCenterY = 0; +public int tag = -1; +public int TagValue = -1;// 磁导航,二维码值,或者rfid值 +``` + +### BasicTrackFields +```csharp +public int IOArea = -1; +public int LidarArea = -2; +public float BiasAlarmThresh = -1; +public float DthAlarmThresh = -1; +public float Speed = 0.2f; +public bool Reverse = false; +public int ReverseDst = -1; +public bool SwitchBarrier = false; +public bool CalibrateWheelEncoder = false; +public float CarDirectionBias = 0; +public bool EnableCarAbsoluteDirection = false; +public float CarAbsoluteDirection = 0; +public float SlowDistance = -1; +public float StopDistance = -1; +``` + +### BasicPlanFields +```csharp +public string action = "/"; +public float CarLength = -1; +public float CarWidth = -1; +``` + +--- + +## 五、扩展字段类型 + +### MultiWheelLifterTrackFields (继承 BasicTrackFields) +```csharp +public int SleepTime = 0; +public float TrayTarget = 0; +``` + +### MultiWheelLifterSiteFields (继承 BasicSiteFields) +```csharp +public bool ChangeAvoidanceParam = false; +``` + +### KivaSiteFields (继承 BasicSiteFields) +```csharp +public int AngleTarget = 0; +public bool Turn = false; +public float FetchSpeed = 0; +public int FetchLidarArea = -2; +public int FetchIOArea = -1; +public bool FetchReverse = false; +public float FetchBlindMoveDist = 0; +public float FetchLiftDownTarget = -1; +public float FetchLiftUpTarget = -1; +public bool FetchUseQr = false; +public int FetchQrMode = -1; +public bool FetchIsUpQr = false; +public bool FetchUseDetector = false; +public int FetchDetector = 0; +public float FetchDetectWidth = -1; +public float FetchDetectDepth = -1; +public bool FetchLeaveSrcEarly = false; +public float FetchShieldObstacleDist = -1; +// ... 以及 Put 和 LeaveShelf 相关字段 +``` + +### KivaTrackFields (继承 BasicTrackFields) +```csharp +public int ManeuverDir = 0; +public int ForwardDst = 0; +public int ForwardObChooseDst = -2; +public float BlindMoveDist = 0; +public bool UseDetector = false; +public int DetectorMode = -1; +public float DetectWidth = -1; +public float DetectDepth = -1; +public bool LeaveSrcEarly = false; +public float ShieldObstacleDist = -1; +``` + +### KivaPlanFields (继承 BasicPlanFields) +```csharp +public bool reverse = false; +public int level = 0; +``` + +--- + +## 六、ITrackCoder 接口 + +程序化脚本生成器需要实现 `ITrackCoder` 接口: + +```csharp +public interface ITrackCoder +{ + /// + /// 生成脚本代码 + /// + /// 路径计划 + /// 当前路径段 + /// 起点 + /// 终点 + /// 段索引 + /// 是否成功生成 + bool Code(SegmentPlan plan, Track track, Site src, Site dst, int i); + + /// + /// 是否阻止后续脚本生成 + /// + bool toBlock(); +} +``` + +--- + +*文档生成时间: 2024年* diff --git a/StandardScene.Core/Docs/DEVELOPMENT_GUIDE.pdf b/StandardScene.Core/Docs/DEVELOPMENT_GUIDE.pdf new file mode 100644 index 0000000..576601e Binary files /dev/null and b/StandardScene.Core/Docs/DEVELOPMENT_GUIDE.pdf differ diff --git a/StandardScene.Core/Docs/DoorController寮鍙戞墜鍐.md b/StandardScene.Core/Docs/DoorController寮鍙戞墜鍐.md new file mode 100644 index 0000000..0222027 --- /dev/null +++ b/StandardScene.Core/Docs/DoorController寮鍙戞墜鍐.md @@ -0,0 +1,944 @@ +# DoorController 寮鍙戞墜鍐 + +## 鐩綍 +- [姒傝堪](#姒傝堪) +- [鍩虹鏋舵瀯](#鍩虹鏋舵瀯) +- [寮鍙戞楠(#寮鍙戞楠) +- [瀹炵幇绀轰緥](#瀹炵幇绀轰緥) +- [鐗规ц鏄嶿(#鐗规ц鏄) +- [绾跨▼瀹夊叏](#绾跨▼瀹夊叏) +- [鏈浣冲疄璺礭(#鏈浣冲疄璺) +- [娉ㄦ剰浜嬮」](#娉ㄦ剰浜嬮」) +- [闄勫綍](#闄勫綍) + +--- + +## 姒傝堪 + +`DoorController` 鏄棬鎺у埗绯荤粺鐨勬牳蹇冪粍浠讹紝閲囩敤鎶借薄鍩虹被璁捐锛屾敮鎸佹墿灞曚笉鍚岀被鍨嬬殑闂ㄦ帶鍒跺櫒瀹炵幇銆傛湰鎵嬪唽鎸囧寮鍙戣呭浣曞垱寤鸿嚜瀹氫箟鐨勯棬鎺у埗鍣ㄣ + +### 鏍稿績姒傚康 + +- **BasicDoorController**锛氶棬鎺у埗鍣ㄦ娊璞″熀绫伙紝瀹氫箟閫氱敤鎺ュ彛鍜屽睘鎬 +- **DoorTypeAttribute**锛氱被鍨嬬壒鎬э紝鐢ㄤ簬鏍囪鎺у埗鍣ㄧ被鍨嬶紝鏀寔鍔ㄦ佸疄渚嬪寲 +- **DoorState**锛氶棬鐘舵佹灇涓撅紙Closed/Open/Unknown锛 +- **DoorControllerState**锛氭帶鍒跺櫒鐘舵佹灇涓撅紙Offline/Online/Connecting/Error锛 + +### 璁捐鍘熷垯 + +1. **鎶借薄鍖**锛氭墍鏈夐氫俊缁嗚妭灏佽鍦ㄥ叿浣撳疄鐜扮被涓 +2. **绾跨▼瀹夊叏**锛氱姸鎬佽鍙栧拰鎺у埗鍐欏叆鍒嗙锛岄氫俊鎿嶄綔鍦ㄥ唴閮ㄧ嚎绋嬪畬鎴 +3. **鍙墿灞曟**锛氶氳繃 `DoorTypeAttribute` 瀹炵幇绫诲瀷鑷姩璇嗗埆鍜屽姩鎬佸姞杞 +4. **鐑洿鏂版敮鎸**锛氶厤缃彉鏇存棤闇閲嶅惎浠诲姟 + +--- + +## 鍩虹鏋舵瀯 + +### 1. 绫荤户鎵垮叧绯 + +``` +BasicDoorController (鎶借薄鍩虹被) + 鈹 + 鈹斺攢鈹 ModbusDoorController (Modbus TCP 瀹炵幇) + 鈹斺攢鈹 [鍏朵粬瀹炵幇...] +``` + +### 2. BasicDoorController 鏍稿績灞炴 + +| 灞炴 | 绫诲瀷 | 璇存槑 | +|------|------|------| +| `Index` | int | 鎺у埗鍣ㄧ储寮曪紙鍞竴鏍囪瘑锛 | +| `Ip` | string | IP鍦板潃 | +| `Port` | int | 绔彛鍙 | +| `State` | DoorControllerState | 鎺у埗鍣ㄧ姸鎬 | +| `IsOnline` | bool | 鏄惁鍦ㄧ嚎锛堝彧璇伙級 | +| `DoorStates` | Dictionary | 闂ㄧ姸鎬佸瓧鍏 | +| `DoorControlTargets` | Dictionary | 闂ㄧ洰鏍囨帶鍒剁姸鎬佸瓧鍏 | +| `DoorConfigs` | Dictionary | 闂ㄩ厤缃俊鎭瓧鍏 | +| `LastUpdateTime` | DateTime | 鏈鍚庢洿鏂版椂闂 | +| `ErrorMessage` | string | 閿欒淇℃伅 | + +### 3. 鏍稿績鏂规硶 + +#### 3.1 蹇呴』瀹炵幇鐨勬柟娉曪紙鎶借薄鏂规硶锛 + +```csharp +/// +/// 璇诲彇闂ㄧ姸鎬侊紙寮鍒颁綅淇″彿锛 +/// +/// 闂ㄧ储寮 +/// true=鎵撳紑锛宖alse=鍏抽棴 +public abstract bool ReadDoorState(int doorIndex); + +/// +/// 鍐欏叆闂ㄦ帶鍒朵俊鍙凤紙寮鍏虫帶鍒讹級 +/// +/// 闂ㄧ储寮 +/// true=鎵撳紑锛宖alse=鍏抽棴 +public abstract void WriteDoorControl(int doorIndex, bool open); +``` + +#### 3.2 鍙噸鍐欑殑鏂规硶锛堣櫄鏂规硶锛 + +```csharp +/// +/// 杩炴帴闂ㄦ帶鍒跺櫒 +/// +public virtual void Connect() { } + +/// +/// 鏂紑杩炴帴 +/// +public virtual void Disconnect() { } + +/// +/// 鏇存柊鎺у埗鍣ㄧ姸鎬 +/// +public virtual void UpdateState(DoorControllerState newState, string errorMessage = "") { } + +/// +/// 璁剧疆闂ㄧ殑鐩爣鎺у埗鐘舵 +/// +public virtual void SetDoorControlTarget(int doorIndex, bool open) { } +``` + +--- + +## 寮鍙戞楠 + +### 姝ラ1锛氬垱寤烘帶鍒跺櫒绫 + +鍒涘缓鏂扮被骞剁户鎵 `BasicDoorController`锛 + +```csharp +using StandardScene.ExtendDevice.Door; + +namespace StandardScene.ExtendDevice.Door +{ + public class MyDoorController : BasicDoorController + { + // 瀹炵幇鎶借薄鏂规硶 + } +} +``` + +### 姝ラ2锛氭坊鍔 DoorTypeAttribute + +浣跨敤 `DoorTypeAttribute` 鏍囪鎺у埗鍣ㄧ被鍨嬶細 + +```csharp +[DoorType("MyDoorController")] +public class MyDoorController : BasicDoorController +{ + // ... +} +``` + +**娉ㄦ剰**锛歚DoorTypeAttribute` 鐨 `Name` 鍙傛暟灏嗘樉绀哄湪閰嶇疆鐣岄潰鐨勭被鍨嬩笅鎷夋涓紝骞剁敤浜庨厤缃枃浠朵腑鐨勭被鍨嬫爣璇嗐 + +### 姝ラ3锛氬疄鐜版娊璞℃柟娉 + +瀹炵幇 `ReadDoorState` 鍜 `WriteDoorControl` 鏂规硶锛 + +```csharp +public override bool ReadDoorState(int doorIndex) +{ + // 璇诲彇闂ㄧ姸鎬侀昏緫 + // 杩斿洖 true=鎵撳紑锛宖alse=鍏抽棴 +} + +public override void WriteDoorControl(int doorIndex, bool open) +{ + // 鍐欏叆闂ㄦ帶鍒朵俊鍙烽昏緫 +} +``` + +### 姝ラ4锛氶噸鍐 Connect/Disconnect 鏂规硶 + +濡傛灉闇瑕佸垵濮嬪寲杩炴帴銆佸惎鍔ㄥ悗鍙颁换鍔$瓑锛岄噸鍐 `Connect` 鍜 `Disconnect` 鏂规硶锛 + +```csharp +public override void Connect() +{ + base.Connect(); // 璋冪敤鍩虹被鏂规硶鏇存柊鐘舵 + + // 鍒濆鍖栬繛鎺 + // 鍚姩鍚庡彴浠诲姟 + // 璇诲彇鍒濆鐘舵 +} + +public override void Disconnect() +{ + // 鍋滄鍚庡彴浠诲姟 + // 鍏抽棴杩炴帴 + + base.Disconnect(); // 璋冪敤鍩虹被鏂规硶鏇存柊鐘舵 +} +``` + +### 姝ラ5锛氬疄鐜扮嚎绋嬪畨鍏ㄧ殑鎺у埗閫昏緫 + +濡傛灉闇瑕佸畾鏃惰鍙栫姸鎬佹垨鏍规嵁 `DoorControlTargets` 涓嬪彂鎺у埗鎸囦护锛屽疄鐜板悗鍙颁换鍔★細 + +```csharp +private CancellationTokenSource _cancellationTokenSource; +private Task _readTask; + +public override void Connect() +{ + base.Connect(); + + // 鍚姩瀹氭椂璇诲彇浠诲姟 + _cancellationTokenSource = new CancellationTokenSource(); + _readTask = Task.Run(() => ReadDoorStatesLoop(_cancellationTokenSource.Token)); +} + +private void ReadDoorStatesLoop(CancellationToken cancellationToken) +{ + while (!cancellationToken.IsCancellationRequested) + { + // 璇诲彇鎵鏈夐棬鐨勭姸鎬 + ReadAllDoorStates(); + + // 鏍规嵁 DoorControlTargets 涓嬪彂鎺у埗鎸囦护 + ApplyDoorControlTargets(); + + Thread.Sleep(ReadInterval); + } +} +``` + +--- + +## 瀹炵幇绀轰緥 + +### 绀轰緥1锛歁odbusDoorController锛堝畬鏁村疄鐜帮級 + +```csharp +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using StandardScene.Utils; +using SimpleCore.Library; + +namespace StandardScene.ExtendDevice.Door +{ + /// + /// Modbus 闂ㄦ帶鍒跺櫒瀹炵幇 + /// + [DoorType("ModbusDoorController")] + public class ModbusDoorController : BasicDoorController + { + private ModbusRtu _modbusClient; + private readonly object _syncLock = new object(); + private bool _isStarted = false; + private readonly Dictionary _lastSentControl = new Dictionary(); + private CancellationTokenSource _cancellationTokenSource; + private Task _readTask; + + // 鍙厤缃弬鏁 + public int ReadInterval { get; set; } = 1000; // 璇诲彇闂撮殧锛堟绉掞級 + public int ReconnectInterval { get; set; } = 3000; // 閲嶈繛闂撮殧锛堟绉掞級 + public byte SlaveAddress { get; set; } = 1; // Modbus 浠庣珯鍦板潃 + + /// + /// 绾跨▼瀹夊叏鐨勮缃棬鎺у埗鐩爣 + /// + public override void SetDoorControlTarget(int doorIndex, bool open) + { + lock (_syncLock) + { + base.SetDoorControlTarget(doorIndex, open); + } + } + + /// + /// 杩炴帴闂ㄦ帶鍒跺櫒 + /// + public override void Connect() + { + lock (_syncLock) + { + if (_isStarted) return; + + try + { + UpdateState(DoorControllerState.Connecting); + + // 鍒濆鍖栭棬鐘舵 + var doorIndices = DoorConfigs.Keys.OrderBy(k => k).ToList(); + InitializeDoors(doorIndices); + + // 鍒濆鍖栨渶杩戜竴娆″凡涓嬪彂鐨勬帶鍒剁姸鎬 + _lastSentControl.Clear(); + foreach (var index in doorIndices) + { + _lastSentControl[index] = false; + if (!DoorControlTargets.ContainsKey(index)) + { + DoorControlTargets[index] = false; + } + } + + // 杩炴帴 Modbus TCP + try + { + _modbusClient = new ModbusRtu(); + _modbusClient.StartTcpRtu(Ip, Port); + UpdateState(DoorControllerState.Online); + } + catch (Exception ex) + { + UpdateState(DoorControllerState.Connecting); + Diagnosis.Log($"ModbusDoorController[{Index}] 鍒濇杩炴帴澶辫触: {ex.Message}", "ModbusDoorController", true); + } + + // 鍚姩瀹氭椂璇诲彇浠诲姟 + _cancellationTokenSource = new CancellationTokenSource(); + _readTask = Task.Run(() => ReadDoorStatesLoop(_cancellationTokenSource.Token)); + + _isStarted = true; + } + catch (Exception ex) + { + UpdateState(DoorControllerState.Error, $"鍒濆鍖栧け璐: {ex.Message}"); + _isStarted = false; + } + } + } + + /// + /// 鏂紑杩炴帴 + /// + public override void Disconnect() + { + lock (_syncLock) + { + if (!_isStarted) return; + + try + { + _cancellationTokenSource?.Cancel(); + _readTask?.Wait(1000); + + _modbusClient?.Close(); + _modbusClient = null; + + _isStarted = false; + UpdateState(DoorControllerState.Offline); + } + catch (Exception ex) + { + UpdateState(DoorControllerState.Error, $"鏂紑杩炴帴澶辫触: {ex.Message}"); + } + } + } + + /// + /// 瀹氭椂璇诲彇闂ㄧ姸鎬佸惊鐜 + /// + private void ReadDoorStatesLoop(CancellationToken cancellationToken) + { + while (!cancellationToken.IsCancellationRequested) + { + try + { + if (!_isStarted) break; + + // 妫鏌ヨ繛鎺ョ姸鎬 + bool isConnected = _modbusClient?.modbusRtu?.Connected ?? false; + if (_modbusClient == null || !isConnected) + { + UpdateState(DoorControllerState.Connecting); + TryReconnect(); + isConnected = _modbusClient?.modbusRtu?.Connected ?? false; + if (!isConnected) + { + Thread.Sleep(ReadInterval); + continue; + } + } + + // 璇诲彇鎵鏈夐棬鐨勭姸鎬 + ReadAllDoorStates(); + + // 鏍规嵁鐩爣鎺у埗鐘舵佷笅鍙戞帶鍒舵寚浠 + ApplyDoorControlTargets(); + + UpdateState(DoorControllerState.Online); + } + catch (Exception ex) + { + Diagnosis.Log($"ModbusDoorController[{Index}] 璇诲彇鐘舵佸け璐: {ex.Message}", "ModbusDoorController", true); + UpdateState(DoorControllerState.Error, $"璇诲彇鐘舵佸け璐: {ex.Message}"); + TryReconnect(); + } + + Thread.Sleep(ReadInterval); + } + } + + /// + /// 璇诲彇鎵鏈夐棬鐨勭姸鎬 + /// + private void ReadAllDoorStates() + { + lock (_syncLock) + { + foreach (var doorConfig in DoorConfigs.Values) + { + try + { + var state = ReadDoorState(doorConfig.Index); + UpdateDoorState(doorConfig.Index, state ? DoorState.Open : DoorState.Closed); + } + catch (Exception ex) + { + Diagnosis.Log($"ModbusDoorController[{Index}] 璇诲彇闂▄doorConfig.Index}鐘舵佸け璐: {ex.Message}", "ModbusDoorController", true); + } + } + } + } + + /// + /// 鏍规嵁 DoorControlTargets 涓嬪彂鎺у埗鎸囦护 + /// + private void ApplyDoorControlTargets() + { + lock (_syncLock) + { + foreach (var doorConfig in DoorConfigs.Values) + { + var doorIndex = doorConfig.Index; + + // 鑾峰彇鐩爣鎺у埗鐘舵 + bool target = false; + DoorControlTargets.TryGetValue(doorIndex, out target); + + // 鑾峰彇涓婁竴娆″凡涓嬪彂鐨勭姸鎬 + bool last; + var hasLast = _lastSentControl.TryGetValue(doorIndex, out last); + + // 濡傛灉娌℃湁璁板綍鎴栫姸鎬佸彂鐢熷彉鍖栵紝鍒欎笅鍙戞帶鍒 + if (!hasLast || last != target) + { + try + { + WriteDoorControl(doorIndex, target); + _lastSentControl[doorIndex] = target; + } + catch (Exception ex) + { + Diagnosis.Log($"ModbusDoorController[{Index}] 涓嬪彂闂▄doorIndex}鎺у埗鎸囦护澶辫触: {ex.Message}", "ModbusDoorController", true); + } + } + } + } + } + + /// + /// 璇诲彇闂ㄧ姸鎬侊紙寮鍒颁綅淇″彿锛 + /// + public override bool ReadDoorState(int doorIndex) + { + lock (_syncLock) + { + if (!DoorConfigs.TryGetValue(doorIndex, out var doorConfig)) + { + throw new ArgumentException($"闂▄doorIndex}涓嶅瓨鍦"); + } + + if (_modbusClient == null || !_modbusClient.modbusRtu.Connected) + { + throw new InvalidOperationException("Modbus杩炴帴鏈缓绔"); + } + + // 璇诲彇寮鍒颁綅淇″彿锛堢鏁h緭鍏ワ級 + var data = _modbusClient.ReadDiscreteInputs_02(SlaveAddress, doorConfig.OpenStatusAddress, 1); + return data != null && data.Length > 0 && data[0]; + } + } + + /// + /// 鍐欏叆闂ㄦ帶鍒朵俊鍙凤紙寮鍏虫帶鍒讹級 + /// + public override void WriteDoorControl(int doorIndex, bool open) + { + lock (_syncLock) + { + if (!DoorConfigs.TryGetValue(doorIndex, out var doorConfig)) + { + throw new ArgumentException($"闂▄doorIndex}涓嶅瓨鍦"); + } + + if (_modbusClient == null || !_modbusClient.modbusRtu.Connected) + { + TryReconnect(); + if (_modbusClient == null || !_modbusClient.modbusRtu.Connected) + { + throw new InvalidOperationException("Modbus杩炴帴鏈缓绔"); + } + } + + // 鍐欏叆寮鍏虫帶鍒朵俊鍙凤紙绾垮湀锛 + _modbusClient.WriteMultipleCoils_15(SlaveAddress, doorConfig.ControlAddress, new[] { open }); + } + } + + private void TryReconnect() + { + // 閲嶈繛閫昏緫... + } + } +} +``` + +### 绀轰緥2锛氱畝鍗曢棬鎺у埗鍣紙鏈灏忓疄鐜帮級 + +濡傛灉涓嶉渶瑕佸畾鏃惰鍙栨垨鍚庡彴浠诲姟锛屽彲浠ュ疄鐜版渶绠鍗曠殑鐗堟湰锛 + +```csharp +using System; +using StandardScene.ExtendDevice.Door; + +namespace StandardScene.ExtendDevice.Door +{ + /// + /// 绠鍗曢棬鎺у埗鍣ㄥ疄鐜帮紙鍚屾妯″紡锛 + /// + [DoorType("SimpleDoorController")] + public class SimpleDoorController : BasicDoorController + { + private SimpleDoorClient _client; + + public override void Connect() + { + base.Connect(); + _client = new SimpleDoorClient(Ip, Port); + UpdateState(DoorControllerState.Online); + } + + public override void Disconnect() + { + _client?.Close(); + _client = null; + base.Disconnect(); + } + + public override bool ReadDoorState(int doorIndex) + { + if (!DoorConfigs.TryGetValue(doorIndex, out var doorConfig)) + { + throw new ArgumentException($"闂▄doorIndex}涓嶅瓨鍦"); + } + + if (_client == null || !_client.IsConnected) + { + throw new InvalidOperationException("杩炴帴鏈缓绔"); + } + + // 璇诲彇闂ㄧ姸鎬 + return _client.ReadDoorStatus(doorConfig.OpenStatusAddress); + } + + public override void WriteDoorControl(int doorIndex, bool open) + { + if (!DoorConfigs.TryGetValue(doorIndex, out var doorConfig)) + { + throw new ArgumentException($"闂▄doorIndex}涓嶅瓨鍦"); + } + + if (_client == null || !_client.IsConnected) + { + throw new InvalidOperationException("杩炴帴鏈缓绔"); + } + + // 鍐欏叆鎺у埗淇″彿 + _client.WriteDoorControl(doorConfig.ControlAddress, open); + + // 鍚屾鏇存柊闂ㄧ姸鎬侊紙鍙夛級 + var state = _client.ReadDoorStatus(doorConfig.OpenStatusAddress); + UpdateDoorState(doorIndex, state ? DoorState.Open : DoorState.Closed); + } + + /// + /// 閲嶅啓 SetDoorControlTarget 浠ョ珛鍗虫墽琛屾帶鍒 + /// + public override void SetDoorControlTarget(int doorIndex, bool open) + { + base.SetDoorControlTarget(doorIndex, open); + + // 绔嬪嵆鎵ц鎺у埗锛堝悓姝ユā寮忥級 + try + { + WriteDoorControl(doorIndex, open); + } + catch (Exception ex) + { + Diagnosis.Log($"SimpleDoorController[{Index}] 鎺у埗闂▄doorIndex}澶辫触: {ex.Message}", "SimpleDoorController", true); + } + } + } +} +``` + +--- + +## 鐗规ц鏄 + +### DoorTypeAttribute + +`DoorTypeAttribute` 鐢ㄤ簬鏍囪闂ㄦ帶鍒跺櫒绫诲瀷锛屾敮鎸佸姩鎬佸疄渚嬪寲銆 + +**瀹氫箟**锛 +```csharp +[AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)] +public class DoorTypeAttribute : Attribute +{ + public string Name { get; } + + public DoorTypeAttribute(string name) + { + Name = name ?? throw new ArgumentNullException(nameof(name)); + } +} +``` + +**浣跨敤**锛 +```csharp +[DoorType("MyDoorController")] +public class MyDoorController : BasicDoorController +{ + // ... +} +``` + +**浣滅敤**锛 +1. **绫诲瀷鏍囪瘑**锛歚Name` 鍙傛暟浣滀负绫诲瀷鐨勫敮涓鏍囪瘑锛岀敤浜庨厤缃枃浠朵腑鎸囧畾绫诲瀷 +2. **UI鏄剧ず**锛氶厤缃晫闈細鑷姩璇嗗埆骞舵樉绀烘墍鏈夊甫姝ょ壒鎬х殑鎺у埗鍣ㄧ被鍨 +3. **鍔ㄦ佸疄渚嬪寲**锛歚DoorMission` 鏍规嵁 `Name` 鍔ㄦ佹煡鎵惧苟鍒涘缓瀹炰緥 + +**娉ㄦ剰**锛 +- `Name` 蹇呴』鍞竴 +- 寤鸿浣跨敤绫诲悕浣滀负 `Name` +- `Name` 浼氭樉绀哄湪閰嶇疆鐣岄潰鐨勭被鍨嬩笅鎷夋涓 + +--- + +## 绾跨▼瀹夊叏 + +### 1. 璁捐鍘熷垯 + +闂ㄦ帶鍒跺櫒閲囩敤**璇诲啓鍒嗙**鐨勭嚎绋嬪畨鍏ㄨ璁★細 +- **璇诲彇**锛歚DoorMission` 閫氳繃 `controller.DoorStates` 鐩存帴璁块棶闂ㄧ姸鎬侊紙鍙楅攣淇濇姢锛 +- **鍐欏叆**锛歚DoorMission` 閫氳繃 `controller.SetDoorControlTarget()` 璁剧疆鐩爣鐘舵侊紝瀹為檯閫氫俊鐢遍棬鎺у埗鍣ㄥ唴閮ㄧ嚎绋嬪畬鎴 + +### 2. 绾跨▼瀹夊叏瑕佹眰 + +#### 2.1 SetDoorControlTarget 鏂规硶 + +濡傛灉澶氫釜绾跨▼鍙兘鍚屾椂璋冪敤 `SetDoorControlTarget`锛屽繀椤诲姞閿佷繚鎶わ細 + +```csharp +private readonly object _syncLock = new object(); + +public override void SetDoorControlTarget(int doorIndex, bool open) +{ + lock (_syncLock) + { + base.SetDoorControlTarget(doorIndex, open); + } +} +``` + +#### 2.2 ReadDoorState 鍜 WriteDoorControl 鏂规硶 + +濡傛灉杩欎簺鏂规硶浼氳澶氫釜绾跨▼璋冪敤锛屽繀椤诲姞閿佷繚鎶わ細 + +```csharp +public override bool ReadDoorState(int doorIndex) +{ + lock (_syncLock) + { + // 璇诲彇閫昏緫 + } +} + +public override void WriteDoorControl(int doorIndex, bool open) +{ + lock (_syncLock) + { + // 鍐欏叆閫昏緫 + } +} +``` + +#### 2.3 鍚庡彴浠诲姟璁块棶鍏变韩璧勬簮 + +濡傛灉鍚庡彴浠诲姟浼氳闂 `DoorStates`銆乣DoorControlTargets` 绛夊叡浜祫婧愶紝蹇呴』鍔犻攣锛 + +```csharp +private void ReadAllDoorStates() +{ + lock (_syncLock) + { + foreach (var doorConfig in DoorConfigs.Values) + { + var state = ReadDoorState(doorConfig.Index); + UpdateDoorState(doorConfig.Index, state ? DoorState.Open : DoorState.Closed); + } + } +} + +private void ApplyDoorControlTargets() +{ + lock (_syncLock) + { + foreach (var doorConfig in DoorConfigs.Values) + { + // 璁块棶 DoorControlTargets + // 璋冪敤 WriteDoorControl + } + } +} +``` + +### 3. 鎺ㄨ崘瀹炵幇妯″紡 + +鎺ㄨ崘浣跨敤鍗曚竴閿佸璞′繚鎶ゆ墍鏈夊叡浜祫婧愶細 + +```csharp +public class MyDoorController : BasicDoorController +{ + private readonly object _syncLock = new object(); + + // 鎵鏈夎闂叡浜祫婧愮殑鏂规硶閮戒娇鐢ㄥ悓涓涓攣 + public override void SetDoorControlTarget(int doorIndex, bool open) + { + lock (_syncLock) { /* ... */ } + } + + public override bool ReadDoorState(int doorIndex) + { + lock (_syncLock) { /* ... */ } + } + + public override void WriteDoorControl(int doorIndex, bool open) + { + lock (_syncLock) { /* ... */ } + } + + private void ReadAllDoorStates() + { + lock (_syncLock) { /* ... */ } + } + + private void ApplyDoorControlTargets() + { + lock (_syncLock) { /* ... */ } + } +} +``` + +--- + +## 鏈浣冲疄璺 + +### 1. 閿欒澶勭悊 + +- **杩炴帴閿欒**锛氳缃姸鎬佷负 `Connecting` 鎴 `Error`锛屽苟璁板綍閿欒淇℃伅 +- **璇诲彇閿欒**锛氳褰曟棩蹇楋紝浣嗕笉鎶涘嚭寮傚父锛岃繑鍥為粯璁ゅ兼垨淇濇寔褰撳墠鐘舵 +- **鍐欏叆閿欒**锛氳褰曟棩蹇楋紝灏濊瘯閲嶈繛锛屼絾涓嶅奖鍝嶅叾浠栭棬鐨勬搷浣 + +**绀轰緥**锛 +```csharp +public override bool ReadDoorState(int doorIndex) +{ + try + { + // 璇诲彇閫昏緫 + } + catch (Exception ex) + { + Diagnosis.Log($"璇诲彇闂▄doorIndex}鐘舵佸け璐: {ex.Message}", "MyDoorController", true); + return false; // 杩斿洖榛樿鍊 + } +} +``` + +### 2. 鐘舵佺鐞 + +- **鍙婃椂鏇存柊鐘舵**锛氬湪杩炴帴銆佹柇寮銆侀敊璇椂鍙婃椂璋冪敤 `UpdateState()` +- **鏇存柊鏈鍚庢洿鏂版椂闂**锛氬湪鐘舵佸彉鍖栨椂鏇存柊 `LastUpdateTime` +- **閿欒淇℃伅**锛氬湪閿欒鏃惰褰曡缁嗙殑閿欒淇℃伅鍒 `ErrorMessage` + +**绀轰緥**锛 +```csharp +try +{ + _client.Connect(); + UpdateState(DoorControllerState.Online); +} +catch (Exception ex) +{ + UpdateState(DoorControllerState.Error, $"杩炴帴澶辫触: {ex.Message}"); +} +``` + +### 3. 璧勬簮閲婃斁 + +- **瀹炵幇 Disconnect**锛氱‘淇濇纭叧闂繛鎺ュ拰閲婃斁璧勬簮 +- **瀹炵幇鏋愭瀯鍑芥暟**锛氫綔涓烘渶鍚庣殑瀹夊叏缃戯紝纭繚璧勬簮閲婃斁 + +**绀轰緥**锛 +```csharp +public override void Disconnect() +{ + try + { + _cancellationTokenSource?.Cancel(); + _readTask?.Wait(1000); + _client?.Close(); + _client = null; + UpdateState(DoorControllerState.Offline); + } + catch (Exception ex) + { + UpdateState(DoorControllerState.Error, $"鏂紑杩炴帴澶辫触: {ex.Message}"); + } +} + +~MyDoorController() +{ + Disconnect(); +} +``` + +### 4. 閰嶇疆楠岃瘉 + +鍦 `Connect()` 涓獙璇侀厤缃殑瀹屾暣鎬э細 + +```csharp +public override void Connect() +{ + if (string.IsNullOrWhiteSpace(Ip)) + { + UpdateState(DoorControllerState.Error, "IP鍦板潃鏈厤缃"); + return; + } + + if (Port <= 0 || Port > 65535) + { + UpdateState(DoorControllerState.Error, "绔彛鍙锋棤鏁"); + return; + } + + if (DoorConfigs.Count == 0) + { + UpdateState(DoorControllerState.Error, "鏈厤缃棬"); + return; + } + + // 杩炴帴閫昏緫... +} +``` + +--- + +## 娉ㄦ剰浜嬮」 + +### 1. DoorTypeAttribute 鍛藉悕 + +- `Name` 蹇呴』涓庨厤缃枃浠朵腑浣跨敤鐨勭被鍨嬪悕绉颁竴鑷 +- 寤鸿浣跨敤绫诲悕浣滀负 `Name` +- 閬垮厤浣跨敤鐗规畩瀛楃 + +### 2. 寮傚父澶勭悊 + +- **涓嶈鎶涘嚭鏈鐞嗙殑寮傚父**锛氭墍鏈夊紓甯搁兘搴旇琚崟鑾峰苟璁板綍 +- **涓嶈闃诲绾跨▼**锛氶暱鏃堕棿鎿嶄綔搴旇鍦ㄥ悗鍙扮嚎绋嬩腑鎵ц +- **鎻愪緵閿欒淇℃伅**锛氶氳繃 `ErrorMessage` 灞炴ф彁渚涜缁嗙殑閿欒淇℃伅 + +### 3. 鎬ц兘鑰冭檻 + +- **閬垮厤棰戠箒鐨勮繛鎺/鏂紑**锛氫繚鎸佽繛鎺ユ寔涔呭寲 +- **鎵归噺璇诲彇**锛氬鏋滃彲鑳斤紝鎵归噺璇诲彇澶氫釜闂ㄧ殑鐘舵 +- **鎺у埗璇诲彇棰戠巼**锛氭牴鎹疄闄呴渶姹傝缃悎鐞嗙殑璇诲彇闂撮殧 + +### 4. 鍏煎鎬 + +- **鍚戝悗鍏煎**锛氭柊鐗堟湰搴旇鍏煎鏃х増鏈殑閰嶇疆鏍煎紡 +- **鐗堟湰鏍囪瘑**锛氬鏋滈渶瑕侊紝鍙互鍦ㄥ疄鐜颁腑娣诲姞鐗堟湰妫鏌 + +--- + +## 闄勫綍 + +### A. DoorModel 璇存槑 + +```csharp +public class DoorModel +{ + /// + /// 闂ㄧ储寮 + /// + public int Index { get; set; } + + /// + /// 寮鍏虫帶鍒朵俊鍙峰湴鍧 + /// + public ushort ControlAddress { get; set; } + + /// + /// 寮鍒颁綅淇″彿鍦板潃 + /// + public ushort OpenStatusAddress { get; set; } +} +``` + +### B. DoorState 鏋氫妇 + +```csharp +public enum DoorState +{ + Closed = 0, // 鍏抽棴 + Open = 1, // 鎵撳紑 + Unknown = 2 // 鏈煡鐘舵 +} +``` + +### C. DoorControllerState 鏋氫妇 + +```csharp +public enum DoorControllerState +{ + Offline = 0, // 绂荤嚎 + Online = 1, // 鍦ㄧ嚎 + Connecting = 2, // 杩炴帴涓 + Error = 3 // 閿欒 +} +``` + +### D. 寮鍙戞鏌ユ竻鍗 + +- [ ] 缁ф壙 `BasicDoorController` +- [ ] 娣诲姞 `DoorTypeAttribute` 鐗规 +- [ ] 瀹炵幇 `ReadDoorState` 鏂规硶 +- [ ] 瀹炵幇 `WriteDoorControl` 鏂规硶 +- [ ] 閲嶅啓 `Connect` 鏂规硶锛堝闇瑕侊級 +- [ ] 閲嶅啓 `Disconnect` 鏂规硶锛堝闇瑕侊級 +- [ ] 瀹炵幇绾跨▼瀹夊叏锛堝闇瑕侊級 +- [ ] 娣诲姞閿欒澶勭悊 +- [ ] 娣诲姞璧勬簮閲婃斁閫昏緫 +- [ ] 娣诲姞璇婃柇鏃ュ織 +- [ ] 娴嬭瘯杩炴帴/鏂紑 +- [ ] 娴嬭瘯璇诲彇鐘舵 +- [ ] 娴嬭瘯鎺у埗鍐欏叆 +- [ ] 娴嬭瘯閰嶇疆鐑洿鏂 + +--- + +**鏂囨。鏇存柊鏃堕棿**锛2025-01-09 diff --git a/StandardScene.Core/Docs/DoorMission浣跨敤鎵嬪唽.md b/StandardScene.Core/Docs/DoorMission浣跨敤鎵嬪唽.md new file mode 100644 index 0000000..e8e0958 --- /dev/null +++ b/StandardScene.Core/Docs/DoorMission浣跨敤鎵嬪唽.md @@ -0,0 +1,634 @@ +# DoorMission 浣跨敤鎵嬪唽 + +## 鐩綍 +- [姒傝堪](#姒傝堪) +- [鍩烘湰鍔熻兘](#鍩烘湰鍔熻兘) +- [鍚姩涓庡仠姝(#鍚姩涓庡仠姝) +- [閰嶇疆绠$悊](#閰嶇疆绠$悊) +- [闂ㄦ帶閫昏緫](#闂ㄦ帶閫昏緫) +- [绔欑偣閰嶇疆](#绔欑偣閰嶇疆) +- [UI鐣岄潰](#ui鐣岄潰) +- [鍙傛暟璇存槑](#鍙傛暟璇存槑) +- [鏁呴殰鎺掓煡](#鏁呴殰鎺掓煡) +- [闄勫綍](#闄勫綍) + +--- + +## 姒傝堪 + +`DoorMission` 鏄竴涓棬鎺ц繘绋嬬被锛岀敤浜庣鐞嗗涓棬鎺у埗鍣ㄥ強鍏跺叧鑱旂殑闂ㄣ傚畠鎻愪緵浜嗕互涓嬫牳蹇冨姛鑳斤細 +- 澶氶棬鎺у埗鍣ㄧ鐞嗭紙鏀寔涓嶅悓绫诲瀷锛 +- 鑷姩闂ㄦ帶閫昏緫锛堟牴鎹皬杞︿綅缃嚜鍔ㄥ紑鍏抽棬锛 +- 鎵嬪姩鎺у埗鍔熻兘锛堟敮鎸佷复鏃舵墜鍔ㄦ帶鍒讹紝浼樺厛绾ч珮浜庤嚜鍔ㄦ帶鍒讹級 +- 杞﹁締鍗犵敤绠$悊锛堣窡韪拰娓呯┖闂ㄥ尯鍩熺殑杞﹁締鍗犵敤锛 +- 閰嶇疆鏂囦欢鍔ㄦ佺洃鎺э紙鏀寔鐑洿鏂帮級 +- 绾跨▼瀹夊叏鐨勯棬鐘舵佽鍐 +- 鍙鍖栫殑閰嶇疆鍜岀洃鎺х晫闈 + +### 鏋舵瀯鐗圭偣 +- **鎶借薄鍖栬璁**锛氶棬鎺у埗鍣ㄩ氳繃鎶借薄鍩虹被 `BasicDoorController` 瀹炵幇锛屾敮鎸佹墿灞曚笉鍚岀被鍨嬬殑鎺у埗鍣 +- **绾跨▼瀹夊叏**锛氶棬鐘舵佽鍙栧拰鎺у埗鍐欏叆鍒嗙锛屾墍鏈夐氫俊鎿嶄綔鍦ㄩ棬鎺у埗鍣ㄥ唴閮ㄧ嚎绋嬪畬鎴 +- **浜嬩欢椹卞姩**锛氫笌浜ら氭帶鍒剁郴缁熼泦鎴愶紝鍝嶅簲灏忚溅杩涘叆/绂诲紑绔欑偣浜嬩欢 +- **鐑洿鏂版敮鎸**锛氶厤缃枃浠舵瘡10绉掕嚜鍔ㄦ鏌ユ洿鏂帮紝鏃犻渶閲嶅惎浠诲姟 +- **鎺у埗浠茶鏈哄埗**锛氭墜鍔ㄦ帶鍒朵紭鍏堢骇楂樹簬鑷姩鎺у埗锛屾墜鍔ㄦ帶鍒惰繃鏈熷悗鑷姩鎭㈠鑷姩妯″紡 + +--- + +## 鍩烘湰鍔熻兘 + +### 1. 闂ㄦ帶鍒跺櫒绠$悊 + +#### 1.1 闂ㄦ帶鍒跺櫒绫诲瀷 +闂ㄦ帶鍒跺櫒閫氳繃 `DoorTypeAttribute` 鏍囪绫诲瀷锛岀郴缁熶細鑷姩璇嗗埆骞跺垱寤哄疄渚嬨傚綋鍓嶆敮鎸侊細 +- **ModbusDoorController**锛氬熀浜 Modbus TCP 鐨勯棬鎺у埗鍣 + +#### 1.2 闂ㄦ帶鍒跺櫒閰嶇疆 +姣忎釜闂ㄦ帶鍒跺櫒鍖呭惈浠ヤ笅閰嶇疆锛 +- **Index**锛氭帶鍒跺櫒绱㈠紩锛堝敮涓鏍囪瘑锛 +- **Ip**锛欼P鍦板潃 +- **Port**锛氱鍙e彿锛堥粯璁502锛 +- **Type**锛氭帶鍒跺櫒绫诲瀷锛堥氳繃 `DoorTypeAttribute.Name` 鎸囧畾锛 +- **Doors**锛氶棬鍒楄〃 + +#### 1.3 闂ㄩ厤缃 +姣忎釜闂ㄥ寘鍚互涓嬮厤缃細 +- **Index**锛氶棬绱㈠紩锛堝湪鎺у埗鍣ㄥ唴鍞竴锛 +- **ControlAddress**锛氬紑鍏虫帶鍒朵俊鍙峰湴鍧锛圡odbus 绾垮湀鍦板潃锛 +- **OpenStatusAddress**锛氬紑鍒颁綅淇″彿鍦板潃锛圡odbus 绂绘暎杈撳叆鍦板潃锛 + +### 2. 鑷姩闂ㄦ帶閫昏緫 + +#### 2.1 闂ㄥ紑鍚潯浠 +闂ㄤ細鍦ㄤ互涓嬫儏鍐佃嚜鍔ㄥ紑鍚細 +1. **灏忚溅鍗冲皢杩涘叆鍖哄煙**锛氬綋灏忚溅鍒拌揪绔欑偣涓旂珯鐐圭殑 `EnterDoor` 瀛楁鍖归厤鏃讹紝闂ㄤ細鍦ㄩ攣瀹氬墠寮鍚 +2. **灏忚溅鍦ㄥ尯鍩熷唴**锛氬綋灏忚溅宸茶繘鍏ュ苟閿佸畾绔欑偣鏃讹紝闂ㄤ繚鎸佸紑鍚姸鎬 + +#### 2.2 闂ㄥ叧闂潯浠 +闂ㄤ細鍦ㄤ互涓嬫儏鍐佃嚜鍔ㄥ叧闂細 +- 灏忚溅绂诲紑鍖哄煙鍚庯紝闂ㄨ嚜鍔ㄥ叧闂 + +#### 2.3 闂ㄦ爣璇嗙鏍煎紡 +绔欑偣閰嶇疆涓殑闂ㄦ爣璇嗙鏍煎紡涓猴細**鎺у埗鍣ㄧ储寮.闂ㄧ储寮** + +**绀轰緥**锛 +``` +"EnterDoor": "1.2" // 琛ㄧず鎺у埗鍣ㄧ储寮1锛岄棬绱㈠紩2 +"LeaveDoor": "2.3" // 琛ㄧず鎺у埗鍣ㄧ储寮2锛岄棬绱㈠紩3 +``` + +### 3. 閰嶇疆鏂囦欢鍔ㄦ佺洃鎺 + +绯荤粺姣10绉掕嚜鍔ㄦ鏌 `DoorConfig.json` 鏂囦欢锛屽苟鏍规嵁閰嶇疆鍙樺寲锛 +- **娣诲姞**锛氭柊澧炵殑闂ㄦ帶鍒跺櫒浼氳嚜鍔ㄥ垱寤哄苟杩炴帴 +- **鍒犻櫎**锛氬凡绉婚櫎鐨勯棬鎺у埗鍣ㄤ細鑷姩鏂紑骞剁Щ闄 +- **淇敼**锛氬凡淇敼鐨勯棬鎺у埗鍣ㄤ細鑷姩鏇存柊锛圛P銆佺鍙c佺被鍨嬫垨闂ㄩ厤缃彉鍖栵級 + +--- + +## 鍚姩涓庡仠姝 + +### 1. 鍚姩浠诲姟 + +```csharp +var doorMission = new DoorMission(); +doorMission.Execute(); // 鎵ц"鍚姩杩涚▼" +``` + +**鍚姩娴佺▼**锛 +1. 璁剧疆鏁版嵁鏂囦欢璺緞锛坄DoorConfig.json`锛 +2. 璁㈤槄浜ら氭帶鍒朵簨浠讹紙`BeforeLock`銆乣AfterLeave`銆乣OnLockAcquired`锛 +3. 绔嬪嵆鍔犺浇涓娆¢厤缃紙閬垮厤鐩戞帶鐣岄潰鍦ㄩ娆¤疆璇㈠墠鏃犳暟鎹級 +4. 鍚姩閰嶇疆鐩戞帶浠诲姟锛堟瘡10绉掓鏌ヤ竴娆★級 +5. 鍚姩闂ㄦ帶閫昏緫鐩戞帶浠诲姟锛堟瘡500姣妫鏌ヤ竴娆★級 + +### 2. 鍋滄浠诲姟 + +```csharp +doorMission.Stop(); // 鎵ц"鍋滄杩涚▼" +``` + +**鍋滄娴佺▼**锛 +1. 鍙栨秷浜嬩欢璁㈤槄 +2. 鍙栨秷鎵鏈夊悗鍙颁换鍔 +3. 鏂紑鎵鏈夐棬鎺у埗鍣ㄨ繛鎺 +4. 绛夊緟浠诲姟瀹屾垚锛堟渶澶氱瓑寰5绉掞級 + +### 3. 鍏叡鏂规硶 + +#### 3.1 璇诲彇闂ㄧ姸鎬 +```csharp +bool isOpen = doorMission.GetDoorState(controllerIndex, doorIndex); +``` +- **鍙傛暟**锛 + - `controllerIndex`锛氶棬鎺у埗鍣ㄧ储寮 + - `doorIndex`锛氶棬绱㈠紩 +- **杩斿洖鍊**锛歚true`=鎵撳紑锛宍false`=鍏抽棴 + +#### 3.2 璁剧疆闂ㄦ帶鍒剁洰鏍囷紙鑷姩妯″紡锛 +```csharp +doorMission.SetDoorControlTarget(controllerIndex, doorIndex, open); +``` +- **鍙傛暟**锛 + - `controllerIndex`锛氶棬鎺у埗鍣ㄧ储寮 + - `doorIndex`锛氶棬绱㈠紩 + - `open`锛歚true`=鎵撳紑锛宍false`=鍏抽棴 + +**娉ㄦ剰**锛氭鏂规硶浠呰缃洰鏍囨帶鍒剁姸鎬侊紝瀹為檯閫氫俊鐢遍棬鎺у埗鍣ㄥ唴閮ㄧ嚎绋嬪畬鎴愶紝纭繚绾跨▼瀹夊叏銆傛鏂规硶浼氱珛鍗崇敓鏁堬紝浣嗗彲鑳借鎵嬪姩鎺у埗瑕嗙洊銆 + +#### 3.3 璁剧疆鎵嬪姩鎺у埗鐩爣 +```csharp +bool success = doorMission.SetManualDoorControl(controllerIndex, doorIndex, open, holdSeconds); +``` +- **鍙傛暟**锛 + - `controllerIndex`锛氶棬鎺у埗鍣ㄧ储寮 + - `doorIndex`锛氶棬绱㈠紩 + - `open`锛歚true`=鎵撳紑锛宍false`=鍏抽棴 + - `holdSeconds`锛氭墜鍔ㄤ繚鎸佺鏁帮紙鍙夛紝榛樿10绉掞級 +- **杩斿洖鍊**锛歚true`=鎴愬姛锛宍false`=澶辫触锛堝綋鏈夎溅杈嗗崰鐢ㄤ笖灏濊瘯鍏抽棴鏃惰繑鍥瀎alse锛 + +**鍔熻兘璇存槑**锛 +- 鎵嬪姩鎺у埗浼樺厛绾ч珮浜庤嚜鍔ㄦ帶鍒 +- 鎵嬪姩鎺у埗浼氬湪鎸囧畾鏃堕棿鍚庤嚜鍔ㄨ繃鏈燂紝鎭㈠鑷姩妯″紡 +- **瀹夊叏淇濇姢**锛氬綋闂ㄥ尯鍩熷唴鏈夎溅杈嗗崰鐢ㄦ椂锛岀姝㈡墜鍔ㄥ叧闂棬 +- 鎵嬪姩鎺у埗杩囨湡鍚庯紝绯荤粺鑷姩鎭㈠鑷姩鎺у埗閫昏緫 + +#### 3.4 娓呴櫎鎵嬪姩鎺у埗 +```csharp +doorMission.ClearManualDoorControl(controllerIndex, doorIndex); +``` +- **鍙傛暟**锛 + - `controllerIndex`锛氶棬鎺у埗鍣ㄧ储寮 + - `doorIndex`锛氶棬绱㈠紩 + +**鍔熻兘璇存槑**锛氱珛鍗虫竻闄ゆ墜鍔ㄦ帶鍒惰姹傦紝鎭㈠鑷姩鎺у埗妯″紡銆 + +#### 3.5 娓呯┖杞﹁締鍗犵敤 +```csharp +doorMission.ClearCarsInArea(controllerIndex, doorIndex); +``` +- **鍙傛暟**锛 + - `controllerIndex`锛氶棬鎺у埗鍣ㄧ储寮 + - `doorIndex`锛氶棬绱㈠紩 + +**鍔熻兘璇存槑**锛氭竻绌烘寚瀹氶棬鐨勮溅杈嗗崰鐢ㄨ褰曘傛竻绌哄悗锛屽鏋滈棬澶勪簬鎵撳紑鐘舵佷笖娌℃湁鍏朵粬灏忚溅闇瑕佽繘鍏ワ紝闂ㄤ細鑷姩鍏抽棴銆 + +#### 3.6 鑾峰彇闂ㄦ帶鍒剁姸鎬 +```csharp +var status = doorMission.GetDoorControlStatus(controllerIndex, doorIndex); +``` +- **鍙傛暟**锛 + - `controllerIndex`锛氶棬鎺у埗鍣ㄧ储寮 + - `doorIndex`锛氶棬绱㈠紩 +- **杩斿洖鍊**锛歚DoorControlStatus` 瀵硅薄锛屽寘鍚細 + - `Target`锛氬綋鍓嶇洰鏍囩姸鎬侊紙true=鎵撳紑锛宖alse=鍏抽棴锛 + - `Source`锛氭帶鍒舵潵婧愶紙`ControlSource.Auto` 鎴 `ControlSource.Manual`锛 + - `ManualRemainingSeconds`锛氭墜鍔ㄦ帶鍒跺墿浣欑鏁帮紙浠呭綋Source=Manual鏃舵湁鏁堬級 + - `CarsInArea`锛氳溅杈嗗崰鐢ㄥ垪琛 + +#### 3.7 鑾峰彇杞﹁締鍗犵敤鎯呭喌 +```csharp +var cars = doorMission.GetCarsInArea(controllerIndex, doorIndex); +``` +- **鍙傛暟**锛 + - `controllerIndex`锛氶棬鎺у埗鍣ㄧ储寮 + - `doorIndex`锛氶棬绱㈠紩 +- **杩斿洖鍊**锛氳溅杈咺D鍒楄〃锛坄IReadOnlyList`锛 + +--- + +## 閰嶇疆绠$悊 + +### 1. 閰嶇疆鏂囦欢鏍煎紡 + +閰嶇疆鏂囦欢 `DoorConfig.json` 浣嶄簬绋嬪簭鏍圭洰褰曪紝鏍煎紡濡備笅锛 + +```json +[ + { + "Index": 1, + "Ip": "192.168.1.100", + "Port": 502, + "Type": "ModbusDoorController", + "Doors": [ + { + "Index": 1, + "ControlAddress": 0, + "OpenStatusAddress": 0 + }, + { + "Index": 2, + "ControlAddress": 1, + "OpenStatusAddress": 1 + } + ] + }, + { + "Index": 2, + "Ip": "192.168.1.101", + "Port": 502, + "Type": "ModbusDoorController", + "Doors": [ + { + "Index": 1, + "ControlAddress": 0, + "OpenStatusAddress": 0 + } + ] + } +] +``` + +### 2. 閰嶇疆鐣岄潰 + +閫氳繃璋冪敤 `DoorMission.OpenViewer()` 鎵撳紑闂ㄦ帶鍒跺櫒绠$悊鐣岄潰锛屽彲浠ワ細 +- 娣诲姞銆佸垹闄ゃ佷慨鏀归棬鎺у埗鍣 +- 涓烘瘡涓棬鎺у埗鍣ㄦ坊鍔犮佸垹闄ゃ佷慨鏀归棬閰嶇疆 +- 淇濆瓨閰嶇疆鍒 `DoorConfig.json` + +**鎵撳紑閰嶇疆鐣岄潰**锛 +```csharp +DoorMission.OpenViewer(); +``` + +--- + +## 闂ㄦ帶閫昏緫 + +### 1. 浜嬩欢鍝嶅簲娴佺▼ + +#### 1.1 BeforeLock 浜嬩欢 +褰撳皬杞﹀嵆灏嗛攣瀹氱珯鐐规椂瑙﹀彂锛 +1. 妫鏌ョ珯鐐圭殑 `EnterDoor` 瀛楁 +2. 妫鏌ュ皬杞﹀綋鍓嶇珯鐐圭殑 `PreEnterDoor` 瀛楁鏄惁鍖归厤 +3. 濡傛灉鍖归厤锛岃缃 `_needOpen[(controllerIndex, doorIndex)] = true` +4. 杩斿洖闂ㄧ殑褰撳墠鐘舵侊紙濡傛灉闂ㄥ凡鎵撳紑鍒欏厑璁搁攣瀹氾級 + +#### 1.2 OnLockAcquired 浜嬩欢 +褰撳皬杞︽垚鍔熼攣瀹氱珯鐐规椂瑙﹀彂锛 +1. 妫鏌ョ珯鐐圭殑 `EnterDoor` 瀛楁 +2. 妫鏌ュ皬杞﹀綋鍓嶇珯鐐圭殑 `PreEnterDoor` 瀛楁鏄惁鍖归厤 +3. 濡傛灉鍖归厤锛屽皢灏忚溅ID娣诲姞鍒 `carsInAreas[(controllerIndex, doorIndex)]` +4. 璁剧疆 `_needOpen[(controllerIndex, doorIndex)] = false` + +#### 1.3 AfterLeave 浜嬩欢 +褰撳皬杞︾寮绔欑偣鏃惰Е鍙戯細 +1. 妫鏌ョ珯鐐圭殑 `LeaveDoor` 瀛楁 +2. 妫鏌ュ皬杞﹀綋鍓嶇珯鐐圭殑 `RearLeaveDoor` 瀛楁鏄惁鍖归厤 +3. 濡傛灉鍖归厤锛屼粠 `carsInAreas[(controllerIndex, doorIndex)]` 涓Щ闄ゅ皬杞D + +### 2. 闂ㄦ帶鐘舵佺洃鎺т笌浠茶 + +`MonitorDoorLogicAsync` 浠诲姟姣500姣鎵ц涓娆★紝妫鏌ユ瘡涓棬鐨勬帶鍒堕昏緫骞惰繘琛屼徊瑁侊細 + +```csharp +// 鑷姩鐩爣锛氭湁杞︽垨闇瑕佹墦寮 +var needOpen = _needOpen.TryGetValue(key, out var open) && open; +var hasCarsInArea = carsInAreas.TryGetValue(key, out var cars) && cars.Count > 0; +var autoTarget = needOpen || hasCarsInArea; + +// 鎵嬪姩璇锋眰浠茶锛氫紭鍏堢骇 Manual > Auto锛屾墜鍔ㄨ繃鏈熷悗鑷姩鎭㈠ +bool finalTarget = autoTarget; +if (_manualRequests.TryGetValue(key, out var manual)) +{ + if (manual.ExpireAt <= DateTime.Now) + { + _manualRequests.Remove(key); // 鎵嬪姩鎺у埗杩囨湡锛岀Щ闄 + } + else + { + finalTarget = manual.Target; // 鎵嬪姩鎺у埗鏈夋晥锛屼娇鐢ㄦ墜鍔ㄧ洰鏍 + } +} + +// 璁剧疆闂ㄧ殑鐩爣鎺у埗鐘舵 +controller.SetDoorControlTarget(doorIndex, finalTarget); +``` + +**閫昏緫璇存槑**锛 +- **鑷姩鐩爣璁$畻**锛 + - 濡傛灉 `_needOpen[key] = true`锛岄棬闇瑕佹墦寮锛堝皬杞﹀嵆灏嗚繘鍏ワ級 + - 濡傛灉 `carsInAreas[key]` 涓湁灏忚溅锛岄棬闇瑕佷繚鎸佹墦寮锛堝皬杞﹀湪鍖哄煙鍐咃級 + - 鍏朵粬鎯呭喌锛岃嚜鍔ㄧ洰鏍囦负鍏抽棴 +- **鎺у埗浠茶**锛 + - 鎵嬪姩鎺у埗浼樺厛绾ч珮浜庤嚜鍔ㄦ帶鍒 + - 濡傛灉瀛樺湪鏈夋晥鐨勬墜鍔ㄦ帶鍒惰姹傦紙鏈繃鏈燂級锛屼娇鐢ㄦ墜鍔ㄧ洰鏍 + - 鎵嬪姩鎺у埗杩囨湡鍚庯紝鑷姩绉婚櫎骞舵仮澶嶈嚜鍔ㄦ帶鍒 + - 鏈缁堢洰鏍囧啓鍏ラ棬鎺у埗鍣ㄧ殑 `DoorControlTargets` 瀛楁 + +--- + +## 绔欑偣閰嶇疆 + +### 1. 绔欑偣瀛楁璇存槑 + +绔欑偣闇瑕侀厤缃互涓嬪瓧娈典互瀹炵幇闂ㄦ帶鍔熻兘锛 + +| 瀛楁鍚 | 绫诲瀷 | 璇存槑 | 绀轰緥 | +|--------|------|------|------| +| `EnterDoor` | string | 杩涚珯闂ㄦ爣璇嗙锛堟牸寮忥細鎺у埗鍣ㄧ储寮.闂ㄧ储寮曪級 | `"1.2"` | +| `LeaveDoor` | string | 绂荤珯闂ㄦ爣璇嗙锛堟牸寮忥細鎺у埗鍣ㄧ储寮.闂ㄧ储寮曪級 | `"2.3"` | + +### 2. 灏忚溅绔欑偣瀛楁璇存槑 + +灏忚溅褰撳墠绔欑偣闇瑕侀厤缃互涓嬪瓧娈碉細 + +| 瀛楁鍚 | 绫诲瀷 | 璇存槑 | 绀轰緥 | +|--------|------|------|------| +| `PreEnterDoor` | string | 鍓嶆柟杩涚珯闂ㄦ爣璇嗙锛堜笌鐩爣绔欑偣鐨 `EnterDoor` 鍖归厤锛 | `"1.2"` | +| `RearLeaveDoor` | string | 鍚庢柟绂荤珯闂ㄦ爣璇嗙锛堜笌鐩爣绔欑偣鐨 `LeaveDoor` 鍖归厤锛 | `"2.3"` | + +### 3. 閰嶇疆绀轰緥 + +**绔欑偣閰嶇疆**锛 +```json +{ + "id": 100, + "name": "绔欑偣A", + "fields": { + "EnterDoor": "1.2", + "LeaveDoor": "2.3" + } +} +``` + +**灏忚溅绔欑偣閰嶇疆**锛 +```json +{ + "id": 50, + "name": "灏忚溅褰撳墠浣嶇疆", + "fields": { + "PreEnterDoor": "1.2", + "RearLeaveDoor": "2.3" + } +} +``` + +**宸ヤ綔娴佺▼**锛 +1. 灏忚溅浠庣珯鐐50椹跺悜绔欑偣100 +2. 鍒拌揪绔欑偣100鏃讹紝瑙﹀彂 `BeforeLock` 浜嬩欢 +3. 绯荤粺妫鏌ョ珯鐐100鐨 `EnterDoor`锛坄"1.2"`锛夋槸鍚︿笌绔欑偣50鐨 `PreEnterDoor`锛坄"1.2"`锛夊尮閰 +4. 濡傛灉鍖归厤锛岃缃帶鍒跺櫒1鐨勯棬2涓烘墦寮鐘舵 +5. 闂ㄦ墦寮鍚庯紝灏忚溅閿佸畾绔欑偣100 +6. 瑙﹀彂 `OnLockAcquired` 浜嬩欢锛岄棬淇濇寔鎵撳紑鐘舵 +7. 灏忚溅绂诲紑绔欑偣100鏃讹紝瑙﹀彂 `AfterLeave` 浜嬩欢 +8. 绯荤粺妫鏌ョ珯鐐100鐨 `LeaveDoor`锛坄"2.3"`锛夋槸鍚︿笌绔欑偣50鐨 `RearLeaveDoor`锛坄"2.3"`锛夊尮閰 +9. 濡傛灉鍖归厤锛屼粠鍖哄煙鍐呭皬杞﹀垪琛ㄤ腑绉婚櫎璇ュ皬杞 +10. 濡傛灉娌℃湁鍏朵粬灏忚溅鍦ㄥ尯鍩熷唴锛岄棬鑷姩鍏抽棴 + +--- + +## UI鐣岄潰 + +### 1. 閰嶇疆绠$悊鐣岄潰 + +**鎵撳紑鏂瑰紡**锛 +```csharp +DoorMission.OpenViewer(); +``` + +**鍔熻兘**锛 +- 闂ㄦ帶鍒跺櫒鍒楄〃绠$悊锛堟坊鍔犮佸垹闄ゃ佷慨鏀癸級 +- 闂ㄥ垪琛ㄧ鐞嗭紙涓烘瘡涓帶鍒跺櫒娣诲姞銆佸垹闄ゃ佷慨鏀归棬锛 +- 绫诲瀷閫夋嫨锛堣嚜鍔ㄨ瘑鍒墍鏈夊甫 `DoorTypeAttribute` 鐨勬帶鍒跺櫒绫诲瀷锛 +- 閰嶇疆淇濆瓨鍒 `DoorConfig.json` + +### 2. 鐩戞帶鐣岄潰 + +**鎵撳紑鏂瑰紡**锛 +```csharp +DoorMission.OpenMonitor(); +``` + +**鍔熻兘**锛 +- 瀹炴椂鏄剧ず鎵鏈夐棬鐨勭姸鎬 +- 鏄剧ず鎺у埗鍣ㄧ储寮曘侀棬绱㈠紩銆佸綋鍓嶇姸鎬併佹帶鍒剁洰鏍囥佽溅杈嗗崰鐢 +- 鏄剧ず鎺у埗鏉ユ簮鍜屾墜鍔ㄦ帶鍒跺墿浣欐椂闂 +- 鏄剧ず鎺у埗鍦板潃鍜屽紑鍒颁綅淇″彿鍦板潃 +- 鎵嬪姩鎺у埗闂ㄥ紑鍏筹紙鎵撳紑/鍏抽棴锛屽甫瀹夊叏淇濇姢锛 +- 娓呯┖杞﹁締鍗犵敤璁板綍 +- 鑷姩鍒锋柊锛堥粯璁1绉掑埛鏂伴棿闅旓級 + +**鏄剧ず淇℃伅**锛 +| 鍒楀悕 | 璇存槑 | +|------|------| +| 鎺у埗鍣ㄧ紪鐮 | 闂ㄦ帶鍒跺櫒绱㈠紩 | +| 闂ㄧ紪鐮 | 闂ㄧ储寮 | +| 褰撳墠鐘舵 | 闂ㄧ殑褰撳墠鐘舵侊紙寮/鍏筹紝甯﹂鑹叉爣璇嗭細缁胯壊=鎵撳紑锛岀孩鑹=鍏抽棴锛 | +| 鎺у埗鐩爣 | 闂ㄧ殑鐩爣鎺у埗鐘舵侊紙寮/鍏筹紝甯﹂鑹叉爣璇嗭細缁胯壊=寮锛岀孩鑹=鍏筹級 | +| 杞﹁締鍗犵敤 | 闂ㄥ尯鍩熷唴鐨勫皬杞D鍒楄〃锛堝涓狪D鐢ㄩ楀彿鍒嗛殧锛屾棤杞﹁締鏄剧ず"鏃"锛 | +| 鎺у埗鏉ユ簮 | 褰撳墠鎺у埗鏉ユ簮锛堣嚜鍔/鎵嬪姩锛 | +| 鎵嬪姩鍓╀綑(s) | 鎵嬪姩鎺у埗鍓╀綑绉掓暟锛堜粎褰撴帶鍒舵潵婧=鎵嬪姩鏃舵樉绀猴紝鑷姩鏃舵樉绀"-"锛 | +| 鎺у埗鍦板潃 | Modbus 绾垮湀鍦板潃 | +| 寮鍒颁綅鍦板潃 | Modbus 绂绘暎杈撳叆鍦板潃 | + +**鎵嬪姩鎺у埗鍔熻兘**锛 +- **鎵撳紑鎸夐挳**锛氳缃墜鍔ㄦ墦寮鎺у埗锛岄粯璁や繚鎸10绉 +- **鍏抽棴鎸夐挳**锛氳缃墜鍔ㄥ叧闂帶鍒讹紝榛樿淇濇寔10绉 + - **瀹夊叏淇濇姢**锛氬綋闂ㄥ尯鍩熷唴鏈夎溅杈嗗崰鐢ㄦ椂锛屽叧闂寜閽嚜鍔ㄧ鐢紝鏃犳硶鎵ц鍏抽棴鎿嶄綔 + - 蹇呴』鍏堟竻绌鸿溅杈嗗崰鐢紝鎵嶈兘鎵嬪姩鍏抽棴闂 +- **娓呯┖鍗犵敤鎸夐挳**锛氭竻绌洪変腑闂ㄧ殑杞﹁締鍗犵敤璁板綍 + - 娓呯┖鍚庯紝濡傛灉闂ㄥ浜庢墦寮鐘舵佷笖娌℃湁鍏朵粬灏忚溅闇瑕佽繘鍏ワ紝闂ㄤ細鑷姩鍏抽棴 + - 娓呯┖鍗犵敤鍚庯紝鍙互鎵ц鎵嬪姩鍏抽棴鎿嶄綔 + +**鎺у埗浼樺厛绾ц鏄**锛 +- 鎵嬪姩鎺у埗浼樺厛绾ч珮浜庤嚜鍔ㄦ帶鍒 +- 鎵嬪姩鎺у埗浼氬湪鎸囧畾鏃堕棿锛堥粯璁10绉掞級鍚庤嚜鍔ㄨ繃鏈燂紝鎭㈠鑷姩妯″紡 +- 鍙互閫氳繃"娓呯┖鍗犵敤"鎸夐挳娓呯┖杞﹁締鍗犵敤锛岀劧鍚庢墜鍔ㄥ叧闂棬 + +--- + +## 鍙傛暟璇存槑 + +### 1. 鐩戞帶闂撮殧 + +| 鍙傛暟 | 榛樿鍊 | 璇存槑 | +|------|--------|------| +| 閰嶇疆鐩戞帶闂撮殧 | 10绉 | 妫鏌ラ厤缃枃浠剁殑闂撮殧 | +| 闂ㄦ帶閫昏緫鐩戞帶闂撮殧 | 500姣 | 妫鏌ラ棬鎺ч昏緫鐨勯棿闅旓紙宸蹭紭鍖栵級 | +| 鐩戞帶鐣岄潰鍒锋柊闂撮殧 | 1绉 | 鐩戞帶鐣岄潰鑷姩鍒锋柊闂撮殧 | +| 鎵嬪姩鎺у埗榛樿淇濇寔鏃堕棿 | 10绉 | 鎵嬪姩鎺у埗璇锋眰鐨勯粯璁よ繃鏈熸椂闂 | + +### 2. 绾跨▼瀹夊叏璇存槑 + +- **闂ㄧ姸鎬佽鍙**锛氶氳繃 `controller.DoorStates` 瀛楀吀璁块棶锛屾墍鏈夎鍐欐搷浣滃彈閿佷繚鎶 +- **闂ㄦ帶鍒跺啓鍏**锛氶氳繃 `controller.SetDoorControlTarget()` 璁剧疆鐩爣鐘舵侊紝瀹為檯閫氫俊鐢遍棬鎺у埗鍣ㄥ唴閮ㄧ嚎绋嬪畬鎴 +- **閰嶇疆鍚屾**锛氶厤缃彉鏇存椂浣跨敤閿佷繚鎶わ紝纭繚绾跨▼瀹夊叏 +- **鎺у埗浠茶**锛氭墜鍔ㄦ帶鍒惰姹傚拰鑷姩鎺у埗閫昏緫鐨勪徊瑁佸湪鍚屼竴閿佸唴瀹屾垚锛岀‘淇濈嚎绋嬪畨鍏 +- **杞﹁締鍗犵敤绠$悊**锛氳溅杈嗗崰鐢ㄧ殑澧炲垹鏀规煡鎿嶄綔鍧囧彈閿佷繚鎶 + +### 3. 鎺у埗浠茶鏈哄埗 + +绯荤粺閲囩敤**鎺у埗浠茶鏈哄埗**鏉ュ崗璋冩墜鍔ㄦ帶鍒跺拰鑷姩鎺у埗锛 + +- **浼樺厛绾**锛氭墜鍔ㄦ帶鍒 > 鑷姩鎺у埗 +- **鎵嬪姩鎺у埗杩囨湡**锛氭墜鍔ㄦ帶鍒惰姹備細鍦ㄦ寚瀹氭椂闂达紙榛樿10绉掞級鍚庤嚜鍔ㄨ繃鏈燂紝杩囨湡鍚庢仮澶嶈嚜鍔ㄦ帶鍒 +- **瀹夊叏淇濇姢**锛氬綋闂ㄥ尯鍩熷唴鏈夎溅杈嗗崰鐢ㄦ椂锛岀姝㈡墜鍔ㄥ叧闂棬锛岀‘淇濆畨鍏 +- **浠茶娴佺▼**锛 + 1. 璁$畻鑷姩鐩爣锛堝熀浜庤溅杈嗗崰鐢ㄥ拰闇瑕佹墦寮鏍囧織锛 + 2. 妫鏌ユ槸鍚﹀瓨鍦ㄦ湁鏁堢殑鎵嬪姩鎺у埗璇锋眰 + 3. 濡傛灉鎵嬪姩鎺у埗鏈繃鏈燂紝浣跨敤鎵嬪姩鐩爣锛涘惁鍒欎娇鐢ㄨ嚜鍔ㄧ洰鏍 + 4. 灏嗘渶缁堢洰鏍囧啓鍏ラ棬鎺у埗鍣ㄧ殑 `DoorControlTargets` 瀛楁 + +--- + +## 鏁呴殰鎺掓煡 + +### 闂1锛氶棬鎺у埗鍣ㄦ棤娉曡繛鎺 + +**鎺掓煡姝ラ**锛 +1. 妫鏌ラ厤缃枃浠朵腑鐨 IP 鍜岀鍙f槸鍚︽纭 +2. 妫鏌ョ綉缁滆繛鎺ユ槸鍚︽甯 +3. 鏌ョ湅璇婃柇鏃ュ織涓殑閿欒淇℃伅 +4. 纭闂ㄦ帶鍒跺櫒纭欢鏄惁鍦ㄧ嚎 + +**璇婃柇鍛戒护**锛 +```csharp +var controllers = doorMission.GetDoorControllers(); +foreach (var controller in controllers) +{ + Console.WriteLine($"鎺у埗鍣▄controller.Index}: IP={controller.Ip}, Port={controller.Port}, State={controller.State}, Error={controller.ErrorMessage}"); +} +``` + +### 闂2锛氶棬涓嶈嚜鍔ㄥ紑鍚 + +**鎺掓煡姝ラ**锛 +1. 纭 `DoorMission` 浠诲姟宸插惎鍔 +2. 妫鏌ョ珯鐐圭殑 `EnterDoor` 瀛楁鏄惁閰嶇疆姝g‘ +3. 妫鏌ュ皬杞︾珯鐐圭殑 `PreEnterDoor` 瀛楁鏄惁涓庣洰鏍囩珯鐐圭殑 `EnterDoor` 鍖归厤 +4. 鏌ョ湅闂ㄦ帶鍒跺櫒鐨勮繛鎺ョ姸鎬佹槸鍚︿负 `Online` +5. 妫鏌ラ棬鐨勭姸鎬佹槸鍚︽纭鍙 + +**璇婃柇鍛戒护**锛 +```csharp +// 妫鏌ラ棬鐘舵 +bool isOpen = doorMission.GetDoorState(1, 2); +Console.WriteLine($"鎺у埗鍣1闂2鐨勭姸鎬: {(isOpen ? "鎵撳紑" : "鍏抽棴")}"); + +// 妫鏌ラ棬鎺у埗鍣ㄧ姸鎬 +var controllers = doorMission.GetDoorControllers(); +var controller = controllers.FirstOrDefault(c => c.Index == 1); +if (controller != null) +{ + Console.WriteLine($"鎺у埗鍣ㄧ姸鎬: {controller.State}"); + Console.WriteLine($"鏄惁鍦ㄧ嚎: {controller.IsOnline}"); + Console.WriteLine($"閿欒淇℃伅: {controller.ErrorMessage}"); +} +``` + +### 闂3锛氶厤缃枃浠舵洿鏂板悗涓嶇敓鏁 + +**鎺掓煡姝ラ**锛 +1. 纭閰嶇疆鏂囦欢鏍煎紡姝g‘锛圝SON鏍煎紡锛 +2. 妫鏌ラ厤缃枃浠舵槸鍚︿繚瀛樻垚鍔 +3. 绛夊緟鏈澶10绉掞紝绯荤粺浼氳嚜鍔ㄦ娴嬫洿鏂 +4. 鏌ョ湅璇婃柇鏃ュ織涓殑閰嶇疆鍚屾淇℃伅 + +### 闂4锛氱洃鎺х晫闈㈡棤鏁版嵁 + +**鎺掓煡姝ラ**锛 +1. 纭 `DoorMission` 浠诲姟宸插惎鍔 +2. 妫鏌ユ槸鍚︽湁閰嶇疆鐨勯棬鎺у埗鍣 +3. 鏌ョ湅闂ㄦ帶鍒跺櫒鏄惁鎴愬姛杩炴帴 +4. 妫鏌ョ洃鎺х晫闈㈢殑鍒锋柊闂撮殧璁剧疆 + +### 闂5锛氭墜鍔ㄥ叧闂寜閽棤娉曠偣鍑 + +**鍘熷洜**锛 +- 闂ㄥ尯鍩熷唴鏈夎溅杈嗗崰鐢紝绯荤粺瀹夊叏淇濇姢鏈哄埗绂佹鎵嬪姩鍏抽棴 + +**瑙e喅鏂规硶**锛 +1. 鍏堢偣鍑"娓呯┖鍗犵敤"鎸夐挳锛屾竻绌鸿溅杈嗗崰鐢ㄨ褰 +2. 娓呯┖鍚庯紝鍏抽棴鎸夐挳浼氳嚜鍔ㄥ惎鐢 +3. 鐒跺悗鍙互鎵ц鎵嬪姩鍏抽棴鎿嶄綔 + +### 闂6锛氭墜鍔ㄦ帶鍒朵笉鐢熸晥 + +**鎺掓煡姝ラ**锛 +1. 妫鏌ユ墜鍔ㄦ帶鍒舵槸鍚﹀凡杩囨湡锛堥粯璁10绉掞級 +2. 鏌ョ湅"鎺у埗鏉ユ簮"鍒楋紝纭鏄惁涓"鎵嬪姩" +3. 鏌ョ湅"鎵嬪姩鍓╀綑(s)"鍒楋紝纭鍓╀綑鏃堕棿 +4. 濡傛灉宸茶繃鏈燂紝鎵嬪姩鎺у埗浼氳嚜鍔ㄦ仮澶嶄负鑷姩妯″紡 +5. 鍙互閫氳繃鐩戞帶鐣岄潰閲嶆柊璁剧疆鎵嬪姩鎺у埗 + +--- + +## 闄勫綍 + +### A. 闂ㄦ爣璇嗙瑙f瀽 + +闂ㄦ爣璇嗙鏍煎紡锛歚鎺у埗鍣ㄧ储寮.闂ㄧ储寮昤 + +**瑙f瀽瑙勫垯**锛 +- 蹇呴』鍖呭惈涓涓偣鍙凤紙`.`锛 +- 鐐瑰彿鍓嶅悗蹇呴』涓烘暣鏁 +- 瑙f瀽澶辫触鏃惰繑鍥 `null` + +**绀轰緥**锛 +- `"1.2"` 鈫 `(controllerIndex: 1, doorIndex: 2)` 鉁 +- `"10.5"` 鈫 `(controllerIndex: 10, doorIndex: 5)` 鉁 +- `"1"` 鈫 `null` 鉂岋紙缂哄皯鐐瑰彿锛 +- `"1.2.3"` 鈫 `null` 鉂岋紙澶氫釜鐐瑰彿锛 +- `"a.2"` 鈫 `null` 鉂岋紙闈炴暟瀛楋級 + +### B. 璇婃柇鏃ュ織璇存槑 + +绯荤粺浼氬湪浠ヤ笅鎯呭喌璁板綍璇婃柇鏃ュ織锛 +- 闂ㄦ帶鍒跺櫒杩炴帴鎴愬姛/澶辫触 +- 闂ㄦ帶鍒跺櫒閰嶇疆鍙樻洿 +- 闂ㄧ姸鎬佽鍙栧け璐 +- 闂ㄦ帶鍒跺啓鍏ュけ璐 +- 閰嶇疆鍔犺浇澶辫触 + +**鏃ュ織浣嶇疆**锛氱郴缁熻瘖鏂棩蹇 + +### C. 绫荤粨鏋勫叧绯诲浘 + +``` +DoorMission (闂ㄦ帶杩涚▼) + 鈹 + 鈹溾攢鈹 BasicDoorController (鎶借薄鍩虹被) + 鈹 鈹 + 鈹 鈹斺攢鈹 ModbusDoorController (Modbus 瀹炵幇) + 鈹 + 鈹溾攢鈹 DoorManager (閰嶇疆鐣岄潰) + 鈹 + 鈹溾攢鈹 DoorMonitor (鐩戞帶鐣岄潰) + 鈹 + 鈹斺攢鈹 DoorModel (閰嶇疆妯″瀷) +``` + +### D. 鎺у埗鏉ユ簮鏋氫妇 + +```csharp +public enum ControlSource +{ + Auto = 0, // 鑷姩鎺у埗 + Manual = 1 // 鎵嬪姩鎺у埗 +} +``` + +### E. 闂ㄦ帶鍒剁姸鎬佺粨鏋 + +```csharp +public class DoorControlStatus +{ + public bool Target { get; set; } // 褰撳墠鐩爣鐘舵侊紙true=鎵撳紑锛宖alse=鍏抽棴锛 + public ControlSource Source { get; set; } // 鎺у埗鏉ユ簮锛圓uto/Manual锛 + public double? ManualRemainingSeconds { get; set; } // 鎵嬪姩鎺у埗鍓╀綑绉掓暟锛堜粎褰揝ource=Manual鏃舵湁鏁堬級 + public IReadOnlyList CarsInArea { get; set; } // 杞﹁締鍗犵敤鍒楄〃 +} +``` + +### F. 鐗堟湰鍘嗗彶 + +| 鐗堟湰 | 鏃ユ湡 | 涓昏鏇存柊 | +|------|------|----------| +| 1.0 | 2025-01 | 鍒濆鐗堟湰锛屾敮鎸 Modbus 闂ㄦ帶鍒跺櫒 | +| 1.1 | 2025-01 | 鏂板闂ㄦ帶浠茶鏈哄埗锛屾敮鎸佹墜鍔ㄦ帶鍒朵笌鑷姩鎺у埗鍗忚皟 | +| 1.2 | 2025-01 | 鏂板杞﹁締鍗犵敤绠$悊鍔熻兘锛岀洃鎺х晫闈㈡樉绀鸿溅杈嗗崰鐢ㄦ儏鍐 | +| 1.3 | 2025-01 | 浼樺寲闂ㄦ帶閫昏緫鐩戞帶闂撮殧鑷500ms锛屾柊澧炲畨鍏ㄤ繚鎶ゆ満鍒讹紙鍗犵敤鏃剁姝㈡墜鍔ㄥ叧闂級 | + +--- + +**鏂囨。鏇存柊鏃堕棿**锛2025-01-21 diff --git a/StandardScene.Core/ExtendDevice/ButtonBox/BasicButtonBox.cs b/StandardScene.Core/ExtendDevice/ButtonBox/BasicButtonBox.cs new file mode 100644 index 0000000..39b4531 --- /dev/null +++ b/StandardScene.Core/ExtendDevice/ButtonBox/BasicButtonBox.cs @@ -0,0 +1,263 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using SimpleCore.Library; + +namespace StandardScene.ExtendDevice.ButtonBox +{ + /// + /// 鎸夐挳鐘舵佹灇涓 + /// + public enum ButtonState + { + /// + /// 鏈寜涓 + /// + Released = 0, + + /// + /// 宸叉寜涓 + /// + Pressed = 1, + + /// + /// 鏈煡鐘舵 + /// + Unknown = 2 + } + + /// + /// 鎸夐挳鐩掔姸鎬佹灇涓 + /// + public enum ButtonBoxState + { + /// + /// 绂荤嚎 + /// + Offline = 0, + + /// + /// 鍦ㄧ嚎 + /// + Online = 1, + + /// + /// 杩炴帴涓 + /// + Connecting = 2, + + /// + /// 閿欒 + /// + Error = 3 + } + + /// + /// 鍩虹鎸夐挳鐩掔被锛屽寘鍚姸鎬佹満 + /// + public abstract class BasicButtonBox + { + /// + /// 鎸夐挳鐩掔储寮 + /// + public int Index { get; set; } + + /// + /// IP鍦板潃 + /// + public string Ip { get; set; } = string.Empty; + + /// + /// 绔彛 + /// + public int Port { get; set; } + + /// + /// 鎸夐挳鐩掔姸鎬 + /// + public ButtonBoxState State { get; protected set; } = ButtonBoxState.Offline; + + /// + /// 鏄惁鍦ㄧ嚎 + /// + public bool IsOnline => State == ButtonBoxState.Online; + + /// + /// 鎸夐挳鐘舵佸瓧鍏革紝閿负鎸夐挳绱㈠紩 + /// + public Dictionary ButtonStates { get; protected set; } = new Dictionary(); + + /// + /// 鎸夐挳閰嶇疆淇℃伅瀛楀吀锛岄敭涓烘寜閽储寮 + /// + public Dictionary ButtonConfigs { get; protected set; } = new Dictionary(); + + /// + /// 鎸夐挳鍔ㄤ綔鎵ц鍚庢竻闆跺搴斿瘎瀛樺櫒锛氶粯璁ょ┖瀹炵幇锛屽叿浣撶洅鍨嬶紙濡 Azowie锛夋寜闇閲嶅啓銆 + /// 鐢ㄤ簬瑙h ButtonMission 瀵瑰叿浣撶洅鍨嬬殑 is 鍒ゆ柇锛屼究浜庨┍鍔ㄥ绉昏嚦鍗槦 dll銆 + /// + public virtual void ClearButtonRegister(int buttonIndex) + { + } + + /// + /// 鏈鍚庢洿鏂版椂闂 + /// + public DateTime LastUpdateTime { get; protected set; } = DateTime.Now; + + /// + /// 閿欒淇℃伅 + /// + public string ErrorMessage { get; protected set; } = string.Empty; + + /// + /// 鏇存柊鎸夐挳鐩掔姸鎬 + /// + public virtual void UpdateState(ButtonBoxState newState, string errorMessage = "") + { + State = newState; + ErrorMessage = errorMessage; + LastUpdateTime = DateTime.Now; + } + + /// + /// 鏇存柊鎸夐挳鐘舵 + /// + /// 鎸夐挳绱㈠紩 + /// 鎸夐挳鐘舵 + public virtual void UpdateButtonState(int buttonIndex, ButtonState state) + { + bool hadPrev = ButtonStates.TryGetValue(buttonIndex, out var previous); + if (hadPrev && previous == state) + return; + + ButtonStates[buttonIndex] = state; + if (hadPrev) + Diagnosis.Post($"鎸夐挳鐩抺Index}_鎸夐挳{buttonIndex}:鐘舵佸彉鏇翠负{state}", "ButtonBox", false); + LastUpdateTime = DateTime.Now; + } + + /// + /// 鑾峰彇鎸夐挳鐘舵 + /// + /// 鎸夐挳绱㈠紩 + /// 鎸夐挳鐘舵侊紝濡傛灉涓嶅瓨鍦ㄥ垯杩斿洖Unknown + public virtual ButtonState GetButtonState(int buttonIndex) + { + return ButtonStates.TryGetValue(buttonIndex, out var state) ? state : ButtonState.Unknown; + } + + /// + /// 鍒濆鍖栨寜閽姸鎬 + /// + /// 鎸夐挳绱㈠紩鍒楄〃 + public virtual void InitializeButtons(List buttonIndices) + { + ButtonStates.Clear(); + foreach (var index in buttonIndices) + { + ButtonStates[index] = ButtonState.Released; + } + } + + /// + /// 鍒濆鍖栨寜閽厤缃俊鎭 + /// + /// 鎸夐挳閰嶇疆鍒楄〃 + public virtual void InitializeButtonConfigs(List buttonConfigs) + { + ButtonConfigs.Clear(); + if (buttonConfigs != null) + { + foreach (var config in buttonConfigs) + { + ButtonConfigs[config.Index] = new ButtonModel + { + Index = config.Index, + TriggerMission = config.TriggerMission, + TriggerMethod = config.TriggerMethod, + TriggerMethodParams = config.TriggerMethodParams, + TriggerState = config.TriggerState, + TriggerDelay = config.TriggerDelay + }; + } + } + } + + /// + /// 鏇存柊鎸夐挳閰嶇疆淇℃伅 + /// + /// 鎸夐挳閰嶇疆鍒楄〃 + public virtual void UpdateButtonConfigs(List buttonConfigs) + { + if (buttonConfigs == null) + { + ButtonConfigs.Clear(); + return; + } + + // 鍒涘缓閰嶇疆瀛楀吀 + var configDict = buttonConfigs.ToDictionary(b => b.Index); + + // 鍒犻櫎閰嶇疆涓笉瀛樺湪鐨勬寜閽 + var toRemove = ButtonConfigs.Keys.Where(k => !configDict.ContainsKey(k)).ToList(); + foreach (var key in toRemove) + { + ButtonConfigs.Remove(key); + } + + // 娣诲姞鎴栨洿鏂版寜閽厤缃 + foreach (var config in buttonConfigs) + { + ButtonConfigs[config.Index] = new ButtonModel + { + Index = config.Index, + TriggerMission = config.TriggerMission, + TriggerMethod = config.TriggerMethod, + TriggerMethodParams = config.TriggerMethodParams, + TriggerState = config.TriggerState, + TriggerDelay = config.TriggerDelay + }; + } + } + + /// + /// 鑾峰彇鎸夐挳閰嶇疆淇℃伅 + /// + /// 鎸夐挳绱㈠紩 + /// 鎸夐挳閰嶇疆淇℃伅锛屽鏋滀笉瀛樺湪鍒欒繑鍥瀗ull + public virtual ButtonModel GetButtonConfig(int buttonIndex) + { + return ButtonConfigs.TryGetValue(buttonIndex, out var config) ? config : null; + } + + /// + /// 杩炴帴鎸夐挳鐩 + /// + public virtual void Connect() + { + UpdateState(ButtonBoxState.Connecting); + } + + /// + /// 鏂紑杩炴帴 + /// + public virtual void Disconnect() + { + UpdateState(ButtonBoxState.Offline); + } + + /// + /// 褰撲笌姝ゆ寜閽洅缁戝畾鐨勪笟鍔℃柟娉曟墽琛屽畬鎴愬悗瑙﹀彂鐨勫洖璋冦 + /// 瀛愮被鍙噸鍐欎互瀹炵幇鎸夐挳鐏弽棣堛佽渹楦g瓑鏁堟灉銆 + /// + /// 瑙﹀彂鏈璋冪敤鐨勬寜閽厤缃 + /// 涓氬姟鏂规硶鏄惁鎵ц鎴愬姛 + public virtual void OnActionExecuted(ButtonModel buttonConfig, bool isSuccess) + { + // 鍩虹被榛樿涓嶅仛浠讳綍浜嬶紝鐢卞叿浣撳疄鐜版寜闇瑕侀噸鍐 + } + } +} diff --git a/StandardScene.Core/ExtendDevice/ButtonBox/ButtonBoxManager.Designer.cs b/StandardScene.Core/ExtendDevice/ButtonBox/ButtonBoxManager.Designer.cs new file mode 100644 index 0000000..bf7a199 --- /dev/null +++ b/StandardScene.Core/ExtendDevice/ButtonBox/ButtonBoxManager.Designer.cs @@ -0,0 +1,608 @@ +namespace StandardScene.ExtendDevice.ButtonBox +{ + partial class ButtonBoxManager + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.buttonBoxListView = new System.Windows.Forms.ListView(); + this.columnHeaderBoxIndex = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.columnHeaderIp = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.columnHeaderPort = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.columnHeaderType = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.groupBoxButtonBox = new System.Windows.Forms.GroupBox(); + this.btnSaveButtonBox = new System.Windows.Forms.Button(); + this.btnDeleteButtonBox = new System.Windows.Forms.Button(); + this.btnAddButtonBox = new System.Windows.Forms.Button(); + this.labelType = new System.Windows.Forms.Label(); + this.comboBoxType = new System.Windows.Forms.ComboBox(); + this.labelBoxIndex = new System.Windows.Forms.Label(); + this.textBoxBoxIndex = new System.Windows.Forms.TextBox(); + this.labelPort = new System.Windows.Forms.Label(); + this.textBoxPort = new System.Windows.Forms.TextBox(); + this.labelIp = new System.Windows.Forms.Label(); + this.textBoxIp = new System.Windows.Forms.TextBox(); + this.buttonListView = new System.Windows.Forms.ListView(); + this.columnHeaderButtonIndex = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.columnHeaderTriggerMission = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.columnHeaderTriggerMethod = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.columnHeaderTriggerMethodParams = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.columnHeaderTriggerState = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.columnHeaderTriggerDelay = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.groupBoxButton = new System.Windows.Forms.GroupBox(); + this.btnSaveButton = new System.Windows.Forms.Button(); + this.btnDeleteButton = new System.Windows.Forms.Button(); + this.btnAddButton = new System.Windows.Forms.Button(); + this.labelTriggerMethodParams = new System.Windows.Forms.Label(); + this.textBoxTriggerMethodParams = new System.Windows.Forms.TextBox(); + this.labelTriggerMethod = new System.Windows.Forms.Label(); + this.textBoxTriggerMethod = new System.Windows.Forms.TextBox(); + this.labelTriggerMission = new System.Windows.Forms.Label(); + this.textBoxTriggerMission = new System.Windows.Forms.TextBox(); + this.labelButtonIndex = new System.Windows.Forms.Label(); + this.textBoxButtonIndex = new System.Windows.Forms.TextBox(); + this.labelTriggerState = new System.Windows.Forms.Label(); + this.comboBoxTriggerState = new System.Windows.Forms.ComboBox(); + this.labelTriggerDelay = new System.Windows.Forms.Label(); + this.textBoxTriggerDelay = new System.Windows.Forms.TextBox(); + this.labelTitle = new System.Windows.Forms.Label(); + this.groupBoxButtonBox.SuspendLayout(); + this.groupBoxButton.SuspendLayout(); + this.SuspendLayout(); + // + // buttonBoxListView + // + this.buttonBoxListView.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left))); + this.buttonBoxListView.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; + this.buttonBoxListView.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] { + this.columnHeaderBoxIndex, + this.columnHeaderIp, + this.columnHeaderPort, + this.columnHeaderType}); + this.buttonBoxListView.Font = new System.Drawing.Font("寰蒋闆呴粦", 10.5F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + this.buttonBoxListView.FullRowSelect = true; + this.buttonBoxListView.GridLines = true; + this.buttonBoxListView.HeaderStyle = System.Windows.Forms.ColumnHeaderStyle.Nonclickable; + this.buttonBoxListView.HideSelection = false; + this.buttonBoxListView.Location = new System.Drawing.Point(15, 55); + this.buttonBoxListView.MultiSelect = false; + this.buttonBoxListView.Name = "buttonBoxListView"; + this.buttonBoxListView.OwnerDraw = true; + this.buttonBoxListView.Size = new System.Drawing.Size(450, 290); + this.buttonBoxListView.TabIndex = 0; + this.buttonBoxListView.UseCompatibleStateImageBehavior = false; + this.buttonBoxListView.View = System.Windows.Forms.View.Details; + this.buttonBoxListView.SelectedIndexChanged += new System.EventHandler(this.buttonBoxListView_SelectedIndexChanged); + // + // columnHeaderBoxIndex + // + this.columnHeaderBoxIndex.Text = "缂栫爜"; + this.columnHeaderBoxIndex.Width = 70; + // + // columnHeaderIp + // + this.columnHeaderIp.Text = "IP鍦板潃"; + this.columnHeaderIp.Width = 130; + // + // columnHeaderPort + // + this.columnHeaderPort.Text = "绔彛"; + this.columnHeaderPort.Width = 90; + // + // columnHeaderType + // + this.columnHeaderType.Text = "绫诲瀷"; + this.columnHeaderType.Width = 140; + // + // groupBoxButtonBox + // + this.groupBoxButtonBox.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); + this.groupBoxButtonBox.Controls.Add(this.btnSaveButtonBox); + this.groupBoxButtonBox.Controls.Add(this.btnDeleteButtonBox); + this.groupBoxButtonBox.Controls.Add(this.btnAddButtonBox); + this.groupBoxButtonBox.Controls.Add(this.labelType); + this.groupBoxButtonBox.Controls.Add(this.comboBoxType); + this.groupBoxButtonBox.Controls.Add(this.labelBoxIndex); + this.groupBoxButtonBox.Controls.Add(this.textBoxBoxIndex); + this.groupBoxButtonBox.Controls.Add(this.labelPort); + this.groupBoxButtonBox.Controls.Add(this.textBoxPort); + this.groupBoxButtonBox.Controls.Add(this.labelIp); + this.groupBoxButtonBox.Controls.Add(this.textBoxIp); + this.groupBoxButtonBox.Font = new System.Drawing.Font("寰蒋闆呴粦", 10.5F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + this.groupBoxButtonBox.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(68)))), ((int)(((byte)(68)))), ((int)(((byte)(68))))); + this.groupBoxButtonBox.Location = new System.Drawing.Point(15, 360); + this.groupBoxButtonBox.Name = "groupBoxButtonBox"; + this.groupBoxButtonBox.Padding = new System.Windows.Forms.Padding(12, 10, 12, 12); + this.groupBoxButtonBox.Size = new System.Drawing.Size(450, 250); + this.groupBoxButtonBox.TabIndex = 1; + this.groupBoxButtonBox.TabStop = false; + this.groupBoxButtonBox.Text = "鎸夐挳鐩掍俊鎭"; + // + // btnSaveButtonBox + // + this.btnSaveButtonBox.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(0)))), ((int)(((byte)(122)))), ((int)(((byte)(204))))); + this.btnSaveButtonBox.FlatAppearance.BorderSize = 0; + this.btnSaveButtonBox.FlatAppearance.MouseDownBackColor = System.Drawing.Color.FromArgb(((int)(((byte)(0)))), ((int)(((byte)(92)))), ((int)(((byte)(153))))); + this.btnSaveButtonBox.FlatAppearance.MouseOverBackColor = System.Drawing.Color.FromArgb(((int)(((byte)(0)))), ((int)(((byte)(102)))), ((int)(((byte)(170))))); + this.btnSaveButtonBox.FlatStyle = System.Windows.Forms.FlatStyle.Flat; + this.btnSaveButtonBox.Font = new System.Drawing.Font("寰蒋闆呴粦", 10.5F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + this.btnSaveButtonBox.ForeColor = System.Drawing.Color.White; + this.btnSaveButtonBox.Location = new System.Drawing.Point(330, 200); + this.btnSaveButtonBox.Name = "btnSaveButtonBox"; + this.btnSaveButtonBox.Size = new System.Drawing.Size(100, 38); + this.btnSaveButtonBox.TabIndex = 10; + this.btnSaveButtonBox.Text = "淇濆瓨"; + this.btnSaveButtonBox.UseVisualStyleBackColor = false; + this.btnSaveButtonBox.Click += new System.EventHandler(this.btnSaveButtonBox_Click); + // + // btnDeleteButtonBox + // + this.btnDeleteButtonBox.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(220)))), ((int)(((byte)(53)))), ((int)(((byte)(69))))); + this.btnDeleteButtonBox.FlatAppearance.BorderSize = 0; + this.btnDeleteButtonBox.FlatAppearance.MouseDownBackColor = System.Drawing.Color.FromArgb(((int)(((byte)(165)))), ((int)(((byte)(40)))), ((int)(((byte)(52))))); + this.btnDeleteButtonBox.FlatAppearance.MouseOverBackColor = System.Drawing.Color.FromArgb(((int)(((byte)(187)))), ((int)(((byte)(45)))), ((int)(((byte)(59))))); + this.btnDeleteButtonBox.FlatStyle = System.Windows.Forms.FlatStyle.Flat; + this.btnDeleteButtonBox.Font = new System.Drawing.Font("寰蒋闆呴粦", 10.5F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + this.btnDeleteButtonBox.ForeColor = System.Drawing.Color.White; + this.btnDeleteButtonBox.Location = new System.Drawing.Point(220, 200); + this.btnDeleteButtonBox.Name = "btnDeleteButtonBox"; + this.btnDeleteButtonBox.Size = new System.Drawing.Size(100, 38); + this.btnDeleteButtonBox.TabIndex = 9; + this.btnDeleteButtonBox.Text = "鍒犻櫎"; + this.btnDeleteButtonBox.UseVisualStyleBackColor = false; + this.btnDeleteButtonBox.Click += new System.EventHandler(this.btnDeleteButtonBox_Click); + // + // btnAddButtonBox + // + this.btnAddButtonBox.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(40)))), ((int)(((byte)(167)))), ((int)(((byte)(69))))); + this.btnAddButtonBox.FlatAppearance.BorderSize = 0; + this.btnAddButtonBox.FlatAppearance.MouseDownBackColor = System.Drawing.Color.FromArgb(((int)(((byte)(30)))), ((int)(((byte)(125)))), ((int)(((byte)(52))))); + this.btnAddButtonBox.FlatAppearance.MouseOverBackColor = System.Drawing.Color.FromArgb(((int)(((byte)(33)))), ((int)(((byte)(136)))), ((int)(((byte)(56))))); + this.btnAddButtonBox.FlatStyle = System.Windows.Forms.FlatStyle.Flat; + this.btnAddButtonBox.Font = new System.Drawing.Font("寰蒋闆呴粦", 10.5F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + this.btnAddButtonBox.ForeColor = System.Drawing.Color.White; + this.btnAddButtonBox.Location = new System.Drawing.Point(110, 200); + this.btnAddButtonBox.Name = "btnAddButtonBox"; + this.btnAddButtonBox.Size = new System.Drawing.Size(100, 38); + this.btnAddButtonBox.TabIndex = 8; + this.btnAddButtonBox.Text = "娣诲姞"; + this.btnAddButtonBox.UseVisualStyleBackColor = false; + this.btnAddButtonBox.Click += new System.EventHandler(this.btnAddButtonBox_Click); + // + // labelType + // + this.labelType.AutoSize = true; + this.labelType.Font = new System.Drawing.Font("寰蒋闆呴粦", 10.5F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + this.labelType.Location = new System.Drawing.Point(28, 168); + this.labelType.Name = "labelType"; + this.labelType.Size = new System.Drawing.Size(65, 24); + this.labelType.TabIndex = 7; + this.labelType.Text = "绫诲瀷锛"; + // + // comboBoxType + // + this.comboBoxType.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.comboBoxType.Font = new System.Drawing.Font("寰蒋闆呴粦", 10.5F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + this.comboBoxType.FormattingEnabled = true; + this.comboBoxType.Location = new System.Drawing.Point(110, 165); + this.comboBoxType.Name = "comboBoxType"; + this.comboBoxType.Size = new System.Drawing.Size(320, 32); + this.comboBoxType.TabIndex = 6; + // + // labelBoxIndex + // + this.labelBoxIndex.AutoSize = true; + this.labelBoxIndex.Font = new System.Drawing.Font("寰蒋闆呴粦", 10.5F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + this.labelBoxIndex.Location = new System.Drawing.Point(28, 48); + this.labelBoxIndex.Name = "labelBoxIndex"; + this.labelBoxIndex.Size = new System.Drawing.Size(65, 24); + this.labelBoxIndex.TabIndex = 1; + this.labelBoxIndex.Text = "缂栫爜锛"; + // + // textBoxBoxIndex + // + this.textBoxBoxIndex.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; + this.textBoxBoxIndex.Font = new System.Drawing.Font("寰蒋闆呴粦", 10.5F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + this.textBoxBoxIndex.Location = new System.Drawing.Point(110, 45); + this.textBoxBoxIndex.Name = "textBoxBoxIndex"; + this.textBoxBoxIndex.Size = new System.Drawing.Size(320, 30); + this.textBoxBoxIndex.TabIndex = 0; + // + // labelIp + // + this.labelIp.AutoSize = true; + this.labelIp.Font = new System.Drawing.Font("寰蒋闆呴粦", 10.5F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + this.labelIp.Location = new System.Drawing.Point(18, 88); + this.labelIp.Name = "labelIp"; + this.labelIp.Size = new System.Drawing.Size(85, 24); + this.labelIp.TabIndex = 3; + this.labelIp.Text = "IP鍦板潃锛"; + // + // textBoxIp + // + this.textBoxIp.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; + this.textBoxIp.Font = new System.Drawing.Font("寰蒋闆呴粦", 10.5F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + this.textBoxIp.Location = new System.Drawing.Point(110, 85); + this.textBoxIp.Name = "textBoxIp"; + this.textBoxIp.Size = new System.Drawing.Size(320, 30); + this.textBoxIp.TabIndex = 2; + this.textBoxIp.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(255)))), ((int)(((byte)(255)))), ((int)(((byte)(255))))); + // + // labelPort + // + this.labelPort.AutoSize = true; + this.labelPort.Font = new System.Drawing.Font("寰蒋闆呴粦", 10.5F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + this.labelPort.Location = new System.Drawing.Point(28, 128); + this.labelPort.Name = "labelPort"; + this.labelPort.Size = new System.Drawing.Size(65, 24); + this.labelPort.TabIndex = 5; + this.labelPort.Text = "绔彛锛"; + // + // textBoxPort + // + this.textBoxPort.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; + this.textBoxPort.Font = new System.Drawing.Font("寰蒋闆呴粦", 10.5F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + this.textBoxPort.Location = new System.Drawing.Point(110, 125); + this.textBoxPort.Name = "textBoxPort"; + this.textBoxPort.Size = new System.Drawing.Size(320, 30); + this.textBoxPort.TabIndex = 4; + // + // buttonListView + // + this.buttonListView.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left))); + this.buttonListView.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; + this.buttonListView.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] { + this.columnHeaderButtonIndex, + this.columnHeaderTriggerState, + this.columnHeaderTriggerDelay, + this.columnHeaderTriggerMission, + this.columnHeaderTriggerMethod, + this.columnHeaderTriggerMethodParams}); + this.buttonListView.Font = new System.Drawing.Font("寰蒋闆呴粦", 10.5F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + this.buttonListView.FullRowSelect = true; + this.buttonListView.GridLines = true; + this.buttonListView.HeaderStyle = System.Windows.Forms.ColumnHeaderStyle.Nonclickable; + this.buttonListView.HideSelection = false; + this.buttonListView.Location = new System.Drawing.Point(483, 55); + this.buttonListView.MultiSelect = false; + this.buttonListView.Name = "buttonListView"; + this.buttonListView.OwnerDraw = true; + this.buttonListView.Size = new System.Drawing.Size(700, 290); + this.buttonListView.TabIndex = 2; + this.buttonListView.UseCompatibleStateImageBehavior = false; + this.buttonListView.View = System.Windows.Forms.View.Details; + this.buttonListView.SelectedIndexChanged += new System.EventHandler(this.buttonListView_SelectedIndexChanged); + // + // columnHeaderButtonIndex + // + this.columnHeaderButtonIndex.Text = "缂栫爜"; + this.columnHeaderButtonIndex.Width = 70; + // + // columnHeaderTriggerState + // + this.columnHeaderTriggerState.Text = "瑙﹀彂鐘舵"; + this.columnHeaderTriggerState.Width = 100; + // + // columnHeaderTriggerDelay + // + this.columnHeaderTriggerDelay.Text = "瑙﹀彂寤惰繜"; + this.columnHeaderTriggerDelay.Width = 90; + // + // columnHeaderTriggerMission + // + this.columnHeaderTriggerMission.Text = "瑙﹀彂浠诲姟"; + this.columnHeaderTriggerMission.Width = 140; + // + // columnHeaderTriggerMethod + // + this.columnHeaderTriggerMethod.Text = "瑙﹀彂鏂规硶"; + this.columnHeaderTriggerMethod.Width = 140; + // + // columnHeaderTriggerMethodParams + // + this.columnHeaderTriggerMethodParams.Text = "鏂规硶鍙傛暟"; + this.columnHeaderTriggerMethodParams.Width = 160; + // + // groupBoxButton + // + this.groupBoxButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); + this.groupBoxButton.Controls.Add(this.btnSaveButton); + this.groupBoxButton.Controls.Add(this.btnDeleteButton); + this.groupBoxButton.Controls.Add(this.btnAddButton); + this.groupBoxButton.Controls.Add(this.labelTriggerMethodParams); + this.groupBoxButton.Controls.Add(this.textBoxTriggerMethodParams); + this.groupBoxButton.Controls.Add(this.labelTriggerMethod); + this.groupBoxButton.Controls.Add(this.textBoxTriggerMethod); + this.groupBoxButton.Controls.Add(this.labelTriggerMission); + this.groupBoxButton.Controls.Add(this.textBoxTriggerMission); + this.groupBoxButton.Controls.Add(this.labelButtonIndex); + this.groupBoxButton.Controls.Add(this.textBoxButtonIndex); + this.groupBoxButton.Controls.Add(this.labelTriggerState); + this.groupBoxButton.Controls.Add(this.comboBoxTriggerState); + this.groupBoxButton.Controls.Add(this.labelTriggerDelay); + this.groupBoxButton.Controls.Add(this.textBoxTriggerDelay); + this.groupBoxButton.Font = new System.Drawing.Font("寰蒋闆呴粦", 10.5F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + this.groupBoxButton.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(68)))), ((int)(((byte)(68)))), ((int)(((byte)(68))))); + this.groupBoxButton.Location = new System.Drawing.Point(483, 360); + this.groupBoxButton.Name = "groupBoxButton"; + this.groupBoxButton.Padding = new System.Windows.Forms.Padding(12, 10, 12, 12); + this.groupBoxButton.Size = new System.Drawing.Size(700, 250); + this.groupBoxButton.TabIndex = 3; + this.groupBoxButton.TabStop = false; + this.groupBoxButton.Text = "鎸夐挳淇℃伅"; + // + // btnSaveButton + // + this.btnSaveButton.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(0)))), ((int)(((byte)(122)))), ((int)(((byte)(204))))); + this.btnSaveButton.FlatAppearance.BorderSize = 0; + this.btnSaveButton.FlatAppearance.MouseDownBackColor = System.Drawing.Color.FromArgb(((int)(((byte)(0)))), ((int)(((byte)(92)))), ((int)(((byte)(153))))); + this.btnSaveButton.FlatAppearance.MouseOverBackColor = System.Drawing.Color.FromArgb(((int)(((byte)(0)))), ((int)(((byte)(102)))), ((int)(((byte)(170))))); + this.btnSaveButton.FlatStyle = System.Windows.Forms.FlatStyle.Flat; + this.btnSaveButton.Font = new System.Drawing.Font("寰蒋闆呴粦", 10.5F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + this.btnSaveButton.ForeColor = System.Drawing.Color.White; + this.btnSaveButton.Location = new System.Drawing.Point(580, 180); + this.btnSaveButton.Name = "btnSaveButton"; + this.btnSaveButton.Size = new System.Drawing.Size(100, 38); + this.btnSaveButton.TabIndex = 13; + this.btnSaveButton.Text = "淇濆瓨"; + this.btnSaveButton.UseVisualStyleBackColor = false; + this.btnSaveButton.Click += new System.EventHandler(this.btnSaveButton_Click); + // + // btnDeleteButton + // + this.btnDeleteButton.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(220)))), ((int)(((byte)(53)))), ((int)(((byte)(69))))); + this.btnDeleteButton.FlatAppearance.BorderSize = 0; + this.btnDeleteButton.FlatAppearance.MouseDownBackColor = System.Drawing.Color.FromArgb(((int)(((byte)(165)))), ((int)(((byte)(40)))), ((int)(((byte)(52))))); + this.btnDeleteButton.FlatAppearance.MouseOverBackColor = System.Drawing.Color.FromArgb(((int)(((byte)(187)))), ((int)(((byte)(45)))), ((int)(((byte)(59))))); + this.btnDeleteButton.FlatStyle = System.Windows.Forms.FlatStyle.Flat; + this.btnDeleteButton.Font = new System.Drawing.Font("寰蒋闆呴粦", 10.5F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + this.btnDeleteButton.ForeColor = System.Drawing.Color.White; + this.btnDeleteButton.Location = new System.Drawing.Point(470, 180); + this.btnDeleteButton.Name = "btnDeleteButton"; + this.btnDeleteButton.Size = new System.Drawing.Size(100, 38); + this.btnDeleteButton.TabIndex = 12; + this.btnDeleteButton.Text = "鍒犻櫎"; + this.btnDeleteButton.UseVisualStyleBackColor = false; + this.btnDeleteButton.Click += new System.EventHandler(this.btnDeleteButton_Click); + // + // btnAddButton + // + this.btnAddButton.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(40)))), ((int)(((byte)(167)))), ((int)(((byte)(69))))); + this.btnAddButton.FlatAppearance.BorderSize = 0; + this.btnAddButton.FlatAppearance.MouseDownBackColor = System.Drawing.Color.FromArgb(((int)(((byte)(30)))), ((int)(((byte)(125)))), ((int)(((byte)(52))))); + this.btnAddButton.FlatAppearance.MouseOverBackColor = System.Drawing.Color.FromArgb(((int)(((byte)(33)))), ((int)(((byte)(136)))), ((int)(((byte)(56))))); + this.btnAddButton.FlatStyle = System.Windows.Forms.FlatStyle.Flat; + this.btnAddButton.Font = new System.Drawing.Font("寰蒋闆呴粦", 10.5F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + this.btnAddButton.ForeColor = System.Drawing.Color.White; + this.btnAddButton.Location = new System.Drawing.Point(360, 180); + this.btnAddButton.Name = "btnAddButton"; + this.btnAddButton.Size = new System.Drawing.Size(100, 38); + this.btnAddButton.TabIndex = 11; + this.btnAddButton.Text = "娣诲姞"; + this.btnAddButton.UseVisualStyleBackColor = false; + this.btnAddButton.Click += new System.EventHandler(this.btnAddButton_Click); + // + // labelTriggerMethodParams + // + this.labelTriggerMethodParams.AutoSize = true; + this.labelTriggerMethodParams.Font = new System.Drawing.Font("寰蒋闆呴粦", 10.5F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + this.labelTriggerMethodParams.Location = new System.Drawing.Point(370, 128); + this.labelTriggerMethodParams.Name = "labelTriggerMethodParams"; + this.labelTriggerMethodParams.Size = new System.Drawing.Size(103, 24); + this.labelTriggerMethodParams.TabIndex = 11; + this.labelTriggerMethodParams.Text = "鏂规硶鍙傛暟锛"; + // + // textBoxTriggerMethodParams + // + this.textBoxTriggerMethodParams.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; + this.textBoxTriggerMethodParams.Font = new System.Drawing.Font("寰蒋闆呴粦", 10.5F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + this.textBoxTriggerMethodParams.Location = new System.Drawing.Point(490, 125); + this.textBoxTriggerMethodParams.Name = "textBoxTriggerMethodParams"; + this.textBoxTriggerMethodParams.Size = new System.Drawing.Size(190, 30); + this.textBoxTriggerMethodParams.TabIndex = 10; + // + // labelTriggerState + // + this.labelTriggerState.AutoSize = true; + this.labelTriggerState.Font = new System.Drawing.Font("寰蒋闆呴粦", 10.5F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + this.labelTriggerState.Location = new System.Drawing.Point(370, 48); + this.labelTriggerState.Name = "labelTriggerState"; + this.labelTriggerState.Size = new System.Drawing.Size(103, 24); + this.labelTriggerState.TabIndex = 3; + this.labelTriggerState.Text = "瑙﹀彂鐘舵侊細"; + // + // comboBoxTriggerState + // + this.comboBoxTriggerState.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.comboBoxTriggerState.Font = new System.Drawing.Font("寰蒋闆呴粦", 10.5F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + this.comboBoxTriggerState.FormattingEnabled = true; + this.comboBoxTriggerState.Location = new System.Drawing.Point(490, 45); + this.comboBoxTriggerState.Name = "comboBoxTriggerState"; + this.comboBoxTriggerState.Size = new System.Drawing.Size(190, 32); + this.comboBoxTriggerState.TabIndex = 2; + // + // labelTriggerDelay + // + this.labelTriggerDelay.AutoSize = true; + this.labelTriggerDelay.Font = new System.Drawing.Font("寰蒋闆呴粦", 10.5F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + this.labelTriggerDelay.Location = new System.Drawing.Point(28, 88); + this.labelTriggerDelay.Name = "labelTriggerDelay"; + this.labelTriggerDelay.Size = new System.Drawing.Size(103, 24); + this.labelTriggerDelay.TabIndex = 5; + this.labelTriggerDelay.Text = "瑙﹀彂寤惰繜锛"; + // + // textBoxTriggerDelay + // + this.textBoxTriggerDelay.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; + this.textBoxTriggerDelay.Font = new System.Drawing.Font("寰蒋闆呴粦", 10.5F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + this.textBoxTriggerDelay.Location = new System.Drawing.Point(150, 85); + this.textBoxTriggerDelay.Name = "textBoxTriggerDelay"; + this.textBoxTriggerDelay.Size = new System.Drawing.Size(200, 30); + this.textBoxTriggerDelay.TabIndex = 4; + // + // labelTriggerMethod + // + this.labelTriggerMethod.AutoSize = true; + this.labelTriggerMethod.Font = new System.Drawing.Font("寰蒋闆呴粦", 10.5F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + this.labelTriggerMethod.Location = new System.Drawing.Point(28, 128); + this.labelTriggerMethod.Name = "labelTriggerMethod"; + this.labelTriggerMethod.Size = new System.Drawing.Size(103, 24); + this.labelTriggerMethod.TabIndex = 9; + this.labelTriggerMethod.Text = "瑙﹀彂鏂规硶锛"; + // + // textBoxTriggerMethod + // + this.textBoxTriggerMethod.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; + this.textBoxTriggerMethod.Font = new System.Drawing.Font("寰蒋闆呴粦", 10.5F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + this.textBoxTriggerMethod.Location = new System.Drawing.Point(150, 125); + this.textBoxTriggerMethod.Name = "textBoxTriggerMethod"; + this.textBoxTriggerMethod.Size = new System.Drawing.Size(200, 30); + this.textBoxTriggerMethod.TabIndex = 8; + // + // labelTriggerMission + // + this.labelTriggerMission.AutoSize = true; + this.labelTriggerMission.Font = new System.Drawing.Font("寰蒋闆呴粦", 10.5F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + this.labelTriggerMission.Location = new System.Drawing.Point(370, 88); + this.labelTriggerMission.Name = "labelTriggerMission"; + this.labelTriggerMission.Size = new System.Drawing.Size(103, 24); + this.labelTriggerMission.TabIndex = 7; + this.labelTriggerMission.Text = "瑙﹀彂浠诲姟锛"; + // + // textBoxTriggerMission + // + this.textBoxTriggerMission.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; + this.textBoxTriggerMission.Font = new System.Drawing.Font("寰蒋闆呴粦", 10.5F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + this.textBoxTriggerMission.Location = new System.Drawing.Point(490, 85); + this.textBoxTriggerMission.Name = "textBoxTriggerMission"; + this.textBoxTriggerMission.Size = new System.Drawing.Size(190, 30); + this.textBoxTriggerMission.TabIndex = 6; + // + // labelButtonIndex + // + this.labelButtonIndex.AutoSize = true; + this.labelButtonIndex.Font = new System.Drawing.Font("寰蒋闆呴粦", 10.5F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + this.labelButtonIndex.Location = new System.Drawing.Point(28, 48); + this.labelButtonIndex.Name = "labelButtonIndex"; + this.labelButtonIndex.Size = new System.Drawing.Size(65, 24); + this.labelButtonIndex.TabIndex = 1; + this.labelButtonIndex.Text = "缂栫爜锛"; + // + // textBoxButtonIndex + // + this.textBoxButtonIndex.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; + this.textBoxButtonIndex.Font = new System.Drawing.Font("寰蒋闆呴粦", 10.5F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + this.textBoxButtonIndex.Location = new System.Drawing.Point(150, 45); + this.textBoxButtonIndex.Name = "textBoxButtonIndex"; + this.textBoxButtonIndex.Size = new System.Drawing.Size(200, 30); + this.textBoxButtonIndex.TabIndex = 0; + // + // labelTitle + // + this.labelTitle.AutoSize = true; + this.labelTitle.Font = new System.Drawing.Font("寰蒋闆呴粦", 16F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + this.labelTitle.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(51)))), ((int)(((byte)(51)))), ((int)(((byte)(51))))); + this.labelTitle.Location = new System.Drawing.Point(15, 12); + this.labelTitle.Name = "labelTitle"; + this.labelTitle.Size = new System.Drawing.Size(150, 42); + this.labelTitle.TabIndex = 4; + this.labelTitle.Text = "鎸夐挳鐩掔鐞"; + // + // ButtonBoxManager + // + this.AutoScaleDimensions = new System.Drawing.SizeF(9F, 18F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(245)))), ((int)(((byte)(245)))), ((int)(((byte)(247))))); + this.ClientSize = new System.Drawing.Size(1200, 620); + this.Controls.Add(this.labelTitle); + this.Controls.Add(this.groupBoxButton); + this.Controls.Add(this.buttonListView); + this.Controls.Add(this.groupBoxButtonBox); + this.Controls.Add(this.buttonBoxListView); + this.Font = new System.Drawing.Font("寰蒋闆呴粦", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + this.MinimumSize = new System.Drawing.Size(1200, 620); + this.Name = "ButtonBoxManager"; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen; + this.Text = "鎸夐挳鐩掔鐞"; + this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.ButtonBoxManager_FormClosing); + this.Load += new System.EventHandler(this.ButtonBoxManager_Load); + this.groupBoxButtonBox.ResumeLayout(false); + this.groupBoxButtonBox.PerformLayout(); + this.groupBoxButton.ResumeLayout(false); + this.groupBoxButton.PerformLayout(); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private System.Windows.Forms.ListView buttonBoxListView; + private System.Windows.Forms.ColumnHeader columnHeaderBoxIndex; + private System.Windows.Forms.ColumnHeader columnHeaderIp; + private System.Windows.Forms.ColumnHeader columnHeaderPort; + private System.Windows.Forms.ColumnHeader columnHeaderType; + private System.Windows.Forms.GroupBox groupBoxButtonBox; + private System.Windows.Forms.TextBox textBoxIp; + private System.Windows.Forms.Label labelIp; + private System.Windows.Forms.Label labelPort; + private System.Windows.Forms.TextBox textBoxPort; + private System.Windows.Forms.Label labelBoxIndex; + private System.Windows.Forms.TextBox textBoxBoxIndex; + private System.Windows.Forms.Label labelType; + private System.Windows.Forms.ComboBox comboBoxType; + private System.Windows.Forms.Button btnAddButtonBox; + private System.Windows.Forms.Button btnDeleteButtonBox; + private System.Windows.Forms.Button btnSaveButtonBox; + private System.Windows.Forms.ListView buttonListView; + private System.Windows.Forms.ColumnHeader columnHeaderButtonIndex; + private System.Windows.Forms.ColumnHeader columnHeaderTriggerMission; + private System.Windows.Forms.ColumnHeader columnHeaderTriggerMethod; + private System.Windows.Forms.ColumnHeader columnHeaderTriggerMethodParams; + private System.Windows.Forms.GroupBox groupBoxButton; + private System.Windows.Forms.Label labelButtonIndex; + private System.Windows.Forms.TextBox textBoxButtonIndex; + private System.Windows.Forms.Label labelTriggerMission; + private System.Windows.Forms.TextBox textBoxTriggerMission; + private System.Windows.Forms.Label labelTriggerMethod; + private System.Windows.Forms.TextBox textBoxTriggerMethod; + private System.Windows.Forms.Label labelTriggerMethodParams; + private System.Windows.Forms.TextBox textBoxTriggerMethodParams; + private System.Windows.Forms.Label labelTriggerState; + private System.Windows.Forms.ComboBox comboBoxTriggerState; + private System.Windows.Forms.Label labelTriggerDelay; + private System.Windows.Forms.TextBox textBoxTriggerDelay; + private System.Windows.Forms.ColumnHeader columnHeaderTriggerState; + private System.Windows.Forms.ColumnHeader columnHeaderTriggerDelay; + private System.Windows.Forms.Button btnAddButton; + private System.Windows.Forms.Button btnDeleteButton; + private System.Windows.Forms.Button btnSaveButton; + private System.Windows.Forms.Label labelTitle; + } +} + diff --git a/StandardScene.Core/ExtendDevice/ButtonBox/ButtonBoxManager.cs b/StandardScene.Core/ExtendDevice/ButtonBox/ButtonBoxManager.cs new file mode 100644 index 0000000..17f591a --- /dev/null +++ b/StandardScene.Core/ExtendDevice/ButtonBox/ButtonBoxManager.cs @@ -0,0 +1,1000 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Data; +using System.Drawing; +using System.IO; +using System.Linq; +using System.Net; +using System.Reflection; +using System.Text; +using System.Text.RegularExpressions; +using System.Threading.Tasks; +using System.Windows.Forms; +using StandardScene.Utils; + +namespace StandardScene.ExtendDevice.ButtonBox +{ + public partial class ButtonBoxManager : Form + { + private static ButtonBoxManager _instance = null; + private static readonly object _lock = new object(); + + private const string DataFileName = "ButtonBoxConfig.json"; + private string _dataFilePath; + + private List _buttonBoxes = new List(); + private ButtonBoxModel _currentButtonBox = null; + private ButtonModel _currentButton = null; + + /// + /// 鑾峰彇鍗曚緥瀹炰緥 + /// + public static ButtonBoxManager Instance + { + get + { + if (_instance == null || _instance.IsDisposed) + { + lock (_lock) + { + if (_instance == null || _instance.IsDisposed) + { + _instance = new ButtonBoxManager(); + } + } + } + return _instance; + } + } + + /// + /// 绉佹湁鏋勯犲嚱鏁帮紝纭繚鍗曚緥妯″紡 + /// + private ButtonBoxManager() + { + InitializeComponent(); + // 璁剧疆鏁版嵁鏂囦欢璺緞 + _dataFilePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, DataFileName); + } + + private void ButtonBoxManager_Load(object sender, EventArgs e) + { + // 璁剧疆ListView鐨勮瑙夋牱寮 + SetupListViewStyles(); + + // 璁剧疆鎸夐挳鐨勯紶鏍囨偓鍋滄晥鏋 + SetupButtonHoverEffects(); + + // 鍒濆鍖栫被鍨嬩笅鎷夋 + InitializeTypeComboBox(); + + // 鍒濆鍖栬Е鍙戠姸鎬佷笅鎷夋 + InitializeTriggerStateComboBox(); + + LoadData(); + RefreshButtonBoxList(); + } + + /// + /// 鍒濆鍖栬Е鍙戠姸鎬佷笅鎷夋 + /// + private void InitializeTriggerStateComboBox() + { + comboBoxTriggerState.Items.Clear(); + + // 娣诲姞ButtonState鏋氫妇鐨勬墍鏈夊 + foreach (ButtonState state in Enum.GetValues(typeof(ButtonState))) + { + comboBoxTriggerState.Items.Add(state.ToString()); + } + + // 濡傛灉娌℃湁閫変腑椤癸紝榛樿閫夋嫨绗竴涓 + if (comboBoxTriggerState.Items.Count > 0 && comboBoxTriggerState.SelectedIndex == -1) + { + comboBoxTriggerState.SelectedIndex = 0; + } + } + + /// + /// 鍒濆鍖栫被鍨嬩笅鎷夋 + /// + private void InitializeTypeComboBox() + { + comboBoxType.Items.Clear(); + + try + { + // 鑾峰彇褰撳墠鍛藉悕绌洪棿涓嬫墍鏈夌户鎵胯嚜BasicButtonBox鐨勭被 + // 璺ㄧ▼搴忛泦鍙戠幇锛氭寜閽洅鍏蜂綋绫诲瀷鍙兘浣嶄簬鍗槦鎻掍欢 dll锛圫tandardScene.Devices.ButtonBox锛夛紝 + // 鐢ㄥ唴鏍稿悓娆惧叏鍩熺被鍨嬪彂鐜版浛浠d粎鎵綋鍓嶇▼搴忛泦鐨 GetExecutingAssembly銆 + var buttonBoxTypes = SimpleLite.Utils.UiTypeDiscovery.AllTypes() + .Where(t => t.IsClass + && !t.IsAbstract + && t.Namespace == typeof(BasicButtonBox).Namespace + && t.IsSubclassOf(typeof(BasicButtonBox))) + .OrderBy(t => t.Name) + .ToList(); + + foreach (var type in buttonBoxTypes) + { + comboBoxType.Items.Add(type.Name); + } + + // 濡傛灉娌℃湁鎵惧埌浠讳綍绫诲瀷锛屾坊鍔犻粯璁ら夐」 + if (comboBoxType.Items.Count == 0) + { + comboBoxType.Items.Add("BasicButtonBox"); + } + } + catch (Exception ex) + { + MessageBox.Show($"Failed to init type dropdown: {ex.Message}", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); + comboBoxType.Items.Add("BasicButtonBox"); + } + } + + private int _buttonBoxHoverIndex = -1; + private int _buttonHoverIndex = -1; + + /// + /// 璁剧疆ListView鐨勮瑙夋牱寮 + /// + private void SetupListViewStyles() + { + SetupListView(buttonBoxListView, + ButtonBoxListView_DrawItem, + ButtonBoxListView_DrawSubItem, + ButtonBoxListView_DrawColumnHeader, + ButtonBoxListView_MouseMove, + ButtonBoxListView_MouseLeave); + + SetupListView(buttonListView, + ButtonListView_DrawItem, + ButtonListView_DrawSubItem, + ButtonListView_DrawColumnHeader, + ButtonListView_MouseMove, + ButtonListView_MouseLeave); + } + + private void SetupListView(ListView listView, + DrawListViewItemEventHandler itemHandler, + DrawListViewSubItemEventHandler subItemHandler, + DrawListViewColumnHeaderEventHandler headerHandler, + MouseEventHandler mouseMoveHandler, + EventHandler mouseLeaveHandler) + { + listView.OwnerDraw = true; + listView.BackColor = Color.White; + listView.DrawItem += itemHandler; + listView.DrawSubItem += subItemHandler; + listView.DrawColumnHeader += headerHandler; + listView.MouseMove += mouseMoveHandler; + listView.MouseLeave += mouseLeaveHandler; + + // 鍚敤鍙岀紦鍐诧紝闃叉閲嶇粯鏃剁殑鐏拌壊瑕嗙洊 + typeof(Control)?.GetProperty("DoubleBuffered", BindingFlags.Instance | BindingFlags.NonPublic)? + .SetValue(listView, true, null); + } + + private static readonly Color RowEvenColor = Color.FromArgb(250, 250, 252); + private static readonly Color RowOddColor = Color.White; + private static readonly Color RowHighlightColor = Color.FromArgb(230, 240, 255); + private static readonly Color TextRegularColor = Color.FromArgb(68, 68, 68); + private static readonly Color TextHighlightColor = Color.FromArgb(51, 51, 51); + + private void ButtonBoxListView_MouseMove(object sender, MouseEventArgs e) + { + UpdateHoverIndex(buttonBoxListView, e, true); + } + + private void ButtonBoxListView_MouseLeave(object sender, EventArgs e) + { + ResetHoverIndex(buttonBoxListView, true); + } + + private void ButtonListView_MouseMove(object sender, MouseEventArgs e) + { + UpdateHoverIndex(buttonListView, e, false); + } + + private void ButtonListView_MouseLeave(object sender, EventArgs e) + { + ResetHoverIndex(buttonListView, false); + } + + private void UpdateHoverIndex(ListView listView, MouseEventArgs e, bool isButtonBoxList) + { + var hoveredItem = listView.GetItemAt(e.X, e.Y); + int newIndex = hoveredItem?.Index ?? -1; + + if (isButtonBoxList) + { + if (_buttonBoxHoverIndex != newIndex) + { + _buttonBoxHoverIndex = newIndex; + listView.Invalidate(); + } + } + else + { + if (_buttonHoverIndex != newIndex) + { + _buttonHoverIndex = newIndex; + listView.Invalidate(); + } + } + } + + private void ResetHoverIndex(ListView listView, bool isButtonBoxList) + { + if (isButtonBoxList) + { + if (_buttonBoxHoverIndex != -1) + { + _buttonBoxHoverIndex = -1; + listView.Invalidate(); + } + } + else + { + if (_buttonHoverIndex != -1) + { + _buttonHoverIndex = -1; + listView.Invalidate(); + } + } + } + + /// + /// 鎸夐挳鐩扡istView缁樺埗椤 + /// + private void ButtonBoxListView_DrawItem(object sender, DrawListViewItemEventArgs e) + { + var isHighlighted = e.Item.Selected + || e.ItemIndex == _buttonBoxHoverIndex + || (e.State & ListViewItemStates.Focused) != 0; + + var backColor = isHighlighted + ? RowHighlightColor + : (e.ItemIndex % 2 == 0 ? RowEvenColor : RowOddColor); + + using (var brush = new SolidBrush(backColor)) + { + e.Graphics.FillRectangle(brush, e.Bounds); + } + + var textColor = isHighlighted ? TextHighlightColor : TextRegularColor; + + TextRenderer.DrawText(e.Graphics, e.Item.Text, e.Item.Font, e.Bounds, + textColor, + TextFormatFlags.VerticalCenter | TextFormatFlags.Left | TextFormatFlags.EndEllipsis); + + e.DrawFocusRectangle(); + } + + /// + /// 鎸夐挳鐩扡istView缁樺埗瀛愰」 + /// + private void ButtonBoxListView_DrawSubItem(object sender, DrawListViewSubItemEventArgs e) + { + var isHighlighted = e.Item.Selected + || e.ItemIndex == _buttonBoxHoverIndex + || (e.ItemState & ListViewItemStates.Focused) != 0; + + var backColor = isHighlighted + ? RowHighlightColor + : (e.ItemIndex % 2 == 0 ? RowEvenColor : RowOddColor); + + using (var brush = new SolidBrush(backColor)) + { + e.Graphics.FillRectangle(brush, e.Bounds); + } + + var textColor = isHighlighted ? TextHighlightColor : TextRegularColor; + + TextRenderer.DrawText(e.Graphics, e.SubItem.Text, e.SubItem.Font, e.Bounds, + textColor, + TextFormatFlags.VerticalCenter | TextFormatFlags.Left | TextFormatFlags.EndEllipsis); + } + + /// + /// 鎸夐挳鐩扡istView缁樺埗鍒楁爣棰 + /// + private void ButtonBoxListView_DrawColumnHeader(object sender, DrawListViewColumnHeaderEventArgs e) + { + // 缁樺埗鍒楁爣棰樿儗鏅 + e.Graphics.FillRectangle(new SolidBrush(Color.FromArgb(245, 247, 250)), e.Bounds); + + // 缁樺埗杈规 + e.Graphics.DrawLine(new Pen(Color.FromArgb(220, 220, 220)), + e.Bounds.Left, e.Bounds.Bottom - 1, e.Bounds.Right, e.Bounds.Bottom - 1); + + // 缁樺埗鏂囨湰 + TextRenderer.DrawText(e.Graphics, e.Header.Text, + new Font("寰蒋闆呴粦", 10.5F, FontStyle.Bold), + e.Bounds, Color.FromArgb(68, 68, 68), + TextFormatFlags.VerticalCenter | TextFormatFlags.Left | TextFormatFlags.HorizontalCenter); + } + + /// + /// 鎸夐挳ListView缁樺埗椤 + /// + private void ButtonListView_DrawItem(object sender, DrawListViewItemEventArgs e) + { + var isHighlighted = e.Item.Selected + || e.ItemIndex == _buttonHoverIndex + || (e.State & ListViewItemStates.Focused) != 0; + + var backColor = isHighlighted + ? RowHighlightColor + : (e.ItemIndex % 2 == 0 ? RowEvenColor : RowOddColor); + + using (var brush = new SolidBrush(backColor)) + { + e.Graphics.FillRectangle(brush, e.Bounds); + } + + var textColor = isHighlighted ? TextHighlightColor : TextRegularColor; + + TextRenderer.DrawText(e.Graphics, e.Item.Text, e.Item.Font, e.Bounds, + textColor, + TextFormatFlags.VerticalCenter | TextFormatFlags.Left | TextFormatFlags.EndEllipsis); + + e.DrawFocusRectangle(); + } + + /// + /// 鎸夐挳ListView缁樺埗瀛愰」 + /// + private void ButtonListView_DrawSubItem(object sender, DrawListViewSubItemEventArgs e) + { + var isHighlighted = e.Item.Selected + || e.ItemIndex == _buttonHoverIndex + || (e.ItemState & ListViewItemStates.Focused) != 0; + + var backColor = isHighlighted + ? RowHighlightColor + : (e.ItemIndex % 2 == 0 ? RowEvenColor : RowOddColor); + + using (var brush = new SolidBrush(backColor)) + { + e.Graphics.FillRectangle(brush, e.Bounds); + } + + var textColor = isHighlighted ? TextHighlightColor : TextRegularColor; + + TextRenderer.DrawText(e.Graphics, e.SubItem.Text, e.SubItem.Font, e.Bounds, + textColor, + TextFormatFlags.VerticalCenter | TextFormatFlags.Left | TextFormatFlags.EndEllipsis); + } + + /// + /// 鎸夐挳ListView缁樺埗鍒楁爣棰 + /// + private void ButtonListView_DrawColumnHeader(object sender, DrawListViewColumnHeaderEventArgs e) + { + // 缁樺埗鍒楁爣棰樿儗鏅 + e.Graphics.FillRectangle(new SolidBrush(Color.FromArgb(245, 247, 250)), e.Bounds); + + // 缁樺埗杈规 + e.Graphics.DrawLine(new Pen(Color.FromArgb(220, 220, 220)), + e.Bounds.Left, e.Bounds.Bottom - 1, e.Bounds.Right, e.Bounds.Bottom - 1); + + // 缁樺埗鏂囨湰 + TextRenderer.DrawText(e.Graphics, e.Header.Text, + new Font("寰蒋闆呴粦", 10.5F, FontStyle.Bold), + e.Bounds, Color.FromArgb(68, 68, 68), + TextFormatFlags.VerticalCenter | TextFormatFlags.Left | TextFormatFlags.HorizontalCenter); + } + + /// + /// 璁剧疆鎸夐挳鐨勯紶鏍囨偓鍋滄晥鏋 + /// + private void SetupButtonHoverEffects() + { + // 鎸夐挳鐨勬偓鍋滄晥鏋滅幇鍦ㄩ氳繃FlatAppearance灞炴у湪Designer涓缃 + // 杩欓噷鍙互娣诲姞鍏朵粬棰濆鐨勬晥鏋滐紝濡傚伐鍏锋彁绀虹瓑 + } + + /// + /// 鍒锋柊鎸夐挳鐩掑垪琛 + /// + private void RefreshButtonBoxList() + { + buttonBoxListView.Items.Clear(); + foreach (var box in _buttonBoxes) + { + var item = new ListViewItem(box.Index.ToString()); + item.SubItems.Add(box.Ip); + item.SubItems.Add(box.Port.ToString()); + item.SubItems.Add(box.Type); + item.Tag = box; + item.UseItemStyleForSubItems = false; + buttonBoxListView.Items.Add(item); + } + } + + /// + /// 鍒锋柊鎸夐挳鍒楄〃 + /// + private void RefreshButtonList() + { + buttonListView.Items.Clear(); + if (_currentButtonBox != null) + { + foreach (var button in _currentButtonBox.Buttons) + { + var item = new ListViewItem(button.Index.ToString()); + item.SubItems.Add(button.TriggerState); + item.SubItems.Add(button.TriggerDelay.ToString()); + item.SubItems.Add(button.TriggerMission); + item.SubItems.Add(button.TriggerMethod); + item.SubItems.Add(button.TriggerMethodParams); + item.Tag = button; + item.UseItemStyleForSubItems = false; + buttonListView.Items.Add(item); + } + } + } + + /// + /// 鎸夐挳鐩掑垪琛ㄩ夋嫨鏀瑰彉 + /// + private void buttonBoxListView_SelectedIndexChanged(object sender, EventArgs e) + { + if (buttonBoxListView.SelectedItems.Count > 0) + { + _currentButtonBox = buttonBoxListView.SelectedItems[0].Tag as ButtonBoxModel; + if (_currentButtonBox != null) + { + // 濉厖鎸夐挳鐩掔紪杈戝尯鍩 + textBoxIp.Text = _currentButtonBox.Ip; + textBoxPort.Text = _currentButtonBox.Port.ToString(); + textBoxBoxIndex.Text = _currentButtonBox.Index.ToString(); + // 璁剧疆绫诲瀷涓嬫媺妗 + if (comboBoxType.Items.Contains(_currentButtonBox.Type)) + { + comboBoxType.SelectedItem = _currentButtonBox.Type; + } + else + { + comboBoxType.SelectedIndex = comboBoxType.Items.Count > 0 ? 0 : -1; + } + + // 鍒锋柊鎸夐挳鍒楄〃 + RefreshButtonList(); + } + } + else + { + _currentButtonBox = null; + ClearButtonBoxFields(); + buttonListView.Items.Clear(); + } + } + + /// + /// 鎸夐挳鍒楄〃閫夋嫨鏀瑰彉 + /// + private void buttonListView_SelectedIndexChanged(object sender, EventArgs e) + { + if (buttonListView.SelectedItems.Count > 0) + { + _currentButton = buttonListView.SelectedItems[0].Tag as ButtonModel; + if (_currentButton != null) + { + // 濉厖鎸夐挳缂栬緫鍖哄煙 + textBoxButtonIndex.Text = _currentButton.Index.ToString(); + textBoxTriggerMission.Text = _currentButton.TriggerMission; + textBoxTriggerMethod.Text = _currentButton.TriggerMethod; + textBoxTriggerMethodParams.Text = _currentButton.TriggerMethodParams; + + // 璁剧疆瑙﹀彂鐘舵佷笅鎷夋 + if (comboBoxTriggerState.Items.Contains(_currentButton.TriggerState)) + { + comboBoxTriggerState.SelectedItem = _currentButton.TriggerState; + } + else + { + comboBoxTriggerState.SelectedIndex = comboBoxTriggerState.Items.Count > 0 ? 0 : -1; + } + + textBoxTriggerDelay.Text = _currentButton.TriggerDelay.ToString(); + } + } + else + { + _currentButton = null; + ClearButtonFields(); + } + } + + /// + /// 娣诲姞鎸夐挳鐩 + /// + private void btnAddButtonBox_Click(object sender, EventArgs e) + { + try + { + // 鑾峰彇杈撳叆妗嗙殑鍊 + string ip = textBoxIp.Text.Trim(); + string portText = textBoxPort.Text.Trim(); + string indexText = textBoxBoxIndex.Text.Trim(); + string type = comboBoxType.SelectedItem?.ToString() ?? string.Empty; + + // 纭畾瑕佷娇鐢ㄧ殑鍊硷細濡傛灉杈撳叆妗嗕笉涓虹┖鍒欎娇鐢ㄨ緭鍏ュ硷紝鍚﹀垯浣跨敤榛樿鍊 + int newIndex; + if (!string.IsNullOrWhiteSpace(indexText)) + { + if (!int.TryParse(indexText, out newIndex)) + { + MessageBox.Show("Index must be numeric", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); + return; + } + } + else + { + newIndex = _buttonBoxes.Count > 0 ? _buttonBoxes.Max(b => b.Index) + 1 : 1; + } + + string newIp; + if (!string.IsNullOrWhiteSpace(ip)) + { + // 楠岃瘉IP鍦板潃鏍煎紡 + if (!IsValidIpAddress(ip)) + { + MessageBox.Show("Invalid IP address, e.g. 192.168.1.100", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); + return; + } + newIp = ip; + } + else + { + newIp = "192.168.1.100"; + } + + int newPort; + if (!string.IsNullOrWhiteSpace(portText)) + { + if (!int.TryParse(portText, out newPort)) + { + MessageBox.Show("Port must be numeric", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); + return; + } + } + else + { + newPort = 502; + } + + string newType; + if (!string.IsNullOrWhiteSpace(type)) + { + newType = type; + } + else + { + // 濡傛灉涓嬫媺妗嗘湁閫夐」锛屼娇鐢ㄧ涓涓夐」浣滀负榛樿鍊 + newType = comboBoxType.Items.Count > 0 ? comboBoxType.Items[0].ToString() : "BasicButtonBox"; + } + + // 妫鏌ョ紪鐮佹槸鍚﹂噸澶 + if (_buttonBoxes.Any(b => b.Index == newIndex)) + { + MessageBox.Show($"Index {newIndex} already exists, use another", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); + return; + } + + // 妫鏌P鍦板潃鏄惁閲嶅 + if (_buttonBoxes.Any(b => b.Ip == newIp)) + { + MessageBox.Show($"IP {newIp} already exists, use another", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); + return; + } + + var newBox = new ButtonBoxModel + { + Index = newIndex, + Ip = newIp, + Port = newPort, + Type = newType + }; + + _buttonBoxes.Add(newBox); + RefreshButtonBoxList(); + SaveData(); + + // 閫変腑鏂版坊鍔犵殑鎸夐挳鐩 + foreach (ListViewItem item in buttonBoxListView.Items) + { + if (item.Tag == newBox) + { + item.Selected = true; + item.EnsureVisible(); + break; + } + } + } + catch (Exception ex) + { + MessageBox.Show($"Failed to add button box: {ex.Message}", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); + } + } + + /// + /// 鍒犻櫎鎸夐挳鐩 + /// + private void btnDeleteButtonBox_Click(object sender, EventArgs e) + { + if (_currentButtonBox == null) + { + MessageBox.Show("Please select a button box to delete", "Info", MessageBoxButtons.OK, MessageBoxIcon.Information); + return; + } + +var result = MessageBox.Show($"Delete button box with index {_currentButtonBox.Index}?", "Confirm delete", + MessageBoxButtons.YesNo, MessageBoxIcon.Question); + + if (result == DialogResult.Yes) + { + _buttonBoxes.Remove(_currentButtonBox); + _currentButtonBox = null; + ClearButtonBoxFields(); + RefreshButtonBoxList(); + buttonListView.Items.Clear(); + SaveData(); + } + } + + /// + /// 淇濆瓨鎸夐挳鐩 + /// + private void btnSaveButtonBox_Click(object sender, EventArgs e) + { + if (_currentButtonBox == null) + { + MessageBox.Show("Please select a button box to save", "Info", MessageBoxButtons.OK, MessageBoxIcon.Information); + return; + } + + try + { + string newIp = textBoxIp.Text.Trim(); + + // 楠岃瘉IP鍦板潃鏍煎紡 + if (!IsValidIpAddress(newIp)) + { + MessageBox.Show("Invalid IP address, e.g. 192.168.1.100", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); + return; + } + + if (!int.TryParse(textBoxPort.Text, out int port)) + { + MessageBox.Show("Port must be numeric", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); + return; + } + _currentButtonBox.Port = port; + + if (!int.TryParse(textBoxBoxIndex.Text, out int index)) + { + MessageBox.Show("Index must be numeric", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); + return; + } + + // 妫鏌ョ紪鐮佹槸鍚﹂噸澶嶏紙鎺掗櫎褰撳墠椤癸級 + if (_buttonBoxes.Any(b => b.Index == index && b != _currentButtonBox)) + { + MessageBox.Show($"Index {index} already exists, use another", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); + return; + } + + // 妫鏌P鍦板潃鏄惁閲嶅锛堟帓闄ゅ綋鍓嶉」锛 + if (_buttonBoxes.Any(b => b.Ip == newIp && b != _currentButtonBox)) + { + MessageBox.Show($"IP {newIp} already exists, use another", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); + return; + } + + _currentButtonBox.Index = index; + _currentButtonBox.Type = comboBoxType.SelectedItem?.ToString() ?? string.Empty; + _currentButtonBox.Ip = newIp; + + RefreshButtonBoxList(); + SaveData(); + MessageBox.Show("Saved", "Info", MessageBoxButtons.OK, MessageBoxIcon.Information); + } + catch (Exception ex) + { + MessageBox.Show($"Save failed: {ex.Message}", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); + } + } + + /// + /// 娣诲姞鎸夐挳 + /// + private void btnAddButton_Click(object sender, EventArgs e) + { + if (_currentButtonBox == null) + { + MessageBox.Show("Please select a button box first", "Info", MessageBoxButtons.OK, MessageBoxIcon.Information); + return; + } + + try + { + // 鑾峰彇杈撳叆妗嗙殑鍊 + string indexText = textBoxButtonIndex.Text.Trim(); + string triggerMission = textBoxTriggerMission.Text.Trim(); + string triggerMethod = textBoxTriggerMethod.Text.Trim(); + string triggerMethodParams = textBoxTriggerMethodParams.Text.Trim(); + string triggerState = comboBoxTriggerState.SelectedItem?.ToString() ?? string.Empty; + string triggerDelayText = textBoxTriggerDelay.Text.Trim(); + + // 纭畾瑕佷娇鐢ㄧ殑鍊硷細濡傛灉杈撳叆妗嗕笉涓虹┖鍒欎娇鐢ㄨ緭鍏ュ硷紝鍚﹀垯浣跨敤榛樿鍊 + int newIndex; + if (!string.IsNullOrWhiteSpace(indexText)) + { + if (!int.TryParse(indexText, out newIndex)) + { + MessageBox.Show("Button index must be numeric", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); + return; + } + } + else + { + newIndex = _currentButtonBox.Buttons.Count > 0 + ? _currentButtonBox.Buttons.Max(b => b.Index) + 1 + : 1; + } + + // 妫鏌ユ寜閽紪鐮佹槸鍚﹂噸澶 + if (_currentButtonBox.Buttons.Any(b => b.Index == newIndex)) + { + MessageBox.Show($"Button index {newIndex} already exists, use another", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); + return; + } + + // 瑙f瀽瑙﹀彂寤惰繜锛堝繀椤绘槸ushort绫诲瀷锛岃寖鍥0-65535锛 + ushort triggerDelay = 0; + if (!string.IsNullOrWhiteSpace(triggerDelayText)) + { + if (!ushort.TryParse(triggerDelayText, out triggerDelay)) + { + MessageBox.Show("Trigger delay must be 0-65535", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); + return; + } + } + + var newButton = new ButtonModel + { + Index = newIndex, + TriggerMission = triggerMission, + TriggerMethod = triggerMethod, + TriggerMethodParams = triggerMethodParams, + TriggerState = triggerState, + TriggerDelay = triggerDelay + }; + + _currentButtonBox.Buttons.Add(newButton); + RefreshButtonList(); + SaveData(); + + // 閫変腑鏂版坊鍔犵殑鎸夐挳 + foreach (ListViewItem item in buttonListView.Items) + { + if (item.Tag == newButton) + { + item.Selected = true; + item.EnsureVisible(); + break; + } + } + } + catch (Exception ex) + { + MessageBox.Show($"Failed to add button: {ex.Message}", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); + } + } + + /// + /// 鍒犻櫎鎸夐挳 + /// + private void btnDeleteButton_Click(object sender, EventArgs e) + { + if (_currentButtonBox == null) + { + MessageBox.Show("Please select a button box first", "Info", MessageBoxButtons.OK, MessageBoxIcon.Information); + return; + } + + if (_currentButton == null) + { + MessageBox.Show("Please select a button to delete", "Info", MessageBoxButtons.OK, MessageBoxIcon.Information); + return; + } + +var result = MessageBox.Show($"Delete button with index {_currentButton.Index}?", "Confirm delete", + MessageBoxButtons.YesNo, MessageBoxIcon.Question); + + if (result == DialogResult.Yes) + { + _currentButtonBox.Buttons.Remove(_currentButton); + _currentButton = null; + ClearButtonFields(); + RefreshButtonList(); + SaveData(); + } + } + + /// + /// 淇濆瓨鎸夐挳 + /// + private void btnSaveButton_Click(object sender, EventArgs e) + { + if (_currentButtonBox == null) + { + MessageBox.Show("Please select a button box first", "Info", MessageBoxButtons.OK, MessageBoxIcon.Information); + return; + } + + if (_currentButton == null) + { + MessageBox.Show("Please select a button to save", "Info", MessageBoxButtons.OK, MessageBoxIcon.Information); + return; + } + + try + { + if (!int.TryParse(textBoxButtonIndex.Text, out int index)) + { + MessageBox.Show("Button index must be numeric", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); + return; + } + + // 妫鏌ユ寜閽紪鐮佹槸鍚﹂噸澶嶏紙鎺掗櫎褰撳墠鎸夐挳锛 + if (_currentButtonBox.Buttons.Any(b => b.Index == index && b != _currentButton)) + { + MessageBox.Show($"Button index {index} already exists, use another", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); + return; + } + + // 楠岃瘉瑙﹀彂寤惰繜锛堝繀椤绘槸ushort绫诲瀷锛岃寖鍥0-65535锛 + if (!ushort.TryParse(textBoxTriggerDelay.Text, out ushort triggerDelay)) + { + MessageBox.Show("Trigger delay must be 0-65535", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); + return; + } + + _currentButton.Index = index; + _currentButton.TriggerMission = textBoxTriggerMission.Text; + _currentButton.TriggerMethod = textBoxTriggerMethod.Text; + _currentButton.TriggerMethodParams = textBoxTriggerMethodParams.Text; + _currentButton.TriggerState = comboBoxTriggerState.SelectedItem?.ToString() ?? string.Empty; + _currentButton.TriggerDelay = triggerDelay; + + RefreshButtonList(); + SaveData(); + MessageBox.Show("Saved", "Info", MessageBoxButtons.OK, MessageBoxIcon.Information); + } + catch (Exception ex) + { + MessageBox.Show($"Save failed: {ex.Message}", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); + } + } + + /// + /// 娓呯┖鎸夐挳鐩掑瓧娈 + /// + private void ClearButtonBoxFields() + { + textBoxIp.Text = string.Empty; + textBoxPort.Text = string.Empty; + textBoxBoxIndex.Text = string.Empty; + comboBoxType.SelectedIndex = -1; + } + + /// + /// 娓呯┖鎸夐挳瀛楁 + /// + private void ClearButtonFields() + { + textBoxButtonIndex.Text = string.Empty; + textBoxTriggerMission.Text = string.Empty; + textBoxTriggerMethod.Text = string.Empty; + textBoxTriggerMethodParams.Text = string.Empty; + comboBoxTriggerState.SelectedIndex = comboBoxTriggerState.Items.Count > 0 ? 0 : -1; + textBoxTriggerDelay.Text = string.Empty; + } + + /// + /// 鍔犺浇鏁版嵁 + /// + private void LoadData() + { + try + { + if (File.Exists(_dataFilePath)) + { + var jsonContent = File.ReadAllText(_dataFilePath, Encoding.UTF8); + if (!string.IsNullOrWhiteSpace(jsonContent)) + { + _buttonBoxes = jsonContent.JsonTo>(); + if (_buttonBoxes == null) + { + _buttonBoxes = new List(); + } + } + else + { + _buttonBoxes = new List(); + } + } + else + { + _buttonBoxes = new List(); + } + } + catch (Exception ex) + { + MessageBox.Show($"Load data failed: {ex.Message}", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); + _buttonBoxes = new List(); + } + } + + /// + /// 淇濆瓨鏁版嵁 + /// + private void SaveData() + { + try + { + var jsonContent = _buttonBoxes.ToJson(); + File.WriteAllText(_dataFilePath, jsonContent, Encoding.UTF8); + } + catch (Exception ex) + { + MessageBox.Show($"Save data failed: {ex.Message}", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); + } + } + + /// + /// 楠岃瘉IP鍦板潃鏍煎紡 + /// + /// IP鍦板潃瀛楃涓 + /// 濡傛灉鏍煎紡姝g‘杩斿洖true锛屽惁鍒欒繑鍥瀎alse + private bool IsValidIpAddress(string ipAddress) + { + if (string.IsNullOrWhiteSpace(ipAddress)) + { + return false; + } + + // 浣跨敤姝e垯琛ㄨ揪寮忛獙璇両P鍦板潃鏍煎紡锛圛Pv4锛 + string pattern = @"^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$"; + if (Regex.IsMatch(ipAddress, pattern)) + { + // 浣跨敤System.Net.IPAddress.TryParse杩涜浜屾楠岃瘉 + IPAddress address; + return IPAddress.TryParse(ipAddress, out address) && address.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork; + } + + return false; + } + + /// + /// 绐椾綋鍏抽棴浜嬩欢 + /// + private void ButtonBoxManager_FormClosing(object sender, FormClosingEventArgs e) + { + if (e.CloseReason == CloseReason.UserClosing) + { + // 鍏抽棴鍓嶄繚瀛樻暟鎹 + SaveData(); + e.Cancel = true; + this.Visible = false; + } + } + } +} + diff --git a/StandardScene.Core/ExtendDevice/ButtonBox/ButtonBoxManager.resx b/StandardScene.Core/ExtendDevice/ButtonBox/ButtonBoxManager.resx new file mode 100644 index 0000000..44e9f97 --- /dev/null +++ b/StandardScene.Core/ExtendDevice/ButtonBox/ButtonBoxManager.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + diff --git a/StandardScene.Core/ExtendDevice/ButtonBox/ButtonBoxModel.cs b/StandardScene.Core/ExtendDevice/ButtonBox/ButtonBoxModel.cs new file mode 100644 index 0000000..c14890c --- /dev/null +++ b/StandardScene.Core/ExtendDevice/ButtonBox/ButtonBoxModel.cs @@ -0,0 +1,34 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace StandardScene.ExtendDevice.ButtonBox +{ + /// + /// 鎸夐挳鐩掓ā鍨 + /// + public class ButtonBoxModel + { + public string Ip { get; set; } = string.Empty; + public int Port { get; set; } = 0; + public int Index { get; set; } = 0; + public string Type { get; set; } = string.Empty; + public List Buttons { get; set; } = new List(); + } + + /// + /// 鎸夐挳妯″瀷 + /// + public class ButtonModel + { + public int Index { get; set; } = 0; + public string TriggerMission { get; set; } = string.Empty; + public string TriggerMethod { get; set; } = string.Empty; + public string TriggerMethodParams { get; set; } = string.Empty; + public string TriggerState { get; set; } = string.Empty; + public ushort TriggerDelay { get; set; } = 0; + } +} + diff --git a/StandardScene.Core/ExtendDevice/ButtonBox/ButtonMission.cs b/StandardScene.Core/ExtendDevice/ButtonBox/ButtonMission.cs new file mode 100644 index 0000000..f6e64e7 --- /dev/null +++ b/StandardScene.Core/ExtendDevice/ButtonBox/ButtonMission.cs @@ -0,0 +1,806 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using System.Windows.Forms; +using LessokajiWeaverUtilities.MagicAttributes; +using LessokajiWeaverUtilities.Utilities; +using SimpleLite; +using SimpleLite.RCS; +using SimpleLite.RCS.CarTypes; +using SimpleCore; +using SimpleCore.Library; +using StandardScene; +using StandardScene.Utils; + +namespace StandardScene.ExtendDevice.ButtonBox +{ + [MissionType(Name = "鎸夐挳杩涚▼")] + [I18N.DocumentTranslation(Name = "ButtonMission",locale = "en")] + public class ButtonMission:Mission + { + private const string DataFileName = "ButtonBoxConfig.json"; + private string _dataFilePath; + + /// + /// 褰撳墠鎵鏈夋寜閽洅瀹炰緥鍒楄〃 + /// + private List _buttonBoxes = new List(); + + /// + /// 鐢ㄤ簬绠$悊寮傛寰幆鐨勫彇娑堜护鐗屾簮 + /// + private CancellationTokenSource _cancellationTokenSource; + + /// + /// 淇濆瓨鐩戞帶閰嶇疆涓庣姸鎬佺殑鍚庡彴浠诲姟锛屼究浜庡叧闂椂绛夊緟 + /// + private Task _configTask; + + private Task _stateTask; + + /// + /// 鍚屾閿侊紝鐢ㄤ簬淇濇姢鎸夐挳鐩掑垪琛ㄧ殑骞跺彂璁块棶 + /// + private readonly object _syncLock = new object(); + + [MethodMember(Name = "鍚姩杩涚▼")] + [I18N.DocumentTranslation(Name = "Start Mission", locale = "en")] + public override void Execute() + { + // 璁剧疆鏁版嵁鏂囦欢璺緞 + _dataFilePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, DataFileName); + + // 濡傛灉宸茬粡鍚姩锛屽厛鍋滄涔嬪墠鐨勫惊鐜 + StopInternalAsync().GetAwaiter().GetResult(); + + // 鍒涘缓鏂扮殑鍙栨秷浠ょ墝婧 + _cancellationTokenSource = new CancellationTokenSource(); + + // 鍚姩寮傛寰幆 + var token = _cancellationTokenSource.Token; + _configTask = Task.Run(async () => await MonitorButtonBoxConfigAsync(token), token); + _stateTask = Task.Run(async () => await MonitorButtonStatesAsync(token), token); + status.status = "Running"; + } + + /// + /// 寮傛鐩戞帶鎸夐挳鐩掗厤缃枃浠 + /// + private async Task MonitorButtonBoxConfigAsync(CancellationToken cancellationToken) + { + while (!cancellationToken.IsCancellationRequested) + { + try + { + // 璇诲彇閰嶇疆鏂囦欢 + var configButtonBoxes = LoadButtonBoxConfig(); + + // 鍚屾鎸夐挳鐩掑垪琛 + SyncButtonBoxes(configButtonBoxes); + + // 绛夊緟10绉 + await Task.Delay(TimeSpan.FromSeconds(10), cancellationToken); + } + catch (OperationCanceledException) + { + // 姝e父鍙栨秷锛岄鍑哄惊鐜 + break; + } + catch (Exception ex) + { + // 璁板綍閿欒锛屼絾缁х画杩愯 + Diagnosis.Log($"鎸夐挳鐩掗厤缃洃鎺ч敊璇: {ExceptionFormatter.FormatEx(ex)}", "ButtonMission", true); + // 鍙戠敓閿欒鏃剁瓑寰5绉掑悗閲嶈瘯 + try + { + await Task.Delay(TimeSpan.FromSeconds(5), cancellationToken); + } + catch (OperationCanceledException) + { + break; + } + } + } + } + + /// + /// 鍋滄鐩戞帶浠诲姟 + /// + [MethodMember(Name = "鍋滄杩涚▼")] + [I18N.DocumentTranslation(Name = "Stop Mission", locale = "en")] + public void Stop() + { + StopInternalAsync().GetAwaiter().GetResult(); + status.status = "/"; + } + + /// + /// 鍙栨秷骞堕噴鏀惧綋鍓嶇殑鍙栨秷浠ょ墝婧 + /// + private async Task StopInternalAsync() + { + var cts = Interlocked.Exchange(ref _cancellationTokenSource, null); + var configTask = Interlocked.Exchange(ref _configTask, null); + var stateTask = Interlocked.Exchange(ref _stateTask, null); + + if (cts == null && configTask == null && stateTask == null) + { + return; + } + + try + { + cts?.Cancel(); + } + catch (ObjectDisposedException) + { + // 宸查噴鏀撅紝蹇界暐 + } + + var runningTasks = new[] { configTask, stateTask } + .Where(t => t != null) + .ToArray(); + + if (runningTasks.Length > 0) + { + var aggregateTask = Task.WhenAll(runningTasks); + var timeoutTask = Task.Delay(TimeSpan.FromSeconds(5)); + var completedTask = await Task.WhenAny(aggregateTask, timeoutTask).ConfigureAwait(false); + + if (completedTask == timeoutTask) + { + Diagnosis.Log("鍋滄鎸夐挳鐩戞帶浠诲姟瓒呮椂", "ButtonMission", true); + } + else + { + try + { + await aggregateTask.ConfigureAwait(false); + } + catch (Exception ex) + { + Diagnosis.Log($"鍋滄鎸夐挳鐩戞帶浠诲姟鏃跺彂鐢熷紓甯: {ExceptionFormatter.FormatEx(ex)}", "ButtonMission", true); + } + } + } + + cts?.Dispose(); + + DisconnectAllButtonBoxes(); + } + + /// + /// 鏂紑鎵鏈夋寜閽洅杩炴帴 + /// + private void DisconnectAllButtonBoxes() + { + List snapshot; + lock (_syncLock) + { + snapshot = _buttonBoxes.ToList(); + } + + foreach (var box in snapshot) + { + try + { + box.Disconnect(); + } + catch (Exception ex) + { + Diagnosis.Log($"鍋滄鎸夐挳鐩掑け璐: Index={box.Index}, Error={ExceptionFormatter.FormatEx(ex)}", "ButtonMission", true); + } + } + } + + /// + /// 鐩戞帶鎸夐挳鐘舵侊紝鐢ㄤ簬瑙﹀彂鎸夐挳鍔ㄤ綔 + /// + private async Task MonitorButtonStatesAsync(CancellationToken cancellationToken) + { + while (!cancellationToken.IsCancellationRequested) + { + try + { + List<(BasicButtonBox Box, ButtonModel Config)> snapshot; + lock (_syncLock) + { + snapshot = _buttonBoxes + .SelectMany(box => box.ButtonConfigs.Values.Select(cfg => (Box: box, Config: cfg))) + .ToList(); + } + + foreach (var (box, config) in snapshot) + { + if (box == null || config == null) + { + continue; + } + + if (string.IsNullOrWhiteSpace(config.TriggerMission) || + string.IsNullOrWhiteSpace(config.TriggerMethod)) + { + continue; + } + var desiredState = ButtonState.Pressed; + if (!string.IsNullOrWhiteSpace(config.TriggerState) && + Enum.TryParse(config.TriggerState, out ButtonState parsedState)) + { + desiredState = parsedState; + } + + var currentState = box.GetButtonState(config.Index); + bool isActive = currentState == desiredState&&box.IsOnline; + + int delay = config.TriggerDelay; + if (delay <= 0) + { + delay = 1; + } + + int uniqueId = unchecked((box.Index << 16) ^ config.Index); + + LadderLogic.TriggerOnce(isActive, delay*1000, () => + { + ExecuteButtonAction(config, box); + }, uniqueId); + } + } + catch (OperationCanceledException) + { + break; + } + catch (Exception ex) + { + Diagnosis.Log($"鎸夐挳鐘舵佺洃鎺ч敊璇: {ExceptionFormatter.FormatEx(ex)}", "ButtonMission", true); + } + + try + { + await Task.Delay(TimeSpan.FromMilliseconds(500), cancellationToken); + } + catch (OperationCanceledException) + { + break; + } + } + } + + /// + /// 鍔犺浇鎸夐挳鐩掗厤缃枃浠 + /// + private List LoadButtonBoxConfig() + { + try + { + if (File.Exists(_dataFilePath)) + { + var jsonContent = File.ReadAllText(_dataFilePath, Encoding.UTF8); + if (!string.IsNullOrWhiteSpace(jsonContent)) + { + var buttonBoxes = jsonContent.JsonTo>(); + return buttonBoxes ?? new List(); + } + } + } + catch (Exception ex) + { + Diagnosis.Log($"鍔犺浇鎸夐挳鐩掗厤缃枃浠跺け璐: {ExceptionFormatter.FormatEx(ex)}", "ButtonMission", true); + } + + return new List(); + } + + /// + /// 鍚屾鎸夐挳鐩掑垪琛紝鏍规嵁閰嶇疆鏂囦欢杩涜澧炲垹鏀 + /// + private void SyncButtonBoxes(List configButtonBoxes) + { + var boxesToAdd = new List(); + + lock (_syncLock) + { + // 鍒涘缓閰嶇疆涓殑鎸夐挳鐩掔储寮曞瓧鍏革紝鐢ㄤ簬蹇熸煡鎵 + var configDict = configButtonBoxes.ToDictionary(b => b.Index); + + // 鍒涘缓褰撳墠鎸夐挳鐩掔储寮曞瓧鍏 + var currentDict = _buttonBoxes.ToDictionary(b => b.Index); + + // 1. 鍒犻櫎锛氬湪閰嶇疆涓笉瀛樺湪鐨勬寜閽洅 + var toRemove = _buttonBoxes.Where(b => !configDict.ContainsKey(b.Index)).ToList(); + foreach (var buttonBox in toRemove) + { + try + { + // 鏂紑杩炴帴 + buttonBox.Disconnect(); + _buttonBoxes.Remove(buttonBox); + Diagnosis.Post($"鍒犻櫎鎸夐挳鐩: Index={buttonBox.Index}, IP={buttonBox.Ip}", "ButtonMission", true); + } + catch (Exception ex) + { + Diagnosis.Log($"鍒犻櫎鎸夐挳鐩掑け璐: {ExceptionFormatter.FormatEx(ex)}", "ButtonMission", true); + } + } + + // 2. 娣诲姞鍜屼慨鏀癸細閬嶅巻閰嶇疆涓殑鎸夐挳鐩 + foreach (var configBox in configButtonBoxes) + { + if (currentDict.TryGetValue(configBox.Index, out var existingBox)) + { + // 淇敼锛氭鏌ユ槸鍚﹂渶瑕佹洿鏂 + if (ShouldUpdateButtonBox(existingBox, configBox)) + { + try + { + UpdateButtonBox(existingBox, configBox); + Diagnosis.Post($"鏇存柊鎸夐挳鐩: Index={configBox.Index}, IP={configBox.Ip}, Type={configBox.Type}", "ButtonMission", true); + } + catch (Exception ex) + { + Diagnosis.Log($"鏇存柊鎸夐挳鐩掑け璐: {ExceptionFormatter.FormatEx(ex)}", "ButtonMission", true); + } + } + } + else + { + boxesToAdd.Add(configBox); + } + } + } + + foreach (var configBox in boxesToAdd) + { + try + { + var newBox = CreateButtonBoxInstance(configBox); + if (newBox != null) + { + lock (_syncLock) + { + _buttonBoxes.Add(newBox); + } + Diagnosis.Post($"娣诲姞鎸夐挳鐩: Index={configBox.Index}, IP={configBox.Ip}, Type={configBox.Type}", "ButtonMission", true); + } + else + { + Diagnosis.Log($"鏃犳硶鍒涘缓鎸夐挳鐩掑疄渚: Index={configBox.Index}, Type={configBox.Type}", "ButtonMission", true); + } + } + catch (Exception ex) + { + Diagnosis.Log($"娣诲姞鎸夐挳鐩掑け璐: {ExceptionFormatter.FormatEx(ex)}", "ButtonMission", true); + } + } + } + + /// + /// 鍒ゆ柇鏄惁闇瑕佹洿鏂版寜閽洅 + /// + private bool ShouldUpdateButtonBox(BasicButtonBox existingBox, ButtonBoxModel configBox) + { + // 妫鏌ュ熀鏈睘鎬ф槸鍚﹀彉鏇 + if (existingBox.Ip != configBox.Ip + || existingBox.Port != configBox.Port + || existingBox.GetType().Name != configBox.Type) + { + return true; + } + + // 妫鏌ユ寜閽俊鎭槸鍚﹀彉鏇 + return HasButtonConfigsChanged(existingBox, configBox); + } + + /// + /// 妫鏌ユ寜閽厤缃俊鎭槸鍚﹀彉鏇 + /// + private bool HasButtonConfigsChanged(BasicButtonBox existingBox, ButtonBoxModel configBox) + { + var configButtons = configBox.Buttons ?? new List(); + var configDict = configButtons.ToDictionary(b => b.Index); + var existingDict = existingBox.ButtonConfigs; + + // 妫鏌ユ寜閽暟閲忔槸鍚﹀彉鍖 + if (existingDict.Count != configDict.Count) + { + return true; + } + + // 妫鏌ユ瘡涓寜閽殑閰嶇疆鏄惁鍙樺寲 + foreach (var configButton in configButtons) + { + if (!existingDict.TryGetValue(configButton.Index, out var existingButton)) + { + // 鏂板浜嗘寜閽 + return true; + } + + // 妫鏌ユ寜閽厤缃槸鍚﹀彉鍖 + if (existingButton.TriggerMission != configButton.TriggerMission + || existingButton.TriggerMethod != configButton.TriggerMethod + || existingButton.TriggerMethodParams != configButton.TriggerMethodParams + || existingButton.TriggerState != configButton.TriggerState + || existingButton.TriggerDelay != configButton.TriggerDelay) + { + return true; + } + } + + // 妫鏌ユ槸鍚︽湁鎸夐挳琚垹闄 + foreach (var existingKey in existingDict.Keys) + { + if (!configDict.ContainsKey(existingKey)) + { + return true; + } + } + + return false; + } + + /// + /// 鏇存柊鎸夐挳鐩掑睘鎬 + /// + private void UpdateButtonBox(BasicButtonBox buttonBox, ButtonBoxModel configBox) + { + // 濡傛灉绫诲瀷鏀瑰彉锛岄渶瑕侀噸鏂板垱寤哄疄渚 + if (buttonBox.GetType().Name != configBox.Type) + { + // 鏂紑鏃ц繛鎺 + buttonBox.Disconnect(); + + // 浠庡垪琛ㄤ腑绉婚櫎 + _buttonBoxes.Remove(buttonBox); + + // 鍒涘缓鏂板疄渚 + var newBox = CreateButtonBoxInstance(configBox); + if (newBox != null) + { + _buttonBoxes.Add(newBox); + } + } + else + { + // 鍙洿鏂板睘鎬 + bool needReconnect = buttonBox.Ip != configBox.Ip || buttonBox.Port != configBox.Port; + buttonBox.Ip = configBox.Ip; + buttonBox.Port = configBox.Port; + + // 鏇存柊鎸夐挳閰嶇疆淇℃伅 + buttonBox.UpdateButtonConfigs(configBox.Buttons); + + // 鍒濆鍖栨寜閽姸鎬侊紙鍩轰簬閰嶇疆涓殑鎸夐挳绱㈠紩锛 + var buttonIndices = configBox.Buttons?.Select(b => b.Index).ToList() ?? new List(); + buttonBox.InitializeButtons(buttonIndices); + + // 濡傛灉IP鎴栫鍙f敼鍙橈紝闇瑕侀噸鏂拌繛鎺 + if (needReconnect) + { + buttonBox.Disconnect(); + buttonBox.Connect(); + } + } + } + + /// + /// 閫氳繃绫诲瀷瀛楃涓插垱寤烘寜閽洅瀹炰緥 + /// + private BasicButtonBox CreateButtonBoxInstance(ButtonBoxModel configBox) + { + if (string.IsNullOrWhiteSpace(configBox.Type)) + { + return null; + } + + try + { + // 鑾峰彇褰撳墠鍛藉悕绌洪棿涓嬫墍鏈夌户鎵胯嚜BasicButtonBox鐨勭被 + // 璺ㄧ▼搴忛泦鍙戠幇锛氭寜閽洅鍏蜂綋绫诲瀷鍙兘浣嶄簬鍗槦鎻掍欢 dll锛圫tandardScene.Devices.ButtonBox锛夛紝 + // 鐢ㄥ唴鏍稿悓娆惧叏鍩熺被鍨嬪彂鐜版浛浠d粎鎵綋鍓嶇▼搴忛泦鐨 GetExecutingAssembly銆 + var buttonBoxType = SimpleLite.Utils.UiTypeDiscovery.AllTypes() + .FirstOrDefault(t => t.IsClass + && !t.IsAbstract + && t.Namespace == typeof(BasicButtonBox).Namespace + && t.IsSubclassOf(typeof(BasicButtonBox)) + && t.Name == configBox.Type); + + if (buttonBoxType == null) + { + Diagnosis.Log($"鏈壘鍒版寜閽洅绫诲瀷: {configBox.Type}", "ButtonMission", true); + return null; + } + + // 浣跨敤鍙嶅皠鍒涘缓瀹炰緥 + var instance = (BasicButtonBox)Activator.CreateInstance(buttonBoxType); + + // 璁剧疆灞炴 + instance.Index = configBox.Index; + instance.Ip = configBox.Ip; + instance.Port = configBox.Port; + + // 鍒濆鍖栨寜閽厤缃俊鎭 + instance.InitializeButtonConfigs(configBox.Buttons); + + // 鍒濆鍖栨寜閽姸鎬侊紙鍩轰簬閰嶇疆涓殑鎸夐挳绱㈠紩锛 + var buttonIndices = configBox.Buttons?.Select(b => b.Index).ToList() ?? new List(); + instance.InitializeButtons(buttonIndices); + + // 鑷姩杩炴帴 + instance.Connect(); + + return instance; + } + catch (Exception ex) + { + Diagnosis.Log($"鍒涘缓鎸夐挳鐩掑疄渚嬪け璐: Type={configBox.Type}, Error={ExceptionFormatter.FormatEx(ex)}", "ButtonMission", true); + return null; + } + } + + /// + /// 鑾峰彇褰撳墠鎵鏈夋寜閽洅瀹炰緥锛堝彧璇伙級 + /// + public IReadOnlyList GetButtonBoxes() + { + lock (_syncLock) + { + return _buttonBoxes.ToList().AsReadOnly(); + } + } + + /// + /// 鎵ц鎸夐挳鍔ㄤ綔锛堝湪鐙珛绾跨▼涓紓姝ユ墽琛岋紝閬垮厤闃诲鎸夐挳鐩戞帶寰幆锛 + /// + private void ExecuteButtonAction(ButtonModel buttonConfig, BasicButtonBox buttonBox) + { + + + Task.Run(() => ExecuteButtonActionInternal(buttonConfig, buttonBox)); + } + + /// + /// 瀹為檯鎵ц涓氬姟鏂规硶鐨勫唴閮ㄩ昏緫锛屽寘鍚垚鍔/澶辫触鍙嶉銆 + /// + private void ExecuteButtonActionInternal(ButtonModel buttonConfig, BasicButtonBox buttonBox) + { + var success = false; + try + { + // 鎸夐挳鍔ㄤ綔鎵ц鍚庢竻闆跺搴旀寜閽瘎瀛樺櫒锛堝叿浣撶洅鍨嬫寜闇閲嶅啓锛岄粯璁ょ┖瀹炵幇锛 + buttonBox.ClearButtonRegister(buttonConfig.Index); + + if (string.IsNullOrWhiteSpace(buttonConfig.TriggerMission) || + string.IsNullOrWhiteSpace(buttonConfig.TriggerMethod)) + { + // 閰嶇疆涓嶅畬鏁达紝鐩存帴鍙嶉澶辫触 + Diagnosis.Log("鎸夐挳閰嶇疆缂哄皯 TriggerMission 鎴 TriggerMethod锛屾棤娉曟墽琛屽姩浣", "ButtonMission", true); + return; + } + + var mission = SimpleProject.proj?.Missions? + .FirstOrDefault(m => m.GetType().Name == buttonConfig.TriggerMission || m.name == buttonConfig.TriggerMission); + + if (mission == null) + { + Diagnosis.Log($"鏈壘鍒拌Е鍙戜换鍔: {buttonConfig.TriggerMission}", "ButtonMission", true); + return; + } + + var method = mission.GetType().GetMethod(buttonConfig.TriggerMethod, + BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static); + + if (method == null) + { + Diagnosis.Log($"浠诲姟 {buttonConfig.TriggerMission} 涓湭鎵惧埌鏂规硶 {buttonConfig.TriggerMethod}", "ButtonMission", true); + return; + } + + var parameters = ParseMethodParameters(buttonConfig.TriggerMethodParams, method); + + if (method.IsStatic) + { + var result = method.Invoke(null, parameters); + success = HandleMethodResult(result); + } + else + { + var result = method.Invoke(mission, parameters); + success = HandleMethodResult(result); + } + } + catch (Exception ex) + { + Diagnosis.Log($"鎵ц鎸夐挳鍔ㄤ綔澶辫触: {ExceptionFormatter.FormatEx(ex)}", "ButtonMission", true); + } + finally + { + // 涓氬姟鏂规硶鎵ц瀹屾垚鍚庯紝鍥炶皟鎸夐挳鐩掕繘琛屽弽棣堬紙濡傜伅鍏夈佽渹楦g瓑锛 + try + { + buttonBox.OnActionExecuted(buttonConfig, success); + } + catch (Exception feedbackEx) + { + Diagnosis.Log($"鎸夐挳鐩掓墽琛屽弽棣堝け璐: {ExceptionFormatter.FormatEx(feedbackEx)}", "ButtonMission", true); + } + } + } + + /// + /// 澶勭悊鍙嶅皠璋冪敤缁撴灉锛氭敮鎸 Task/Task<bool> 绛夊紓姝ヨ繑鍥炵被鍨嬨 + /// 杩斿洖 true 琛ㄧず鎵ц鎴愬姛銆 + /// + private bool HandleMethodResult(object result) + { + try + { + switch (result) + { + case null: + return true; + case Task tb: + return tb.GetAwaiter().GetResult(); + case Task t: + t.GetAwaiter().GetResult(); + return true; + case bool b: + return b; + default: + return true; + } + } + catch (Exception ex) + { + Diagnosis.Log($"鎸夐挳鍔ㄤ綔鏂规硶寮傛鎵ц澶辫触: {ExceptionFormatter.FormatEx(ex)}", "ButtonMission", true); + return false; + } + } + + /// + /// 瑙f瀽鏂规硶鍙傛暟 + /// + private object[] ParseMethodParameters(string paramsStr, MethodInfo methodInfo) + { + var paramInfos = methodInfo.GetParameters(); + + if (paramInfos.Length == 0) + { + return Array.Empty(); + } + + if (string.IsNullOrWhiteSpace(paramsStr)) + { + return paramInfos.Select(p => p.HasDefaultValue ? p.DefaultValue : GetDefaultValue(p.ParameterType)).ToArray(); + } + + try + { + var paramStrings = paramsStr.Split(','); + var parameters = new List(); + + for (int i = 0; i < paramInfos.Length; i++) + { + var paramInfo = paramInfos[i]; + var paramType = paramInfo.ParameterType; + + if (i < paramStrings.Length) + { + var trimmed = paramStrings[i].Trim(); + parameters.Add(ConvertParameter(trimmed, paramType)); + } + else + { + parameters.Add(paramInfo.HasDefaultValue ? paramInfo.DefaultValue : GetDefaultValue(paramType)); + } + } + + return parameters.ToArray(); + } + catch (Exception ex) + { + Diagnosis.Log($"瑙f瀽鎸夐挳鍙傛暟澶辫触: {ExceptionFormatter.FormatEx(ex)}", "ButtonMission", true); + return paramInfos.Select(p => p.HasDefaultValue ? p.DefaultValue : GetDefaultValue(p.ParameterType)).ToArray(); + } + } + + /// + /// 杞崲鍙傛暟 + /// + private object ConvertParameter(string value, Type targetType) + { + if (targetType == typeof(string)) + { + return value; + } + if (targetType == typeof(int) || targetType == typeof(int?)) + { + return int.TryParse(value, out int result) ? result : (targetType == typeof(int?) ? (int?)null : 0); + } + if (targetType == typeof(double) || targetType == typeof(double?)) + { + return double.TryParse(value, out double result) ? result : (targetType == typeof(double?) ? (double?)null : 0d); + } + if (targetType == typeof(float) || targetType == typeof(float?)) + { + return float.TryParse(value, out float result) ? result : (targetType == typeof(float?) ? (float?)null : 0f); + } + if (targetType == typeof(bool) || targetType == typeof(bool?)) + { + return bool.TryParse(value, out bool result) ? result : (targetType == typeof(bool?) ? (bool?)null : false); + } + if (targetType.IsEnum) + { + try + { + return Enum.Parse(targetType, value, true); + } + catch + { + return Enum.GetValues(targetType).GetValue(0); + } + } + + return value; + } + + /// + /// 鑾峰彇绫诲瀷榛樿鍊 + /// + private object GetDefaultValue(Type type) + { + if (type.IsValueType) + { + return Activator.CreateInstance(type); + } + + return null; + } + + /// + /// 鎵撳紑鎸夐挳鐩掔鐞嗙晫闈 + /// + [MethodMember(Name = "鎵撳紑绠$悊鐣岄潰")] + [I18N.DocumentTranslation(Name = "Open Manager", locale = "en")] + public static void OpenViewer() + { + try + { + var manager = ButtonBoxManager.Instance; + + // 纭繚绐椾綋娌℃湁琚攢姣 + if (manager.IsDisposed) + { + // 濡傛灉绐椾綋琚攢姣侊紝鍗曚緥浼氳嚜鍔ㄩ噸鏂板垱寤 + manager = ButtonBoxManager.Instance; + } + + if (manager.Visible) + { + // 濡傛灉鐣岄潰宸茬粡鍙锛屽皢鍏舵縺娲诲苟缃簬鏈鍓 + if (manager.WindowState == FormWindowState.Minimized) + { + manager.WindowState = FormWindowState.Normal; + } + manager.Activate(); + manager.BringToFront(); + } + else + { + // 濡傛灉鐣岄潰涓嶅彲瑙侊紝鏄剧ず瀹 + manager.Show(); + manager.Activate(); + } + } + catch (Exception ex) + { + MessageBox.Show($"鎵撳紑鎸夐挳鐩掔鐞嗙晫闈㈠け璐: {ex.Message}", "閿欒", + MessageBoxButtons.OK, MessageBoxIcon.Error); + } + } + } +} diff --git a/StandardScene.Core/ExtendDevice/Door/BasicDoorController.cs b/StandardScene.Core/ExtendDevice/Door/BasicDoorController.cs new file mode 100644 index 0000000..f6ff150 --- /dev/null +++ b/StandardScene.Core/ExtendDevice/Door/BasicDoorController.cs @@ -0,0 +1,278 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using SimpleCore.Library; + +namespace StandardScene.ExtendDevice.Door +{ + /// + /// 闂ㄧ姸鎬佹灇涓 + /// + public enum DoorState + { + /// + /// 鍏抽棴 + /// + Closed = 0, + + /// + /// 鎵撳紑 + /// + Open = 1, + + /// + /// 鏈煡鐘舵 + /// + Unknown = 2 + } + + /// + /// 闂ㄦ帶鍒跺櫒鐘舵佹灇涓 + /// + public enum DoorControllerState + { + /// + /// 绂荤嚎 + /// + Offline = 0, + + /// + /// 鍦ㄧ嚎 + /// + Online = 1, + + /// + /// 杩炴帴涓 + /// + Connecting = 2, + + /// + /// 閿欒 + /// + Error = 3 + } + + /// + /// 鍩虹闂ㄦ帶鍒跺櫒绫 + /// + public abstract class BasicDoorController + { + /// + /// 鎺у埗鍣ㄧ储寮 + /// + public int Index { get; set; } + + /// + /// IP鍦板潃 + /// + public string Ip { get; set; } = string.Empty; + + /// + /// 绔彛 + /// + public int Port { get; set; } = 502; + + /// + /// 鎺у埗鍣ㄧ姸鎬 + /// + public DoorControllerState State { get; protected set; } = DoorControllerState.Offline; + + /// + /// 鏄惁鍦ㄧ嚎 + /// + public bool IsOnline => State == DoorControllerState.Online; + + /// + /// 闂ㄧ姸鎬佸瓧鍏革紝閿负闂ㄧ储寮 + /// + public Dictionary DoorStates { get; protected set; } = new Dictionary(); + + /// + /// 闂ㄧ洰鏍囨帶鍒跺瓧鍏革紝閿负闂ㄧ储寮曪紝鍊间负鏈熸湜鐨勫紑鍏崇姸鎬侊紙true=鎵撳紑锛宖alse=鍏抽棴锛 + /// 浠呬綔涓烘寚浠ょ紦瀛橈紝瀹為檯閫氫俊鐢卞叿浣撻棬鎺у埗鍣ㄥ唴閮ㄧ嚎绋嬪畬鎴 + /// + public Dictionary DoorControlTargets { get; protected set; } = new Dictionary(); + + /// + /// 闂ㄩ厤缃俊鎭瓧鍏革紝閿负闂ㄧ储寮 + /// + public Dictionary DoorConfigs { get; protected set; } = new Dictionary(); + + /// + /// 鏈鍚庢洿鏂版椂闂 + /// + public DateTime LastUpdateTime { get; protected set; } = DateTime.Now; + + /// + /// 閿欒淇℃伅 + /// + public string ErrorMessage { get; protected set; } = string.Empty; + + /// + /// 鏇存柊鎺у埗鍣ㄧ姸鎬 + /// + public virtual void UpdateState(DoorControllerState newState, string errorMessage = "") + { + State = newState; + ErrorMessage = errorMessage; + LastUpdateTime = DateTime.Now; + } + + /// + /// 鏇存柊闂ㄧ姸鎬 + /// + /// 闂ㄧ储寮 + /// 闂ㄧ姸鎬 + public virtual void UpdateDoorState(int doorIndex, DoorState state) + { + if (!DoorStates.ContainsKey(doorIndex)) + { + Diagnosis.Post($"闂ㄦ帶鍒跺櫒{Index}涓嶅瓨鍦ㄩ棬{doorIndex}"); + } + DoorStates[doorIndex] = state; + LastUpdateTime = DateTime.Now; + } + + /// + /// 鑾峰彇闂ㄧ姸鎬 + /// + /// 闂ㄧ储寮 + /// 闂ㄧ姸鎬侊紝濡傛灉涓嶅瓨鍦ㄥ垯杩斿洖Unknown + public virtual DoorState GetDoorState(int doorIndex) + { + return DoorStates.TryGetValue(doorIndex, out var state) ? state : DoorState.Unknown; + } + + /// + /// 鍒濆鍖栭棬鐘舵 + /// + /// 闂ㄧ储寮曞垪琛 + public virtual void InitializeDoors(List doorIndices) + { + DoorStates.Clear(); + DoorControlTargets.Clear(); + foreach (var index in doorIndices) + { + DoorStates[index] = DoorState.Closed; + DoorControlTargets[index] = false; + } + } + + /// + /// 鍒濆鍖栭棬閰嶇疆淇℃伅 + /// + /// 闂ㄩ厤缃垪琛 + public virtual void InitializeDoorConfigs(List doorConfigs) + { + DoorConfigs.Clear(); + if (doorConfigs != null) + { + foreach (var config in doorConfigs) + { + DoorConfigs[config.Index] = new DoorModel + { + Index = config.Index, + ControlAddress = config.ControlAddress, + OpenStatusAddress = config.OpenStatusAddress, + NoControl = config.NoControl + }; + } + } + } + + /// + /// 鏇存柊闂ㄩ厤缃俊鎭 + /// + /// 闂ㄩ厤缃垪琛 + public virtual void UpdateDoorConfigs(List doorConfigs) + { + if (doorConfigs == null) + { + DoorConfigs.Clear(); + return; + } + + // 鍒涘缓閰嶇疆瀛楀吀 + var configDict = doorConfigs.ToDictionary(d => d.Index); + + // 鍒犻櫎閰嶇疆涓笉瀛樺湪鐨勯棬 + var toRemove = DoorConfigs.Keys.Where(k => !configDict.ContainsKey(k)).ToList(); + foreach (var key in toRemove) + { + DoorConfigs.Remove(key); + } + + // 娣诲姞鎴栨洿鏂伴棬閰嶇疆 + foreach (var config in doorConfigs) + { + DoorConfigs[config.Index] = new DoorModel + { + Index = config.Index, + ControlAddress = config.ControlAddress, + OpenStatusAddress = config.OpenStatusAddress, + NoControl = config.NoControl + }; + } + } + + /// + /// 鑾峰彇闂ㄩ厤缃俊鎭 + /// + /// 闂ㄧ储寮 + /// 闂ㄩ厤缃俊鎭紝濡傛灉涓嶅瓨鍦ㄥ垯杩斿洖null + public virtual DoorModel GetDoorConfig(int doorIndex) + { + return DoorConfigs.TryGetValue(doorIndex, out var config) ? config : null; + } + + /// + /// 璁剧疆闂ㄧ殑鐩爣鎺у埗鐘舵侊紙浠呬慨鏀瑰唴瀛樺瓧娈碉紝涓嶇洿鎺ヨ繘琛岄氫俊锛 + /// 瀹為檯鐨勯氫俊鍐欏叆鐢卞叿浣撻棬鎺у埗鍣ㄥ湪鍐呴儴绾跨▼涓牴鎹鐩爣鐘舵佹墽琛 + /// + /// 闂ㄧ储寮 + /// true=鎵撳紑锛宖alse=鍏抽棴 + public virtual void SetDoorControlTarget(int doorIndex, bool open) + { + // 缁熶竴鏀寔 NoControl锛氬綋闂ㄨ閰嶇疆涓轰笉鍏佽鍙戦佷换浣曟帶鍒舵寚浠ゆ椂锛 + // 寮哄埗灏嗙洰鏍囩疆涓 false锛屽苟閬垮厤涓哄叾瀹冩帶鍒跺櫒鐣欎笅鈥滈渶瑕佸紑闂ㄢ濈殑鐩爣銆 + if (DoorConfigs.TryGetValue(doorIndex, out var cfg) && cfg != null && cfg.NoControl) + { + DoorControlTargets[doorIndex] = false; + LastUpdateTime = DateTime.Now; + return; + } + DoorControlTargets[doorIndex] = open; + LastUpdateTime = DateTime.Now; + } + + /// + /// 杩炴帴闂ㄦ帶鍒跺櫒 + /// + public virtual void Connect() + { + UpdateState(DoorControllerState.Connecting); + } + + /// + /// 鏂紑杩炴帴 + /// + public virtual void Disconnect() + { + UpdateState(DoorControllerState.Offline); + } + + /// + /// 璇诲彇闂ㄧ姸鎬侊紙寮鍒颁綅淇″彿锛 + /// + /// 闂ㄧ储寮 + /// 闂ㄧ姸鎬 + public abstract bool ReadDoorState(int doorIndex); + + /// + /// 鍐欏叆闂ㄦ帶鍒朵俊鍙凤紙寮鍏虫帶鍒讹級 + /// + /// 闂ㄧ储寮 + /// true=鎵撳紑锛宖alse=鍏抽棴 + public abstract void WriteDoorControl(int doorIndex, bool open); + } +} diff --git a/StandardScene.Core/ExtendDevice/Door/DoorManager.Designer.cs b/StandardScene.Core/ExtendDevice/Door/DoorManager.Designer.cs new file mode 100644 index 0000000..94f48c0 --- /dev/null +++ b/StandardScene.Core/ExtendDevice/Door/DoorManager.Designer.cs @@ -0,0 +1,521 @@ +namespace StandardScene.ExtendDevice.Door +{ + partial class DoorManager + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.doorControllerListView = new System.Windows.Forms.ListView(); + this.columnHeaderControllerIndex = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.columnHeaderIp = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.columnHeaderPort = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.columnHeaderType = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.groupBoxController = new System.Windows.Forms.GroupBox(); + this.btnSaveController = new System.Windows.Forms.Button(); + this.btnDeleteController = new System.Windows.Forms.Button(); + this.btnAddController = new System.Windows.Forms.Button(); + this.labelType = new System.Windows.Forms.Label(); + this.comboBoxType = new System.Windows.Forms.ComboBox(); + this.labelControllerIndex = new System.Windows.Forms.Label(); + this.textBoxControllerIndex = new System.Windows.Forms.TextBox(); + this.labelPort = new System.Windows.Forms.Label(); + this.textBoxPort = new System.Windows.Forms.TextBox(); + this.labelIp = new System.Windows.Forms.Label(); + this.textBoxIp = new System.Windows.Forms.TextBox(); + this.doorListView = new System.Windows.Forms.ListView(); + this.columnHeaderDoorIndex = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.columnHeaderControlAddress = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.columnHeaderOpenStatusAddress = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.groupBoxDoor = new System.Windows.Forms.GroupBox(); + this.btnSaveDoor = new System.Windows.Forms.Button(); + this.btnDeleteDoor = new System.Windows.Forms.Button(); + this.btnAddDoor = new System.Windows.Forms.Button(); + this.labelOpenStatusAddress = new System.Windows.Forms.Label(); + this.textBoxOpenStatusAddress = new System.Windows.Forms.TextBox(); + this.labelControlAddress = new System.Windows.Forms.Label(); + this.textBoxControlAddress = new System.Windows.Forms.TextBox(); + this.labelDoorIndex = new System.Windows.Forms.Label(); + this.textBoxDoorIndex = new System.Windows.Forms.TextBox(); + this.labelTitle = new System.Windows.Forms.Label(); + this.groupBoxController.SuspendLayout(); + this.groupBoxDoor.SuspendLayout(); + this.SuspendLayout(); + // + // doorControllerListView + // + this.doorControllerListView.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left))); + this.doorControllerListView.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; + this.doorControllerListView.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] { + this.columnHeaderControllerIndex, + this.columnHeaderIp, + this.columnHeaderPort, + this.columnHeaderType}); + this.doorControllerListView.Font = new System.Drawing.Font("寰蒋闆呴粦", 10.5F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + this.doorControllerListView.FullRowSelect = true; + this.doorControllerListView.GridLines = true; + this.doorControllerListView.HeaderStyle = System.Windows.Forms.ColumnHeaderStyle.Nonclickable; + this.doorControllerListView.HideSelection = false; + this.doorControllerListView.Location = new System.Drawing.Point(15, 55); + this.doorControllerListView.MultiSelect = false; + this.doorControllerListView.Name = "doorControllerListView"; + this.doorControllerListView.OwnerDraw = true; + this.doorControllerListView.Size = new System.Drawing.Size(450, 290); + this.doorControllerListView.TabIndex = 0; + this.doorControllerListView.UseCompatibleStateImageBehavior = false; + this.doorControllerListView.View = System.Windows.Forms.View.Details; + this.doorControllerListView.SelectedIndexChanged += new System.EventHandler(this.doorControllerListView_SelectedIndexChanged); + // + // columnHeaderControllerIndex + // + this.columnHeaderControllerIndex.Text = "缂栫爜"; + this.columnHeaderControllerIndex.Width = 70; + // + // columnHeaderIp + // + this.columnHeaderIp.Text = "IP鍦板潃"; + this.columnHeaderIp.Width = 130; + // + // columnHeaderPort + // + this.columnHeaderPort.Text = "绔彛"; + this.columnHeaderPort.Width = 90; + // + // columnHeaderType + // + this.columnHeaderType.Text = "绫诲瀷"; + this.columnHeaderType.Width = 140; + // + // groupBoxController + // + this.groupBoxController.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); + this.groupBoxController.Controls.Add(this.btnSaveController); + this.groupBoxController.Controls.Add(this.btnDeleteController); + this.groupBoxController.Controls.Add(this.btnAddController); + this.groupBoxController.Controls.Add(this.labelType); + this.groupBoxController.Controls.Add(this.comboBoxType); + this.groupBoxController.Controls.Add(this.labelControllerIndex); + this.groupBoxController.Controls.Add(this.textBoxControllerIndex); + this.groupBoxController.Controls.Add(this.labelPort); + this.groupBoxController.Controls.Add(this.textBoxPort); + this.groupBoxController.Controls.Add(this.labelIp); + this.groupBoxController.Controls.Add(this.textBoxIp); + this.groupBoxController.Font = new System.Drawing.Font("寰蒋闆呴粦", 10.5F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + this.groupBoxController.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(68)))), ((int)(((byte)(68)))), ((int)(((byte)(68))))); + this.groupBoxController.Location = new System.Drawing.Point(15, 360); + this.groupBoxController.Name = "groupBoxController"; + this.groupBoxController.Padding = new System.Windows.Forms.Padding(12, 10, 12, 12); + this.groupBoxController.Size = new System.Drawing.Size(450, 250); + this.groupBoxController.TabIndex = 1; + this.groupBoxController.TabStop = false; + this.groupBoxController.Text = "闂ㄦ帶鍒跺櫒淇℃伅"; + // + // btnSaveController + // + this.btnSaveController.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(0)))), ((int)(((byte)(122)))), ((int)(((byte)(204))))); + this.btnSaveController.FlatAppearance.BorderSize = 0; + this.btnSaveController.FlatAppearance.MouseDownBackColor = System.Drawing.Color.FromArgb(((int)(((byte)(0)))), ((int)(((byte)(92)))), ((int)(((byte)(153))))); + this.btnSaveController.FlatAppearance.MouseOverBackColor = System.Drawing.Color.FromArgb(((int)(((byte)(0)))), ((int)(((byte)(102)))), ((int)(((byte)(170))))); + this.btnSaveController.FlatStyle = System.Windows.Forms.FlatStyle.Flat; + this.btnSaveController.Font = new System.Drawing.Font("寰蒋闆呴粦", 10.5F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + this.btnSaveController.ForeColor = System.Drawing.Color.White; + this.btnSaveController.Location = new System.Drawing.Point(330, 200); + this.btnSaveController.Name = "btnSaveController"; + this.btnSaveController.Size = new System.Drawing.Size(100, 38); + this.btnSaveController.TabIndex = 10; + this.btnSaveController.Text = "淇濆瓨"; + this.btnSaveController.UseVisualStyleBackColor = false; + this.btnSaveController.Click += new System.EventHandler(this.btnSaveController_Click); + // + // btnDeleteController + // + this.btnDeleteController.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(220)))), ((int)(((byte)(53)))), ((int)(((byte)(69))))); + this.btnDeleteController.FlatAppearance.BorderSize = 0; + this.btnDeleteController.FlatAppearance.MouseDownBackColor = System.Drawing.Color.FromArgb(((int)(((byte)(165)))), ((int)(((byte)(40)))), ((int)(((byte)(52))))); + this.btnDeleteController.FlatAppearance.MouseOverBackColor = System.Drawing.Color.FromArgb(((int)(((byte)(187)))), ((int)(((byte)(45)))), ((int)(((byte)(59))))); + this.btnDeleteController.FlatStyle = System.Windows.Forms.FlatStyle.Flat; + this.btnDeleteController.Font = new System.Drawing.Font("寰蒋闆呴粦", 10.5F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + this.btnDeleteController.ForeColor = System.Drawing.Color.White; + this.btnDeleteController.Location = new System.Drawing.Point(220, 200); + this.btnDeleteController.Name = "btnDeleteController"; + this.btnDeleteController.Size = new System.Drawing.Size(100, 38); + this.btnDeleteController.TabIndex = 9; + this.btnDeleteController.Text = "鍒犻櫎"; + this.btnDeleteController.UseVisualStyleBackColor = false; + this.btnDeleteController.Click += new System.EventHandler(this.btnDeleteController_Click); + // + // btnAddController + // + this.btnAddController.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(40)))), ((int)(((byte)(167)))), ((int)(((byte)(69))))); + this.btnAddController.FlatAppearance.BorderSize = 0; + this.btnAddController.FlatAppearance.MouseDownBackColor = System.Drawing.Color.FromArgb(((int)(((byte)(30)))), ((int)(((byte)(125)))), ((int)(((byte)(52))))); + this.btnAddController.FlatAppearance.MouseOverBackColor = System.Drawing.Color.FromArgb(((int)(((byte)(33)))), ((int)(((byte)(136)))), ((int)(((byte)(56))))); + this.btnAddController.FlatStyle = System.Windows.Forms.FlatStyle.Flat; + this.btnAddController.Font = new System.Drawing.Font("寰蒋闆呴粦", 10.5F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + this.btnAddController.ForeColor = System.Drawing.Color.White; + this.btnAddController.Location = new System.Drawing.Point(110, 200); + this.btnAddController.Name = "btnAddController"; + this.btnAddController.Size = new System.Drawing.Size(100, 38); + this.btnAddController.TabIndex = 8; + this.btnAddController.Text = "娣诲姞"; + this.btnAddController.UseVisualStyleBackColor = false; + this.btnAddController.Click += new System.EventHandler(this.btnAddController_Click); + // + // labelType + // + this.labelType.AutoSize = true; + this.labelType.Font = new System.Drawing.Font("寰蒋闆呴粦", 10.5F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + this.labelType.Location = new System.Drawing.Point(28, 168); + this.labelType.Name = "labelType"; + this.labelType.Size = new System.Drawing.Size(65, 24); + this.labelType.TabIndex = 7; + this.labelType.Text = "绫诲瀷锛"; + // + // comboBoxType + // + this.comboBoxType.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.comboBoxType.Font = new System.Drawing.Font("寰蒋闆呴粦", 10.5F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + this.comboBoxType.FormattingEnabled = true; + this.comboBoxType.Location = new System.Drawing.Point(110, 165); + this.comboBoxType.Name = "comboBoxType"; + this.comboBoxType.Size = new System.Drawing.Size(320, 32); + this.comboBoxType.TabIndex = 6; + // + // labelControllerIndex + // + this.labelControllerIndex.AutoSize = true; + this.labelControllerIndex.Font = new System.Drawing.Font("寰蒋闆呴粦", 10.5F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + this.labelControllerIndex.Location = new System.Drawing.Point(28, 48); + this.labelControllerIndex.Name = "labelControllerIndex"; + this.labelControllerIndex.Size = new System.Drawing.Size(65, 24); + this.labelControllerIndex.TabIndex = 1; + this.labelControllerIndex.Text = "缂栫爜锛"; + // + // textBoxControllerIndex + // + this.textBoxControllerIndex.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; + this.textBoxControllerIndex.Font = new System.Drawing.Font("寰蒋闆呴粦", 10.5F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + this.textBoxControllerIndex.Location = new System.Drawing.Point(110, 45); + this.textBoxControllerIndex.Name = "textBoxControllerIndex"; + this.textBoxControllerIndex.Size = new System.Drawing.Size(320, 30); + this.textBoxControllerIndex.TabIndex = 0; + // + // labelIp + // + this.labelIp.AutoSize = true; + this.labelIp.Font = new System.Drawing.Font("寰蒋闆呴粦", 10.5F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + this.labelIp.Location = new System.Drawing.Point(18, 88); + this.labelIp.Name = "labelIp"; + this.labelIp.Size = new System.Drawing.Size(85, 24); + this.labelIp.TabIndex = 3; + this.labelIp.Text = "IP鍦板潃锛"; + // + // textBoxIp + // + this.textBoxIp.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; + this.textBoxIp.Font = new System.Drawing.Font("寰蒋闆呴粦", 10.5F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + this.textBoxIp.Location = new System.Drawing.Point(110, 85); + this.textBoxIp.Name = "textBoxIp"; + this.textBoxIp.Size = new System.Drawing.Size(320, 30); + this.textBoxIp.TabIndex = 2; + this.textBoxIp.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(255)))), ((int)(((byte)(255)))), ((int)(((byte)(255))))); + // + // labelPort + // + this.labelPort.AutoSize = true; + this.labelPort.Font = new System.Drawing.Font("寰蒋闆呴粦", 10.5F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + this.labelPort.Location = new System.Drawing.Point(28, 128); + this.labelPort.Name = "labelPort"; + this.labelPort.Size = new System.Drawing.Size(65, 24); + this.labelPort.TabIndex = 5; + this.labelPort.Text = "绔彛锛"; + // + // textBoxPort + // + this.textBoxPort.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; + this.textBoxPort.Font = new System.Drawing.Font("寰蒋闆呴粦", 10.5F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + this.textBoxPort.Location = new System.Drawing.Point(110, 125); + this.textBoxPort.Name = "textBoxPort"; + this.textBoxPort.Size = new System.Drawing.Size(320, 30); + this.textBoxPort.TabIndex = 4; + // + // doorListView + // + this.doorListView.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left))); + this.doorListView.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; + this.doorListView.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] { + this.columnHeaderDoorIndex, + this.columnHeaderControlAddress, + this.columnHeaderOpenStatusAddress}); + this.doorListView.Font = new System.Drawing.Font("寰蒋闆呴粦", 10.5F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + this.doorListView.FullRowSelect = true; + this.doorListView.GridLines = true; + this.doorListView.HeaderStyle = System.Windows.Forms.ColumnHeaderStyle.Nonclickable; + this.doorListView.HideSelection = false; + this.doorListView.Location = new System.Drawing.Point(483, 55); + this.doorListView.MultiSelect = false; + this.doorListView.Name = "doorListView"; + this.doorListView.OwnerDraw = true; + this.doorListView.Size = new System.Drawing.Size(500, 290); + this.doorListView.TabIndex = 2; + this.doorListView.UseCompatibleStateImageBehavior = false; + this.doorListView.View = System.Windows.Forms.View.Details; + this.doorListView.SelectedIndexChanged += new System.EventHandler(this.doorListView_SelectedIndexChanged); + // + // columnHeaderDoorIndex + // + this.columnHeaderDoorIndex.Text = "缂栫爜"; + this.columnHeaderDoorIndex.Width = 100; + // + // columnHeaderControlAddress + // + this.columnHeaderControlAddress.Text = "鎺у埗鍦板潃"; + this.columnHeaderControlAddress.Width = 180; + // + // columnHeaderOpenStatusAddress + // + this.columnHeaderOpenStatusAddress.Text = "寮鍒颁綅鍦板潃"; + this.columnHeaderOpenStatusAddress.Width = 180; + // + // groupBoxDoor + // + this.groupBoxDoor.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); + this.checkBoxNoControl = new System.Windows.Forms.CheckBox(); + this.groupBoxDoor.Controls.Add(this.checkBoxNoControl); + this.groupBoxDoor.Controls.Add(this.btnSaveDoor); + this.groupBoxDoor.Controls.Add(this.btnDeleteDoor); + this.groupBoxDoor.Controls.Add(this.btnAddDoor); + this.groupBoxDoor.Controls.Add(this.labelOpenStatusAddress); + this.groupBoxDoor.Controls.Add(this.textBoxOpenStatusAddress); + this.groupBoxDoor.Controls.Add(this.labelControlAddress); + this.groupBoxDoor.Controls.Add(this.textBoxControlAddress); + this.groupBoxDoor.Controls.Add(this.labelDoorIndex); + this.groupBoxDoor.Controls.Add(this.textBoxDoorIndex); + this.groupBoxDoor.Font = new System.Drawing.Font("寰蒋闆呴粦", 10.5F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + this.groupBoxDoor.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(68)))), ((int)(((byte)(68)))), ((int)(((byte)(68))))); + this.groupBoxDoor.Location = new System.Drawing.Point(483, 360); + this.groupBoxDoor.Name = "groupBoxDoor"; + this.groupBoxDoor.Padding = new System.Windows.Forms.Padding(12, 10, 12, 12); + this.groupBoxDoor.Size = new System.Drawing.Size(500, 250); + this.groupBoxDoor.TabIndex = 3; + this.groupBoxDoor.TabStop = false; + this.groupBoxDoor.Text = "闂ㄤ俊鎭"; + // + // btnSaveDoor + // + this.btnSaveDoor.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(0)))), ((int)(((byte)(122)))), ((int)(((byte)(204))))); + this.btnSaveDoor.FlatAppearance.BorderSize = 0; + this.btnSaveDoor.FlatAppearance.MouseDownBackColor = System.Drawing.Color.FromArgb(((int)(((byte)(0)))), ((int)(((byte)(92)))), ((int)(((byte)(153))))); + this.btnSaveDoor.FlatAppearance.MouseOverBackColor = System.Drawing.Color.FromArgb(((int)(((byte)(0)))), ((int)(((byte)(102)))), ((int)(((byte)(170))))); + this.btnSaveDoor.FlatStyle = System.Windows.Forms.FlatStyle.Flat; + this.btnSaveDoor.Font = new System.Drawing.Font("寰蒋闆呴粦", 10.5F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + this.btnSaveDoor.ForeColor = System.Drawing.Color.White; + this.btnSaveDoor.Location = new System.Drawing.Point(380, 200); + this.btnSaveDoor.Name = "btnSaveDoor"; + this.btnSaveDoor.Size = new System.Drawing.Size(100, 38); + this.btnSaveDoor.TabIndex = 7; + this.btnSaveDoor.Text = "淇濆瓨"; + this.btnSaveDoor.UseVisualStyleBackColor = false; + this.btnSaveDoor.Click += new System.EventHandler(this.btnSaveDoor_Click); + // + // btnDeleteDoor + // + this.btnDeleteDoor.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(220)))), ((int)(((byte)(53)))), ((int)(((byte)(69))))); + this.btnDeleteDoor.FlatAppearance.BorderSize = 0; + this.btnDeleteDoor.FlatAppearance.MouseDownBackColor = System.Drawing.Color.FromArgb(((int)(((byte)(165)))), ((int)(((byte)(40)))), ((int)(((byte)(52))))); + this.btnDeleteDoor.FlatAppearance.MouseOverBackColor = System.Drawing.Color.FromArgb(((int)(((byte)(187)))), ((int)(((byte)(45)))), ((int)(((byte)(59))))); + this.btnDeleteDoor.FlatStyle = System.Windows.Forms.FlatStyle.Flat; + this.btnDeleteDoor.Font = new System.Drawing.Font("寰蒋闆呴粦", 10.5F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + this.btnDeleteDoor.ForeColor = System.Drawing.Color.White; + this.btnDeleteDoor.Location = new System.Drawing.Point(270, 200); + this.btnDeleteDoor.Name = "btnDeleteDoor"; + this.btnDeleteDoor.Size = new System.Drawing.Size(100, 38); + this.btnDeleteDoor.TabIndex = 6; + this.btnDeleteDoor.Text = "鍒犻櫎"; + this.btnDeleteDoor.UseVisualStyleBackColor = false; + this.btnDeleteDoor.Click += new System.EventHandler(this.btnDeleteDoor_Click); + // + // btnAddDoor + // + this.btnAddDoor.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(40)))), ((int)(((byte)(167)))), ((int)(((byte)(69))))); + this.btnAddDoor.FlatAppearance.BorderSize = 0; + this.btnAddDoor.FlatAppearance.MouseDownBackColor = System.Drawing.Color.FromArgb(((int)(((byte)(30)))), ((int)(((byte)(125)))), ((int)(((byte)(52))))); + this.btnAddDoor.FlatAppearance.MouseOverBackColor = System.Drawing.Color.FromArgb(((int)(((byte)(33)))), ((int)(((byte)(136)))), ((int)(((byte)(56))))); + this.btnAddDoor.FlatStyle = System.Windows.Forms.FlatStyle.Flat; + this.btnAddDoor.Font = new System.Drawing.Font("寰蒋闆呴粦", 10.5F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + this.btnAddDoor.ForeColor = System.Drawing.Color.White; + this.btnAddDoor.Location = new System.Drawing.Point(160, 200); + this.btnAddDoor.Name = "btnAddDoor"; + this.btnAddDoor.Size = new System.Drawing.Size(100, 38); + this.btnAddDoor.TabIndex = 5; + this.btnAddDoor.Text = "娣诲姞"; + this.btnAddDoor.UseVisualStyleBackColor = false; + this.btnAddDoor.Click += new System.EventHandler(this.btnAddDoor_Click); + // + // labelOpenStatusAddress + // + this.labelOpenStatusAddress.AutoSize = true; + this.labelOpenStatusAddress.Font = new System.Drawing.Font("寰蒋闆呴粦", 10.5F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + this.labelOpenStatusAddress.Location = new System.Drawing.Point(18, 128); + this.labelOpenStatusAddress.Name = "labelOpenStatusAddress"; + this.labelOpenStatusAddress.Size = new System.Drawing.Size(103, 24); + this.labelOpenStatusAddress.TabIndex = 4; + this.labelOpenStatusAddress.Text = "寮鍒颁綅鍦板潃锛"; + // + // textBoxOpenStatusAddress + // + this.textBoxOpenStatusAddress.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; + this.textBoxOpenStatusAddress.Font = new System.Drawing.Font("寰蒋闆呴粦", 10.5F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + this.textBoxOpenStatusAddress.Location = new System.Drawing.Point(150, 125); + this.textBoxOpenStatusAddress.Name = "textBoxOpenStatusAddress"; + this.textBoxOpenStatusAddress.Size = new System.Drawing.Size(330, 30); + this.textBoxOpenStatusAddress.TabIndex = 3; + // + // checkBoxNoControl + // + this.checkBoxNoControl.AutoSize = true; + this.checkBoxNoControl.Font = new System.Drawing.Font("寰蒋闆呴粦", 10.5F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + this.checkBoxNoControl.Location = new System.Drawing.Point(150, 165); + this.checkBoxNoControl.Name = "checkBoxNoControl"; + this.checkBoxNoControl.Size = new System.Drawing.Size(162, 28); + this.checkBoxNoControl.TabIndex = 4; + this.checkBoxNoControl.Text = "绂佹闂ㄦ帶鍙戦佹寚浠"; + this.checkBoxNoControl.UseVisualStyleBackColor = true; + // + // labelControlAddress + // + this.labelControlAddress.AutoSize = true; + this.labelControlAddress.Font = new System.Drawing.Font("寰蒋闆呴粦", 10.5F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + this.labelControlAddress.Location = new System.Drawing.Point(18, 88); + this.labelControlAddress.Name = "labelControlAddress"; + this.labelControlAddress.Size = new System.Drawing.Size(103, 24); + this.labelControlAddress.TabIndex = 2; + this.labelControlAddress.Text = "鎺у埗鍦板潃锛"; + // + // textBoxControlAddress + // + this.textBoxControlAddress.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; + this.textBoxControlAddress.Font = new System.Drawing.Font("寰蒋闆呴粦", 10.5F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + this.textBoxControlAddress.Location = new System.Drawing.Point(150, 85); + this.textBoxControlAddress.Name = "textBoxControlAddress"; + this.textBoxControlAddress.Size = new System.Drawing.Size(330, 30); + this.textBoxControlAddress.TabIndex = 1; + // + // labelDoorIndex + // + this.labelDoorIndex.AutoSize = true; + this.labelDoorIndex.Font = new System.Drawing.Font("寰蒋闆呴粦", 10.5F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + this.labelDoorIndex.Location = new System.Drawing.Point(28, 48); + this.labelDoorIndex.Name = "labelDoorIndex"; + this.labelDoorIndex.Size = new System.Drawing.Size(65, 24); + this.labelDoorIndex.TabIndex = 0; + this.labelDoorIndex.Text = "缂栫爜锛"; + // + // textBoxDoorIndex + // + this.textBoxDoorIndex.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; + this.textBoxDoorIndex.Font = new System.Drawing.Font("寰蒋闆呴粦", 10.5F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + this.textBoxDoorIndex.Location = new System.Drawing.Point(150, 45); + this.textBoxDoorIndex.Name = "textBoxDoorIndex"; + this.textBoxDoorIndex.Size = new System.Drawing.Size(330, 30); + this.textBoxDoorIndex.TabIndex = 0; + // + // labelTitle + // + this.labelTitle.AutoSize = true; + this.labelTitle.Font = new System.Drawing.Font("寰蒋闆呴粦", 16F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + this.labelTitle.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(51)))), ((int)(((byte)(51)))), ((int)(((byte)(51))))); + this.labelTitle.Location = new System.Drawing.Point(15, 12); + this.labelTitle.Name = "labelTitle"; + this.labelTitle.Size = new System.Drawing.Size(150, 42); + this.labelTitle.TabIndex = 4; + this.labelTitle.Text = "闂ㄦ帶鍒跺櫒绠$悊"; + // + // DoorManager + // + this.AutoScaleDimensions = new System.Drawing.SizeF(9F, 18F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(245)))), ((int)(((byte)(245)))), ((int)(((byte)(247))))); + this.ClientSize = new System.Drawing.Size(1000, 620); + this.Controls.Add(this.labelTitle); + this.Controls.Add(this.groupBoxDoor); + this.Controls.Add(this.doorListView); + this.Controls.Add(this.groupBoxController); + this.Controls.Add(this.doorControllerListView); + this.Font = new System.Drawing.Font("寰蒋闆呴粦", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + this.MinimumSize = new System.Drawing.Size(1000, 620); + this.Name = "DoorManager"; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen; + this.Text = "闂ㄦ帶鍒跺櫒绠$悊"; + this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.DoorManager_FormClosing); + this.Load += new System.EventHandler(this.DoorManager_Load); + this.groupBoxController.ResumeLayout(false); + this.groupBoxController.PerformLayout(); + this.groupBoxDoor.ResumeLayout(false); + this.groupBoxDoor.PerformLayout(); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private System.Windows.Forms.ListView doorControllerListView; + private System.Windows.Forms.ColumnHeader columnHeaderControllerIndex; + private System.Windows.Forms.ColumnHeader columnHeaderIp; + private System.Windows.Forms.ColumnHeader columnHeaderPort; + private System.Windows.Forms.ColumnHeader columnHeaderType; + private System.Windows.Forms.GroupBox groupBoxController; + private System.Windows.Forms.TextBox textBoxIp; + private System.Windows.Forms.Label labelIp; + private System.Windows.Forms.Label labelPort; + private System.Windows.Forms.TextBox textBoxPort; + private System.Windows.Forms.Label labelControllerIndex; + private System.Windows.Forms.TextBox textBoxControllerIndex; + private System.Windows.Forms.Label labelType; + private System.Windows.Forms.ComboBox comboBoxType; + private System.Windows.Forms.Button btnAddController; + private System.Windows.Forms.Button btnDeleteController; + private System.Windows.Forms.Button btnSaveController; + private System.Windows.Forms.ListView doorListView; + private System.Windows.Forms.ColumnHeader columnHeaderDoorIndex; + private System.Windows.Forms.ColumnHeader columnHeaderControlAddress; + private System.Windows.Forms.ColumnHeader columnHeaderOpenStatusAddress; + private System.Windows.Forms.GroupBox groupBoxDoor; + private System.Windows.Forms.Label labelDoorIndex; + private System.Windows.Forms.TextBox textBoxDoorIndex; + private System.Windows.Forms.Label labelControlAddress; + private System.Windows.Forms.TextBox textBoxControlAddress; + private System.Windows.Forms.Label labelOpenStatusAddress; + private System.Windows.Forms.TextBox textBoxOpenStatusAddress; + private System.Windows.Forms.Button btnAddDoor; + private System.Windows.Forms.Button btnDeleteDoor; + private System.Windows.Forms.Button btnSaveDoor; + private System.Windows.Forms.Label labelTitle; + private System.Windows.Forms.CheckBox checkBoxNoControl; + } +} diff --git a/StandardScene.Core/ExtendDevice/Door/DoorManager.cs b/StandardScene.Core/ExtendDevice/Door/DoorManager.cs new file mode 100644 index 0000000..bc939b8 --- /dev/null +++ b/StandardScene.Core/ExtendDevice/Door/DoorManager.cs @@ -0,0 +1,961 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Drawing; +using System.IO; +using System.Linq; +using System.Net; +using System.Reflection; +using System.Text; +using System.Text.RegularExpressions; +using System.Windows.Forms; +using StandardScene.Utils; + +namespace StandardScene.ExtendDevice.Door +{ + public partial class DoorManager : Form + { + private static DoorManager _instance = null; + private static readonly object _lock = new object(); + + private const string DataFileName = "DoorConfig.json"; + private string _dataFilePath; + + private List _doorControllers = new List(); + private DoorControllerModel _currentController = null; + private DoorModel _currentDoor = null; + + private int _controllerHoverIndex = -1; + private int _doorHoverIndex = -1; + + /// + /// 鑾峰彇鍗曚緥瀹炰緥 + /// + public static DoorManager Instance + { + get + { + if (_instance == null || _instance.IsDisposed) + { + lock (_lock) + { + if (_instance == null || _instance.IsDisposed) + { + _instance = new DoorManager(); + } + } + } + return _instance; + } + } + + /// + /// 绉佹湁鏋勯犲嚱鏁帮紝纭繚鍗曚緥妯″紡 + /// + private DoorManager() + { + InitializeComponent(); + // 璁剧疆鏁版嵁鏂囦欢璺緞 + _dataFilePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, DataFileName); + } + + private void DoorManager_Load(object sender, EventArgs e) + { + // 璁剧疆ListView鐨勮瑙夋牱寮 + SetupListViewStyles(); + + // 鍒濆鍖栫被鍨嬩笅鎷夋 + InitializeTypeComboBox(); + + LoadData(); + RefreshControllerList(); + } + + /// + /// 鍒濆鍖栫被鍨嬩笅鎷夋锛屾樉绀 DoorTypeAttribute.Name + /// + private void InitializeTypeComboBox() + { + comboBoxType.Items.Clear(); + + try + { + // 鑾峰彇褰撳墠鍛藉悕绌洪棿涓嬫墍鏈夌户鎵胯嚜BasicDoorController涓斿甫鏈塂oorTypeAttribute鐗规х殑绫 + // 鍦ㄦ彃浠/瀹夸富鐜涓 GetExecutingAssembly 鍙兘涓嶆槸 StandardScene.dll + // 璺ㄧ▼搴忛泦鍙戠幇锛氶棬鎺у埗鍣ㄥ叿浣撶被鍨嬪彲鑳戒綅浜庡崼鏄熸彃浠 dll锛圫tandardScene.Devices.Door锛夈 + var controllerTypes = SimpleLite.Utils.UiTypeDiscovery.AllTypes() + .Where(t => t.IsClass + && !t.IsAbstract + && t.Namespace == typeof(BasicDoorController).Namespace + && t.IsSubclassOf(typeof(BasicDoorController)) + && t.IsDefined(typeof(DoorTypeAttribute), false)) + .ToList(); + + var typeNames = new List(); + foreach (var type in controllerTypes) + { + var attr = type.GetCustomAttribute(); + if (attr != null && !string.IsNullOrWhiteSpace(attr.Name)) + { + typeNames.Add(attr.Name); + } + } + + // 鎸夊悕绉版帓搴 + typeNames.Sort(); + + foreach (var typeName in typeNames) + { + comboBoxType.Items.Add(typeName); + } + + // 濡傛灉娌℃湁鎵惧埌浠讳綍绫诲瀷锛屾坊鍔犻粯璁ら夐」 + if (comboBoxType.Items.Count == 0) + { + comboBoxType.Items.Add("ModbusDoorController"); + } + } + catch (Exception ex) + { + MessageBox.Show($"鍒濆鍖栫被鍨嬩笅鎷夋澶辫触: {ex.Message}", "閿欒", MessageBoxButtons.OK, MessageBoxIcon.Error); + comboBoxType.Items.Add("ModbusDoorController"); + } + } + + /// + /// 璁剧疆ListView鐨勮瑙夋牱寮 + /// + private void SetupListViewStyles() + { + SetupListView(doorControllerListView, + ControllerListView_DrawItem, + ControllerListView_DrawSubItem, + ControllerListView_DrawColumnHeader, + ControllerListView_MouseMove, + ControllerListView_MouseLeave); + + SetupListView(doorListView, + DoorListView_DrawItem, + DoorListView_DrawSubItem, + DoorListView_DrawColumnHeader, + DoorListView_MouseMove, + DoorListView_MouseLeave); + } + + private void SetupListView(ListView listView, + DrawListViewItemEventHandler itemHandler, + DrawListViewSubItemEventHandler subItemHandler, + DrawListViewColumnHeaderEventHandler headerHandler, + MouseEventHandler mouseMoveHandler, + EventHandler mouseLeaveHandler) + { + listView.OwnerDraw = true; + listView.BackColor = Color.White; + listView.DrawItem += itemHandler; + listView.DrawSubItem += subItemHandler; + listView.DrawColumnHeader += headerHandler; + listView.MouseMove += mouseMoveHandler; + listView.MouseLeave += mouseLeaveHandler; + + // 鍚敤鍙岀紦鍐 + typeof(Control)?.GetProperty("DoubleBuffered", BindingFlags.Instance | BindingFlags.NonPublic)? + .SetValue(listView, true, null); + } + + private static readonly Color RowEvenColor = Color.FromArgb(250, 250, 252); + private static readonly Color RowOddColor = Color.White; + private static readonly Color RowHighlightColor = Color.FromArgb(230, 240, 255); + private static readonly Color TextRegularColor = Color.FromArgb(68, 68, 68); + private static readonly Color TextHighlightColor = Color.FromArgb(51, 51, 51); + + private void ControllerListView_MouseMove(object sender, MouseEventArgs e) + { + UpdateHoverIndex(doorControllerListView, e, true); + } + + private void ControllerListView_MouseLeave(object sender, EventArgs e) + { + ResetHoverIndex(doorControllerListView, true); + } + + private void DoorListView_MouseMove(object sender, MouseEventArgs e) + { + UpdateHoverIndex(doorListView, e, false); + } + + private void DoorListView_MouseLeave(object sender, EventArgs e) + { + ResetHoverIndex(doorListView, false); + } + + private void UpdateHoverIndex(ListView listView, MouseEventArgs e, bool isControllerList) + { + var hoveredItem = listView.GetItemAt(e.X, e.Y); + int newIndex = hoveredItem?.Index ?? -1; + + if (isControllerList) + { + if (_controllerHoverIndex != newIndex) + { + _controllerHoverIndex = newIndex; + listView.Invalidate(); + } + } + else + { + if (_doorHoverIndex != newIndex) + { + _doorHoverIndex = newIndex; + listView.Invalidate(); + } + } + } + + private void ResetHoverIndex(ListView listView, bool isControllerList) + { + if (isControllerList) + { + if (_controllerHoverIndex != -1) + { + _controllerHoverIndex = -1; + listView.Invalidate(); + } + } + else + { + if (_doorHoverIndex != -1) + { + _doorHoverIndex = -1; + listView.Invalidate(); + } + } + } + + private void ControllerListView_DrawItem(object sender, DrawListViewItemEventArgs e) + { + var isHighlighted = e.Item.Selected + || e.ItemIndex == _controllerHoverIndex + || (e.State & ListViewItemStates.Focused) != 0; + + var backColor = isHighlighted + ? RowHighlightColor + : (e.ItemIndex % 2 == 0 ? RowEvenColor : RowOddColor); + + using (var brush = new SolidBrush(backColor)) + { + e.Graphics.FillRectangle(brush, e.Bounds); + } + + var textColor = isHighlighted ? TextHighlightColor : TextRegularColor; + + TextRenderer.DrawText(e.Graphics, e.Item.Text, e.Item.Font, e.Bounds, + textColor, + TextFormatFlags.VerticalCenter | TextFormatFlags.Left | TextFormatFlags.EndEllipsis); + + e.DrawFocusRectangle(); + } + + private void ControllerListView_DrawSubItem(object sender, DrawListViewSubItemEventArgs e) + { + var isHighlighted = e.Item.Selected + || e.ItemIndex == _controllerHoverIndex + || (e.ItemState & ListViewItemStates.Focused) != 0; + + var backColor = isHighlighted + ? RowHighlightColor + : (e.ItemIndex % 2 == 0 ? RowEvenColor : RowOddColor); + + using (var brush = new SolidBrush(backColor)) + { + e.Graphics.FillRectangle(brush, e.Bounds); + } + + var textColor = isHighlighted ? TextHighlightColor : TextRegularColor; + + TextRenderer.DrawText(e.Graphics, e.SubItem.Text, e.SubItem.Font, e.Bounds, + textColor, + TextFormatFlags.VerticalCenter | TextFormatFlags.Left | TextFormatFlags.EndEllipsis); + } + + private void ControllerListView_DrawColumnHeader(object sender, DrawListViewColumnHeaderEventArgs e) + { + e.Graphics.FillRectangle(new SolidBrush(Color.FromArgb(245, 247, 250)), e.Bounds); + + e.Graphics.DrawLine(new Pen(Color.FromArgb(220, 220, 220)), + e.Bounds.Left, e.Bounds.Bottom - 1, e.Bounds.Right, e.Bounds.Bottom - 1); + + TextRenderer.DrawText(e.Graphics, e.Header.Text, + new Font("寰蒋闆呴粦", 10.5F, FontStyle.Bold), + e.Bounds, Color.FromArgb(68, 68, 68), + TextFormatFlags.VerticalCenter | TextFormatFlags.Left | TextFormatFlags.HorizontalCenter); + } + + private void DoorListView_DrawItem(object sender, DrawListViewItemEventArgs e) + { + var isHighlighted = e.Item.Selected + || e.ItemIndex == _doorHoverIndex + || (e.State & ListViewItemStates.Focused) != 0; + + var backColor = isHighlighted + ? RowHighlightColor + : (e.ItemIndex % 2 == 0 ? RowEvenColor : RowOddColor); + + using (var brush = new SolidBrush(backColor)) + { + e.Graphics.FillRectangle(brush, e.Bounds); + } + + var textColor = isHighlighted ? TextHighlightColor : TextRegularColor; + + TextRenderer.DrawText(e.Graphics, e.Item.Text, e.Item.Font, e.Bounds, + textColor, + TextFormatFlags.VerticalCenter | TextFormatFlags.Left | TextFormatFlags.EndEllipsis); + + e.DrawFocusRectangle(); + } + + private void DoorListView_DrawSubItem(object sender, DrawListViewSubItemEventArgs e) + { + var isHighlighted = e.Item.Selected + || e.ItemIndex == _doorHoverIndex + || (e.ItemState & ListViewItemStates.Focused) != 0; + + var backColor = isHighlighted + ? RowHighlightColor + : (e.ItemIndex % 2 == 0 ? RowEvenColor : RowOddColor); + + using (var brush = new SolidBrush(backColor)) + { + e.Graphics.FillRectangle(brush, e.Bounds); + } + + var textColor = isHighlighted ? TextHighlightColor : TextRegularColor; + + TextRenderer.DrawText(e.Graphics, e.SubItem.Text, e.SubItem.Font, e.Bounds, + textColor, + TextFormatFlags.VerticalCenter | TextFormatFlags.Left | TextFormatFlags.EndEllipsis); + } + + private void DoorListView_DrawColumnHeader(object sender, DrawListViewColumnHeaderEventArgs e) + { + e.Graphics.FillRectangle(new SolidBrush(Color.FromArgb(245, 247, 250)), e.Bounds); + + e.Graphics.DrawLine(new Pen(Color.FromArgb(220, 220, 220)), + e.Bounds.Left, e.Bounds.Bottom - 1, e.Bounds.Right, e.Bounds.Bottom - 1); + + TextRenderer.DrawText(e.Graphics, e.Header.Text, + new Font("寰蒋闆呴粦", 10.5F, FontStyle.Bold), + e.Bounds, Color.FromArgb(68, 68, 68), + TextFormatFlags.VerticalCenter | TextFormatFlags.Left | TextFormatFlags.HorizontalCenter); + } + + /// + /// 鍒锋柊闂ㄦ帶鍒跺櫒鍒楄〃 + /// + private void RefreshControllerList() + { + doorControllerListView.Items.Clear(); + foreach (var controller in _doorControllers) + { + var item = new ListViewItem(controller.Index.ToString()); + item.SubItems.Add(controller.Ip); + item.SubItems.Add(controller.Port.ToString()); + item.SubItems.Add(controller.Type); + item.Tag = controller; + item.UseItemStyleForSubItems = false; + doorControllerListView.Items.Add(item); + } + } + + /// + /// 鍒锋柊闂ㄥ垪琛 + /// + private void RefreshDoorList() + { + doorListView.Items.Clear(); + if (_currentController != null) + { + foreach (var door in _currentController.Doors) + { + var item = new ListViewItem(door.Index.ToString()); + item.SubItems.Add(door.ControlAddress.ToString()); + item.SubItems.Add(door.OpenStatusAddress.ToString()); + item.Tag = door; + item.UseItemStyleForSubItems = false; + doorListView.Items.Add(item); + } + } + } + + /// + /// 闂ㄦ帶鍒跺櫒鍒楄〃閫夋嫨鏀瑰彉 + /// + private void doorControllerListView_SelectedIndexChanged(object sender, EventArgs e) + { + if (doorControllerListView.SelectedItems.Count > 0) + { + _currentController = doorControllerListView.SelectedItems[0].Tag as DoorControllerModel; + if (_currentController != null) + { + // 濉厖闂ㄦ帶鍒跺櫒缂栬緫鍖哄煙 + textBoxIp.Text = _currentController.Ip; + textBoxPort.Text = _currentController.Port.ToString(); + textBoxControllerIndex.Text = _currentController.Index.ToString(); + // 璁剧疆绫诲瀷涓嬫媺妗 + if (comboBoxType.Items.Contains(_currentController.Type)) + { + comboBoxType.SelectedItem = _currentController.Type; + } + else + { + comboBoxType.SelectedIndex = comboBoxType.Items.Count > 0 ? 0 : -1; + } + + // 鍒锋柊闂ㄥ垪琛 + RefreshDoorList(); + } + } + else + { + _currentController = null; + ClearControllerFields(); + doorListView.Items.Clear(); + } + } + + /// + /// 闂ㄥ垪琛ㄩ夋嫨鏀瑰彉 + /// + private void doorListView_SelectedIndexChanged(object sender, EventArgs e) + { + if (doorListView.SelectedItems.Count > 0) + { + _currentDoor = doorListView.SelectedItems[0].Tag as DoorModel; + if (_currentDoor != null) + { + // 濉厖闂ㄧ紪杈戝尯鍩 + textBoxDoorIndex.Text = _currentDoor.Index.ToString(); + textBoxControlAddress.Text = _currentDoor.ControlAddress.ToString(); + textBoxOpenStatusAddress.Text = _currentDoor.OpenStatusAddress.ToString(); + checkBoxNoControl.Checked = _currentDoor.NoControl; + } + } + else + { + _currentDoor = null; + ClearDoorFields(); + } + } + + /// + /// 娣诲姞闂ㄦ帶鍒跺櫒 + /// + private void btnAddController_Click(object sender, EventArgs e) + { + try + { + string ip = textBoxIp.Text.Trim(); + string portText = textBoxPort.Text.Trim(); + string indexText = textBoxControllerIndex.Text.Trim(); + string type = comboBoxType.SelectedItem?.ToString() ?? string.Empty; + + int newIndex; + if (!string.IsNullOrWhiteSpace(indexText)) + { + if (!int.TryParse(indexText, out newIndex)) + { + MessageBox.Show("缂栫爜蹇呴』鏄暟瀛", "閿欒", MessageBoxButtons.OK, MessageBoxIcon.Error); + return; + } + } + else + { + newIndex = _doorControllers.Count > 0 ? _doorControllers.Max(c => c.Index) + 1 : 1; + } + + string newIp; + if (!string.IsNullOrWhiteSpace(ip)) + { + if (!IsValidIpAddress(ip)) + { + MessageBox.Show("鏃犳晥鐨処P鍦板潃锛屼緥濡傦細192.168.1.100", "閿欒", MessageBoxButtons.OK, MessageBoxIcon.Error); + return; + } + newIp = ip; + } + else + { + newIp = "192.168.1.100"; + } + + int newPort; + if (!string.IsNullOrWhiteSpace(portText)) + { + if (!int.TryParse(portText, out newPort)) + { + MessageBox.Show("绔彛蹇呴』鏄暟瀛", "閿欒", MessageBoxButtons.OK, MessageBoxIcon.Error); + return; + } + } + else + { + newPort = 502; + } + + string newType; + if (!string.IsNullOrWhiteSpace(type)) + { + newType = type; + } + else + { + newType = comboBoxType.Items.Count > 0 ? comboBoxType.Items[0].ToString() : "ModbusDoorController"; + } + + // 妫鏌ョ紪鐮佹槸鍚﹂噸澶 + if (_doorControllers.Any(c => c.Index == newIndex)) + { + MessageBox.Show($"缂栫爜 {newIndex} 宸插瓨鍦紝璇蜂娇鐢ㄥ叾浠栫紪鐮", "閿欒", MessageBoxButtons.OK, MessageBoxIcon.Error); + return; + } + + // 妫鏌P鍦板潃鏄惁閲嶅 + if (_doorControllers.Any(c => c.Ip == newIp)) + { + MessageBox.Show($"IP鍦板潃 {newIp} 宸插瓨鍦紝璇蜂娇鐢ㄥ叾浠朓P鍦板潃", "閿欒", MessageBoxButtons.OK, MessageBoxIcon.Error); + return; + } + + var newController = new DoorControllerModel + { + Index = newIndex, + Ip = newIp, + Port = newPort, + Type = newType + }; + + _doorControllers.Add(newController); + RefreshControllerList(); + SaveData(); + + // 閫変腑鏂版坊鍔犵殑闂ㄦ帶鍒跺櫒 + foreach (ListViewItem item in doorControllerListView.Items) + { + if (item.Tag == newController) + { + item.Selected = true; + item.EnsureVisible(); + break; + } + } + } + catch (Exception ex) + { + MessageBox.Show($"娣诲姞闂ㄦ帶鍒跺櫒澶辫触: {ex.Message}", "閿欒", MessageBoxButtons.OK, MessageBoxIcon.Error); + } + } + + /// + /// 鍒犻櫎闂ㄦ帶鍒跺櫒 + /// + private void btnDeleteController_Click(object sender, EventArgs e) + { + if (_currentController == null) + { + MessageBox.Show("璇烽夋嫨瑕佸垹闄ょ殑闂ㄦ帶鍒跺櫒", "鎻愮ず", MessageBoxButtons.OK, MessageBoxIcon.Information); + return; + } + + var result = MessageBox.Show($"鍒犻櫎缂栫爜涓 {_currentController.Index} 鐨勯棬鎺у埗鍣紵", "纭鍒犻櫎", + MessageBoxButtons.YesNo, MessageBoxIcon.Question); + + if (result == DialogResult.Yes) + { + _doorControllers.Remove(_currentController); + _currentController = null; + ClearControllerFields(); + RefreshControllerList(); + doorListView.Items.Clear(); + SaveData(); + } + } + + /// + /// 淇濆瓨闂ㄦ帶鍒跺櫒 + /// + private void btnSaveController_Click(object sender, EventArgs e) + { + if (_currentController == null) + { + MessageBox.Show("璇烽夋嫨瑕佷繚瀛樼殑闂ㄦ帶鍒跺櫒", "鎻愮ず", MessageBoxButtons.OK, MessageBoxIcon.Information); + return; + } + + try + { + string newIp = textBoxIp.Text.Trim(); + + if (!IsValidIpAddress(newIp)) + { + MessageBox.Show("鏃犳晥鐨処P鍦板潃锛屼緥濡傦細192.168.1.100", "閿欒", MessageBoxButtons.OK, MessageBoxIcon.Error); + return; + } + + if (!int.TryParse(textBoxPort.Text, out int port)) + { + MessageBox.Show("绔彛蹇呴』鏄暟瀛", "閿欒", MessageBoxButtons.OK, MessageBoxIcon.Error); + return; + } + _currentController.Port = port; + + if (!int.TryParse(textBoxControllerIndex.Text, out int index)) + { + MessageBox.Show("缂栫爜蹇呴』鏄暟瀛", "閿欒", MessageBoxButtons.OK, MessageBoxIcon.Error); + return; + } + + // 妫鏌ョ紪鐮佹槸鍚﹂噸澶嶏紙鎺掗櫎褰撳墠椤癸級 + if (_doorControllers.Any(c => c.Index == index && c != _currentController)) + { + MessageBox.Show($"缂栫爜 {index} 宸插瓨鍦紝璇蜂娇鐢ㄥ叾浠栫紪鐮", "閿欒", MessageBoxButtons.OK, MessageBoxIcon.Error); + return; + } + + // 妫鏌P鍦板潃鏄惁閲嶅锛堟帓闄ゅ綋鍓嶉」锛 + if (_doorControllers.Any(c => c.Ip == newIp && c != _currentController)) + { + MessageBox.Show($"IP鍦板潃 {newIp} 宸插瓨鍦紝璇蜂娇鐢ㄥ叾浠朓P鍦板潃", "閿欒", MessageBoxButtons.OK, MessageBoxIcon.Error); + return; + } + + _currentController.Index = index; + _currentController.Type = comboBoxType.SelectedItem?.ToString() ?? string.Empty; + _currentController.Ip = newIp; + + RefreshControllerList(); + SaveData(); + MessageBox.Show("淇濆瓨鎴愬姛", "鎻愮ず", MessageBoxButtons.OK, MessageBoxIcon.Information); + } + catch (Exception ex) + { + MessageBox.Show($"淇濆瓨澶辫触: {ex.Message}", "閿欒", MessageBoxButtons.OK, MessageBoxIcon.Error); + } + } + + /// + /// 娣诲姞闂 + /// + private void btnAddDoor_Click(object sender, EventArgs e) + { + if (_currentController == null) + { + MessageBox.Show("璇峰厛閫夋嫨闂ㄦ帶鍒跺櫒", "鎻愮ず", MessageBoxButtons.OK, MessageBoxIcon.Information); + return; + } + + try + { + string indexText = textBoxDoorIndex.Text.Trim(); + string controlAddressText = textBoxControlAddress.Text.Trim(); + string openStatusAddressText = textBoxOpenStatusAddress.Text.Trim(); + + int newIndex; + if (!string.IsNullOrWhiteSpace(indexText)) + { + if (!int.TryParse(indexText, out newIndex)) + { + MessageBox.Show("闂ㄧ紪鐮佸繀椤绘槸鏁板瓧", "閿欒", MessageBoxButtons.OK, MessageBoxIcon.Error); + return; + } + } + else + { + newIndex = _currentController.Doors.Count > 0 + ? _currentController.Doors.Max(d => d.Index) + 1 + : 1; + } + + // 妫鏌ラ棬缂栫爜鏄惁閲嶅 + if (_currentController.Doors.Any(d => d.Index == newIndex)) + { + MessageBox.Show($"闂ㄧ紪鐮 {newIndex} 宸插瓨鍦紝璇蜂娇鐢ㄥ叾浠栫紪鐮", "閿欒", MessageBoxButtons.OK, MessageBoxIcon.Error); + return; + } + + ushort controlAddress = 0; + if (!string.IsNullOrWhiteSpace(controlAddressText)) + { + if (!ushort.TryParse(controlAddressText, out controlAddress)) + { + MessageBox.Show("鎺у埗鍦板潃蹇呴』鏄0-65535涔嬮棿鐨勬暟瀛", "閿欒", MessageBoxButtons.OK, MessageBoxIcon.Error); + return; + } + } + + ushort openStatusAddress = 0; + if (!string.IsNullOrWhiteSpace(openStatusAddressText)) + { + if (!ushort.TryParse(openStatusAddressText, out openStatusAddress)) + { + MessageBox.Show("寮鍒颁綅鍦板潃蹇呴』鏄0-65535涔嬮棿鐨勬暟瀛", "閿欒", MessageBoxButtons.OK, MessageBoxIcon.Error); + return; + } + } + + var newDoor = new DoorModel + { + Index = newIndex, + ControlAddress = controlAddress, + OpenStatusAddress = openStatusAddress, + NoControl = checkBoxNoControl.Checked + }; + + _currentController.Doors.Add(newDoor); + RefreshDoorList(); + SaveData(); + + // 閫変腑鏂版坊鍔犵殑闂 + foreach (ListViewItem item in doorListView.Items) + { + if (item.Tag == newDoor) + { + item.Selected = true; + item.EnsureVisible(); + break; + } + } + } + catch (Exception ex) + { + MessageBox.Show($"娣诲姞闂ㄥけ璐: {ex.Message}", "閿欒", MessageBoxButtons.OK, MessageBoxIcon.Error); + } + } + + /// + /// 鍒犻櫎闂 + /// + private void btnDeleteDoor_Click(object sender, EventArgs e) + { + if (_currentController == null) + { + MessageBox.Show("璇峰厛閫夋嫨闂ㄦ帶鍒跺櫒", "鎻愮ず", MessageBoxButtons.OK, MessageBoxIcon.Information); + return; + } + + if (_currentDoor == null) + { + MessageBox.Show("璇烽夋嫨瑕佸垹闄ょ殑闂", "鎻愮ず", MessageBoxButtons.OK, MessageBoxIcon.Information); + return; + } + + var result = MessageBox.Show($"鍒犻櫎缂栫爜涓 {_currentDoor.Index} 鐨勯棬锛", "纭鍒犻櫎", + MessageBoxButtons.YesNo, MessageBoxIcon.Question); + + if (result == DialogResult.Yes) + { + _currentController.Doors.Remove(_currentDoor); + _currentDoor = null; + ClearDoorFields(); + RefreshDoorList(); + SaveData(); + } + } + + /// + /// 淇濆瓨闂 + /// + private void btnSaveDoor_Click(object sender, EventArgs e) + { + if (_currentController == null) + { + MessageBox.Show("璇峰厛閫夋嫨闂ㄦ帶鍒跺櫒", "鎻愮ず", MessageBoxButtons.OK, MessageBoxIcon.Information); + return; + } + + if (_currentDoor == null) + { + MessageBox.Show("璇烽夋嫨瑕佷繚瀛樼殑闂", "鎻愮ず", MessageBoxButtons.OK, MessageBoxIcon.Information); + return; + } + + try + { + if (!int.TryParse(textBoxDoorIndex.Text, out int index)) + { + MessageBox.Show("闂ㄧ紪鐮佸繀椤绘槸鏁板瓧", "閿欒", MessageBoxButtons.OK, MessageBoxIcon.Error); + return; + } + + // 妫鏌ラ棬缂栫爜鏄惁閲嶅锛堟帓闄ゅ綋鍓嶉棬锛 + if (_currentController.Doors.Any(d => d.Index == index && d != _currentDoor)) + { + MessageBox.Show($"闂ㄧ紪鐮 {index} 宸插瓨鍦紝璇蜂娇鐢ㄥ叾浠栫紪鐮", "閿欒", MessageBoxButtons.OK, MessageBoxIcon.Error); + return; + } + + if (!ushort.TryParse(textBoxControlAddress.Text, out ushort controlAddress)) + { + MessageBox.Show("鎺у埗鍦板潃蹇呴』鏄0-65535涔嬮棿鐨勬暟瀛", "閿欒", MessageBoxButtons.OK, MessageBoxIcon.Error); + return; + } + + if (!ushort.TryParse(textBoxOpenStatusAddress.Text, out ushort openStatusAddress)) + { + MessageBox.Show("寮鍒颁綅鍦板潃蹇呴』鏄0-65535涔嬮棿鐨勬暟瀛", "閿欒", MessageBoxButtons.OK, MessageBoxIcon.Error); + return; + } + + _currentDoor.Index = index; + _currentDoor.ControlAddress = controlAddress; + _currentDoor.OpenStatusAddress = openStatusAddress; + _currentDoor.NoControl = checkBoxNoControl.Checked; + + RefreshDoorList(); + SaveData(); + MessageBox.Show("淇濆瓨鎴愬姛", "鎻愮ず", MessageBoxButtons.OK, MessageBoxIcon.Information); + } + catch (Exception ex) + { + MessageBox.Show($"淇濆瓨澶辫触: {ex.Message}", "閿欒", MessageBoxButtons.OK, MessageBoxIcon.Error); + } + } + + /// + /// 娓呯┖闂ㄦ帶鍒跺櫒瀛楁 + /// + private void ClearControllerFields() + { + textBoxIp.Text = string.Empty; + textBoxPort.Text = string.Empty; + textBoxControllerIndex.Text = string.Empty; + comboBoxType.SelectedIndex = -1; + } + + /// + /// 娓呯┖闂ㄥ瓧娈 + /// + private void ClearDoorFields() + { + textBoxDoorIndex.Text = string.Empty; + textBoxControlAddress.Text = string.Empty; + textBoxOpenStatusAddress.Text = string.Empty; + checkBoxNoControl.Checked = false; + } + + /// + /// 鍔犺浇鏁版嵁 + /// + private void LoadData() + { + try + { + if (File.Exists(_dataFilePath)) + { + var jsonContent = File.ReadAllText(_dataFilePath, Encoding.UTF8); + if (!string.IsNullOrWhiteSpace(jsonContent)) + { + _doorControllers = jsonContent.JsonTo>(); + if (_doorControllers == null) + { + _doorControllers = new List(); + } + } + else + { + _doorControllers = new List(); + } + } + else + { + _doorControllers = new List(); + } + } + catch (Exception ex) + { + MessageBox.Show($"鍔犺浇鏁版嵁澶辫触: {ex.Message}", "閿欒", MessageBoxButtons.OK, MessageBoxIcon.Error); + _doorControllers = new List(); + } + } + + /// + /// 淇濆瓨鏁版嵁 + /// + private void SaveData() + { + try + { + var jsonContent = _doorControllers.ToJson(); + File.WriteAllText(_dataFilePath, jsonContent, Encoding.UTF8); + } + catch (Exception ex) + { + MessageBox.Show($"淇濆瓨鏁版嵁澶辫触: {ex.Message}", "閿欒", MessageBoxButtons.OK, MessageBoxIcon.Error); + } + } + + /// + /// 楠岃瘉IP鍦板潃鏍煎紡 + /// + private bool IsValidIpAddress(string ipAddress) + { + if (string.IsNullOrWhiteSpace(ipAddress)) + { + return false; + } + + string pattern = @"^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$"; + if (Regex.IsMatch(ipAddress, pattern)) + { + IPAddress address; + return IPAddress.TryParse(ipAddress, out address) && address.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork; + } + + return false; + } + + /// + /// 绐椾綋鍏抽棴浜嬩欢 + /// + private void DoorManager_FormClosing(object sender, FormClosingEventArgs e) + { + if (e.CloseReason == CloseReason.UserClosing) + { + // 鍏抽棴鍓嶄繚瀛樻暟鎹 + SaveData(); + e.Cancel = true; + this.Visible = false; + } + } + + /// + /// 鎵撳紑绠$悊鐣岄潰锛堥潤鎬佹柟娉曪級 + /// + public static void OpenViewer() + { + try + { + var manager = Instance; + + if (manager.Visible) + { + if (manager.WindowState == FormWindowState.Minimized) + { + manager.WindowState = FormWindowState.Normal; + } + manager.Activate(); + manager.BringToFront(); + } + else + { + manager.Show(); + manager.Activate(); + } + } + catch (Exception ex) + { + MessageBox.Show($"鎵撳紑闂ㄦ帶鍒跺櫒绠$悊鐣岄潰澶辫触: {ex.Message}", "閿欒", + MessageBoxButtons.OK, MessageBoxIcon.Error); + } + } + } +} diff --git a/StandardScene.Core/ExtendDevice/Door/DoorManager.resx b/StandardScene.Core/ExtendDevice/Door/DoorManager.resx new file mode 100644 index 0000000..4391a28 --- /dev/null +++ b/StandardScene.Core/ExtendDevice/Door/DoorManager.resx @@ -0,0 +1,64 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 17, 17 + + diff --git a/StandardScene.Core/ExtendDevice/Door/DoorMission.cs b/StandardScene.Core/ExtendDevice/Door/DoorMission.cs new file mode 100644 index 0000000..5a315ae --- /dev/null +++ b/StandardScene.Core/ExtendDevice/Door/DoorMission.cs @@ -0,0 +1,1042 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using LessokajiWeaverUtilities.MagicAttributes; +using LessokajiWeaverUtilities.Utilities; +using Newtonsoft.Json; +using SimpleLite.RCS; +using SimpleLite.RCS.CarTypes; +using SimpleCore; +using SimpleCore.Library; +using SimpleCore.PropType; +using SimpleCore.Traffic; +using StandardScene.Utils; + +namespace StandardScene.ExtendDevice.Door +{ + //todo:2026.1.21 1,闂ㄦ帶鍒跺櫒鐨勯氫俊鍙傛暟閰嶇疆绉诲姩鍒板叿浣撻棬鎺у埗鍣ㄧ被涓鐞嗭紝瀹炵幇瀵逛笉鍚岄氫俊绫诲瀷鐨勬敮鎸 + [MissionType(Name = "闂ㄦ帶杩涚▼")] + [I18N.DocumentTranslation(Name = "DoorMission", locale = "en")] + public class DoorMission : Mission + { + private const string DataFileName = "DoorConfig.json"; + private string _dataFilePath; + + /// + /// 褰撳墠鎵鏈夐棬鎺у埗鍣ㄥ疄渚嬪垪琛 + /// + private List _doorControllers = new List(); + + /// + /// 鐢ㄤ簬绠$悊寮傛寰幆鐨勫彇娑堜护鐗屾簮 + /// + private CancellationTokenSource _cancellationTokenSource; + + /// + /// 淇濆瓨鐩戞帶閰嶇疆涓庣姸鎬佺殑鍚庡彴浠诲姟 + /// + private Task _configTask; + private Task _stateTask; + + /// + /// 鍚屾閿侊紝鐢ㄤ簬淇濇姢闂ㄦ帶鍒跺櫒鍒楄〃鐨勫苟鍙戣闂 + /// + private readonly object _syncLock = new object(); + + /// + /// 闂ㄥ尯鍩熷唴鐨勫皬杞﹁褰曪細(ControllerIndex, DoorIndex) -> List + /// + [JsonIgnore] private readonly Dictionary<(int ControllerIndex, int DoorIndex), List> carsInAreas = new Dictionary<(int ControllerIndex, int DoorIndex), List>(); + + /// + /// 闇瑕佹墦寮鐨勯棬锛(ControllerIndex, DoorIndex) -> bool + /// + [JsonIgnore] private readonly Dictionary<(int ControllerIndex, int DoorIndex), bool> _needOpen = new Dictionary<(int ControllerIndex, int DoorIndex), bool>(); + + /// + /// 鎵嬪姩鎺у埗璇锋眰锛(ControllerIndex, DoorIndex) -> ManualControlRequest + /// + [JsonIgnore] private readonly Dictionary<(int ControllerIndex, int DoorIndex), ManualControlRequest> _manualRequests = new Dictionary<(int ControllerIndex, int DoorIndex), ManualControlRequest>(); + + private const int DefaultManualHoldSeconds = 10; + + public enum ControlSource + { + Auto = 0, + Manual = 1 + } + + private class ManualControlRequest + { + public bool Target { get; set; } + public DateTime ExpireAt { get; set; } + } + + public class DoorControlStatus + { + public bool Target { get; set; } + public ControlSource Source { get; set; } + public double? ManualRemainingSeconds { get; set; } + public IReadOnlyList CarsInArea { get; set; } = Array.Empty(); + } + + /// + /// 鐩戞帶鐣岄潰蹇収椤癸紙绾跨▼瀹夊叏蹇収锛岄伩鍏峌I鐩存帴鏋氫妇鍙彉瀛楀吀锛 + /// + public class DoorMonitorSnapshotItem + { + public int ControllerIndex { get; set; } + public int DoorIndex { get; set; } + public DoorState State { get; set; } + public bool Target { get; set; } + public ControlSource Source { get; set; } + public double? ManualRemainingSeconds { get; set; } + public IReadOnlyList CarsInArea { get; set; } = Array.Empty(); + public ushort ControlAddress { get; set; } + public ushort OpenStatusAddress { get; set; } + } + + [MethodMember(Name = "鍚姩杩涚▼")] + [I18N.DocumentTranslation(Name = "Start Mission", locale = "en")] + public override void Execute() + { + // 璁剧疆鏁版嵁鏂囦欢璺緞 + _dataFilePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, DataFileName); + + // 濡傛灉宸茬粡鍚姩锛屽厛鍋滄涔嬪墠鐨勫惊鐜 + StopInternalAsync().GetAwaiter().GetResult(); + + TrafficControl.BeforeLock += BeforeLockEvent; + TrafficControl.AfterLeave += AfterLeaveEvent; + TrafficControl.OnLockAcquired += OnLockAcquiredEvent; + + // 鍚姩鏃剁珛鍗冲姞杞戒竴娆¢厤缃紝閬垮厤鐩戞帶鐣岄潰鍦ㄧ涓娆¤疆璇㈠墠鏃犳暟鎹 + try + { + var configControllers = LoadDoorControllerConfig(); + SyncDoorControllers(configControllers); + } + catch (Exception ex) + { + Diagnosis.Log($"鍚姩鏃跺姞杞介棬鎺у埗鍣ㄩ厤缃け璐: {ExceptionFormatter.FormatEx(ex)}", "DoorMission", true); + } + + // 鍒涘缓鏂扮殑鍙栨秷浠ょ墝婧 + _cancellationTokenSource = new CancellationTokenSource(); + + // 鍚姩寮傛寰幆 + var token = _cancellationTokenSource.Token; + _configTask = Task.Run(async () => await MonitorDoorConfigAsync(token), token); + _stateTask = Task.Run(async () => await MonitorDoorLogicAsync(token), token); + status.status = "Running"; + } + + /// + /// 鍋滄鐩戞帶浠诲姟 + /// + [MethodMember(Name = "鍋滄杩涚▼")] + [I18N.DocumentTranslation(Name = "Stop Mission", locale = "en")] + public void Stop() + { + StopInternalAsync().GetAwaiter().GetResult(); + status.status = "/"; + } + + /// + /// 鍙栨秷骞堕噴鏀惧綋鍓嶇殑鍙栨秷浠ょ墝婧 + /// + private async Task StopInternalAsync() + { + // 鍙栨秷浜嬩欢璁㈤槄 + TrafficControl.BeforeLock -= BeforeLockEvent; + TrafficControl.AfterLeave -= AfterLeaveEvent; + TrafficControl.OnLockAcquired -= OnLockAcquiredEvent; + + var cts = Interlocked.Exchange(ref _cancellationTokenSource, null); + var configTask = Interlocked.Exchange(ref _configTask, null); + var stateTask = Interlocked.Exchange(ref _stateTask, null); + + if (cts == null && configTask == null && stateTask == null) + { + return; + } + + try + { + cts?.Cancel(); + } + catch (ObjectDisposedException) + { + // 宸查噴鏀撅紝蹇界暐 + } + + var runningTasks = new[] { configTask, stateTask } + .Where(t => t != null) + .ToArray(); + + if (runningTasks.Length > 0) + { + var aggregateTask = Task.WhenAll(runningTasks); + var timeoutTask = Task.Delay(TimeSpan.FromSeconds(5)); + var completedTask = await Task.WhenAny(aggregateTask, timeoutTask).ConfigureAwait(false); + + if (completedTask == timeoutTask) + { + Diagnosis.Log("鍋滄闂ㄦ帶浠诲姟瓒呮椂", "DoorMission", true); + } + else + { + try + { + await aggregateTask.ConfigureAwait(false); + } + catch (Exception ex) + { + Diagnosis.Log($"鍋滄闂ㄦ帶浠诲姟鏃跺彂鐢熷紓甯: {ExceptionFormatter.FormatEx(ex)}", "DoorMission", true); + } + } + } + + cts?.Dispose(); + + DisconnectAllDoorControllers(); + } + + /// + /// 鏂紑鎵鏈夐棬鎺у埗鍣ㄨ繛鎺 + /// + private void DisconnectAllDoorControllers() + { + List snapshot; + lock (_syncLock) + { + snapshot = _doorControllers.ToList(); + } + + foreach (var controller in snapshot) + { + try + { + controller.Disconnect(); + } + catch (Exception ex) + { + Diagnosis.Log($"鍋滄闂ㄦ帶鍒跺櫒澶辫触: Index={controller.Index}, Error={ExceptionFormatter.FormatEx(ex)}", "DoorMission", true); + } + } + } + + /// + /// 寮傛鐩戞帶闂ㄦ帶鍒跺櫒閰嶇疆鏂囦欢 + /// + private async Task MonitorDoorConfigAsync(CancellationToken cancellationToken) + { + while (!cancellationToken.IsCancellationRequested) + { + try + { + // 璇诲彇閰嶇疆鏂囦欢 + var configControllers = LoadDoorControllerConfig(); + + // 鍚屾闂ㄦ帶鍒跺櫒鍒楄〃 + SyncDoorControllers(configControllers); + + // 绛夊緟10绉 + await Task.Delay(TimeSpan.FromSeconds(10), cancellationToken); + } + catch (OperationCanceledException) + { + // 姝e父鍙栨秷锛岄鍑哄惊鐜 + break; + } + catch (Exception ex) + { + Diagnosis.Log($"闂ㄦ帶鍒跺櫒閰嶇疆鐩戞帶閿欒: {ExceptionFormatter.FormatEx(ex)}", "DoorMission", true); + // 鍙戠敓閿欒鏃剁瓑寰5绉掑悗閲嶈瘯 + try + { + await Task.Delay(TimeSpan.FromSeconds(5), cancellationToken); + } + catch (OperationCanceledException) + { + break; + } + } + } + } + + /// + /// 鐩戞帶闂ㄦ帶閫昏緫 + /// + private async Task MonitorDoorLogicAsync(CancellationToken cancellationToken) + { + while (!cancellationToken.IsCancellationRequested) + { + try + { + lock (_syncLock) + { + foreach (var controller in _doorControllers) + { + foreach (var doorConfig in controller.DoorConfigs.Values) + { + var doorIndex = doorConfig.Index; + var key = (controller.Index, doorIndex); + var needOpen = _needOpen.TryGetValue(key, out var open) && open; + + // 鑷姩鐩爣锛氭湁杞︽垨闇瑕佹墦寮 + var hasCarsInArea = carsInAreas.TryGetValue(key, out var cars) && cars.Count > 0; + var autoTarget = needOpen || hasCarsInArea; + + // 鎵嬪姩璇锋眰浠茶锛氫紭鍏堢骇 Manual > Auto锛屾墜鍔ㄨ繃鏈熷悗鑷姩鎭㈠ + bool finalTarget = autoTarget; + if (_manualRequests.TryGetValue(key, out var manual)) + { + if (manual.ExpireAt <= DateTime.Now) + { + _manualRequests.Remove(key); + } + else + { + finalTarget = manual.Target; + } + } + + // 浠呮洿鏂伴棬鎺у埗鍣ㄤ腑鐨勭洰鏍囨帶鍒跺瓧娈碉紝涓嶇洿鎺ヨ繘琛岄氫俊 + controller.SetDoorControlTarget(doorIndex, finalTarget); + } + } + } + + await Task.Delay(TimeSpan.FromMilliseconds(500), cancellationToken); + } + catch (OperationCanceledException) + { + // 姝e父鍙栨秷锛岄鍑哄惊鐜 + break; + } + catch (Exception ex) + { + Diagnosis.Log($"闂ㄦ帶閫昏緫鐩戞帶閿欒: {ExceptionFormatter.FormatEx(ex)}", "DoorMission", true); + // 鍙戠敓閿欒鏃剁瓑寰5绉掑悗閲嶈瘯 + try + { + await Task.Delay(TimeSpan.FromSeconds(5), cancellationToken); + } + catch (OperationCanceledException) + { + break; + } + } + } + } + + /// + /// 鍔犺浇闂ㄦ帶鍒跺櫒閰嶇疆鏂囦欢 + /// + private List LoadDoorControllerConfig() + { + try + { + if (File.Exists(_dataFilePath)) + { + var jsonContent = File.ReadAllText(_dataFilePath, Encoding.UTF8); + if (!string.IsNullOrWhiteSpace(jsonContent)) + { + var controllers = jsonContent.JsonTo>(); + return controllers ?? new List(); + } + } + } + catch (Exception ex) + { + Diagnosis.Log($"鍔犺浇闂ㄦ帶鍒跺櫒閰嶇疆鏂囦欢澶辫触: {ExceptionFormatter.FormatEx(ex)}", "DoorMission", true); + } + + return new List(); + } + + /// + /// 鍚屾闂ㄦ帶鍒跺櫒鍒楄〃锛屾牴鎹厤缃枃浠惰繘琛屽鍒犳敼 + /// + private void SyncDoorControllers(List configControllers) + { + var controllersToAdd = new List(); + + lock (_syncLock) + { + // 鍒涘缓閰嶇疆涓殑闂ㄦ帶鍒跺櫒绱㈠紩瀛楀吀 + var configDict = configControllers.ToDictionary(c => c.Index); + + // 鍒涘缓褰撳墠闂ㄦ帶鍒跺櫒绱㈠紩瀛楀吀 + var currentDict = _doorControllers.ToDictionary(c => c.Index); + + // 1. 鍒犻櫎锛氬湪閰嶇疆涓笉瀛樺湪鐨勯棬鎺у埗鍣 + var toRemove = _doorControllers.Where(c => !configDict.ContainsKey(c.Index)).ToList(); + foreach (var controller in toRemove) + { + try + { + controller.Disconnect(); + _doorControllers.Remove(controller); + Diagnosis.Post($"鍒犻櫎闂ㄦ帶鍒跺櫒: Index={controller.Index}, IP={controller.Ip}", "DoorMission", true); + } + catch (Exception ex) + { + Diagnosis.Log($"鍒犻櫎闂ㄦ帶鍒跺櫒澶辫触: {ExceptionFormatter.FormatEx(ex)}", "DoorMission", true); + } + } + + // 2. 娣诲姞鍜屼慨鏀癸細閬嶅巻閰嶇疆涓殑闂ㄦ帶鍒跺櫒 + foreach (var configController in configControllers) + { + if (currentDict.TryGetValue(configController.Index, out var existingController)) + { + // 淇敼锛氭鏌ユ槸鍚﹂渶瑕佹洿鏂 + if (ShouldUpdateDoorController(existingController, configController)) + { + try + { + UpdateDoorController(existingController, configController); + Diagnosis.Post($"鏇存柊闂ㄦ帶鍒跺櫒: Index={configController.Index}, IP={configController.Ip}, Type={configController.Type}", "DoorMission", true); + } + catch (Exception ex) + { + Diagnosis.Log($"鏇存柊闂ㄦ帶鍒跺櫒澶辫触: {ExceptionFormatter.FormatEx(ex)}", "DoorMission", true); + } + } + } + else + { + controllersToAdd.Add(configController); + } + } + } + + foreach (var configController in controllersToAdd) + { + try + { + var newController = CreateDoorControllerInstance(configController); + if (newController != null) + { + lock (_syncLock) + { + _doorControllers.Add(newController); + } + Diagnosis.Post($"娣诲姞闂ㄦ帶鍒跺櫒: Index={configController.Index}, IP={configController.Ip}, Type={configController.Type}", "DoorMission", true); + } + else + { + Diagnosis.Log($"鏃犳硶鍒涘缓闂ㄦ帶鍒跺櫒瀹炰緥: Index={configController.Index}, Type={configController.Type}", "DoorMission", true); + } + } + catch (Exception ex) + { + Diagnosis.Log($"娣诲姞闂ㄦ帶鍒跺櫒澶辫触: {ExceptionFormatter.FormatEx(ex)}", "DoorMission", true); + } + } + } + + /// + /// 鑾峰彇闂ㄦ帶鍒跺櫒绫诲瀷鐨 DoorTypeAttribute.Name + /// + private string GetDoorTypeName(Type controllerType) + { + if (controllerType == null) + { + return string.Empty; + } + + var attr = controllerType.GetCustomAttribute(); + return attr?.Name ?? controllerType.Name; + } + + /// + /// 鍒ゆ柇鏄惁闇瑕佹洿鏂伴棬鎺у埗鍣 + /// + private bool ShouldUpdateDoorController(BasicDoorController existingController, DoorControllerModel configController) + { + // 妫鏌ュ熀鏈睘鎬ф槸鍚﹀彉鏇 + var existingTypeName = GetDoorTypeName(existingController.GetType()); + if (existingController.Ip != configController.Ip + || existingController.Port != configController.Port + || existingTypeName != configController.Type) + { + return true; + } + + // 妫鏌ラ棬閰嶇疆淇℃伅鏄惁鍙樻洿 + return HasDoorConfigsChanged(existingController, configController); + } + + /// + /// 妫鏌ラ棬閰嶇疆淇℃伅鏄惁鍙樻洿 + /// + private bool HasDoorConfigsChanged(BasicDoorController existingController, DoorControllerModel configController) + { + var configDoors = configController.Doors ?? new List(); + var configDict = configDoors.ToDictionary(d => d.Index); + var existingDict = existingController.DoorConfigs; + + // 妫鏌ラ棬鏁伴噺鏄惁鍙樺寲 + if (existingDict.Count != configDict.Count) + { + return true; + } + + // 妫鏌ユ瘡涓棬鐨勯厤缃槸鍚﹀彉鍖 + foreach (var configDoor in configDoors) + { + if (!existingDict.TryGetValue(configDoor.Index, out var existingDoor)) + { + // 鏂板浜嗛棬 + return true; + } + + // 妫鏌ラ棬閰嶇疆鏄惁鍙樺寲 + if (existingDoor.ControlAddress != configDoor.ControlAddress + || existingDoor.OpenStatusAddress != configDoor.OpenStatusAddress) + { + return true; + } + } + + // 妫鏌ユ槸鍚︽湁闂ㄨ鍒犻櫎 + foreach (var existingKey in existingDict.Keys) + { + if (!configDict.ContainsKey(existingKey)) + { + return true; + } + } + + return false; + } + + /// + /// 鏇存柊闂ㄦ帶鍒跺櫒灞炴 + /// + private void UpdateDoorController(BasicDoorController controller, DoorControllerModel configController) + { + // 濡傛灉绫诲瀷鏀瑰彉锛岄渶瑕侀噸鏂板垱寤哄疄渚 + var existingTypeName = GetDoorTypeName(controller.GetType()); + if (existingTypeName != configController.Type) + { + // 鏂紑鏃ц繛鎺 + controller.Disconnect(); + + // 浠庡垪琛ㄤ腑绉婚櫎 + _doorControllers.Remove(controller); + + // 鍒涘缓鏂板疄渚 + var newController = CreateDoorControllerInstance(configController); + if (newController != null) + { + _doorControllers.Add(newController); + } + } + else + { + // 鍙洿鏂板睘鎬 + bool needReconnect = controller.Ip != configController.Ip || controller.Port != configController.Port; + controller.Ip = configController.Ip; + controller.Port = configController.Port; + + // 鏇存柊闂ㄩ厤缃俊鎭 + controller.UpdateDoorConfigs(configController.Doors); + + // 鍒濆鍖栭棬鐘舵侊紙鍩轰簬閰嶇疆涓殑闂ㄧ储寮曪級 + var doorIndices = configController.Doors?.Select(d => d.Index).ToList() ?? new List(); + controller.InitializeDoors(doorIndices); + + // 濡傛灉IP鎴栫鍙f敼鍙橈紝闇瑕侀噸鏂拌繛鎺 + if (needReconnect) + { + controller.Disconnect(); + controller.Connect(); + } + } + } + + /// + /// 鏍规嵁 DoorTypeAttribute.Name 鑾峰彇绫诲瀷 + /// + private Type GetControllerTypeByDoorTypeName(string doorTypeName) + { + if (string.IsNullOrWhiteSpace(doorTypeName)) + { + return null; + } + + try + { + // 鍦ㄦ彃浠/瀹夸富鐜涓 GetExecutingAssembly 鍙兘涓嶆槸 StandardScene.dll + // 杩欓噷鍥哄畾浠庨棬鎺у埗鍣ㄥ熀绫绘墍鍦ㄧ▼搴忛泦鏌ユ壘 + // 璺ㄧ▼搴忛泦鍙戠幇锛氶棬鎺у埗鍣ㄥ叿浣撶被鍨嬪彲鑳戒綅浜庡崼鏄熸彃浠 dll锛圫tandardScene.Devices.Door锛夛紝 + // 鐢ㄥ唴鏍稿悓娆惧叏鍩熺被鍨嬪彂鐜版浛浠d粎鎵熀绫绘墍鍦ㄧ▼搴忛泦銆 + var allControllerTypes = SimpleLite.Utils.UiTypeDiscovery.AllTypes() + .Where(t => t.IsClass + && !t.IsAbstract + && t.Namespace == typeof(BasicDoorController).Namespace + && t.IsSubclassOf(typeof(BasicDoorController)) + && t.IsDefined(typeof(DoorTypeAttribute), false)); + + foreach (var type in allControllerTypes) + { + var attr = type.GetCustomAttribute(); + if (attr != null && attr.Name == doorTypeName) + { + return type; + } + } + + return null; + } + catch (Exception ex) + { + Diagnosis.Log($"鑾峰彇闂ㄦ帶鍒跺櫒绫诲瀷澶辫触: DoorTypeName={doorTypeName}, Error={ExceptionFormatter.FormatEx(ex)}", "DoorMission", true); + return null; + } + } + + /// + /// 閫氳繃 DoorTypeAttribute.Name 鍒涘缓闂ㄦ帶鍒跺櫒瀹炰緥 + /// + private BasicDoorController CreateDoorControllerInstance(DoorControllerModel configController) + { + if (string.IsNullOrWhiteSpace(configController.Type)) + { + return null; + } + + try + { + // 閫氳繃 DoorTypeAttribute.Name 鏌ユ壘绫诲瀷 + var controllerType = GetControllerTypeByDoorTypeName(configController.Type); + + if (controllerType == null) + { + Diagnosis.Log($"鏈壘鍒伴棬鎺у埗鍣ㄧ被鍨: {configController.Type}", "DoorMission", true); + return null; + } + + // 浣跨敤鍙嶅皠鍒涘缓瀹炰緥 + var instance = (BasicDoorController)Activator.CreateInstance(controllerType); + + // 璁剧疆灞炴 + instance.Index = configController.Index; + instance.Ip = configController.Ip; + instance.Port = configController.Port; + + // 鍒濆鍖栭棬閰嶇疆淇℃伅 + instance.InitializeDoorConfigs(configController.Doors); + + // 鍒濆鍖栭棬鐘舵侊紙鍩轰簬閰嶇疆涓殑闂ㄧ储寮曪級 + var doorIndices = configController.Doors?.Select(d => d.Index).ToList() ?? new List(); + instance.InitializeDoors(doorIndices); + + // 鑷姩杩炴帴 + instance.Connect(); + + return instance; + } + catch (Exception ex) + { + Diagnosis.Log($"鍒涘缓闂ㄦ帶鍒跺櫒瀹炰緥澶辫触: Type={configController.Type}, Error={ExceptionFormatter.FormatEx(ex)}", "DoorMission", true); + return null; + } + } + + /// + /// 鑾峰彇褰撳墠鎵鏈夐棬鎺у埗鍣ㄥ疄渚嬶紙鍙锛 + /// + public IReadOnlyList GetDoorControllers() + { + lock (_syncLock) + { + return _doorControllers.ToList().AsReadOnly(); + } + } + + /// + /// 鑾峰彇鐢ㄤ簬鐩戞帶鐣岄潰鐨勯棬鐘舵佸揩鐓 + /// + public IReadOnlyList GetDoorMonitorSnapshot() + { + lock (_syncLock) + { + var snapshot = new List(); + var now = DateTime.Now; + + foreach (var controller in _doorControllers) + { + var doorConfigs = controller.DoorConfigs?.Values?.ToList() ?? new List(); + foreach (var doorConfig in doorConfigs) + { + var key = (controller.Index, doorConfig.Index); + var cars = carsInAreas.TryGetValue(key, out var list) && list != null + ? list.ToList().AsReadOnly() + : new List().AsReadOnly(); + + var source = ControlSource.Auto; + double? manualRemaining = null; + bool target = false; + + if (_manualRequests.TryGetValue(key, out var manual)) + { + if (manual.ExpireAt > now) + { + source = ControlSource.Manual; + target = manual.Target; + manualRemaining = (manual.ExpireAt - now).TotalSeconds; + } + else + { + _manualRequests.Remove(key); + } + } + + if (source == ControlSource.Auto) + { + var needOpen = _needOpen.TryGetValue(key, out var open) && open; + target = needOpen || cars.Count > 0; + } + + snapshot.Add(new DoorMonitorSnapshotItem + { + ControllerIndex = controller.Index, + DoorIndex = doorConfig.Index, + State = controller.GetDoorState(doorConfig.Index), + Target = target, + Source = source, + ManualRemainingSeconds = manualRemaining, + CarsInArea = cars, + ControlAddress = doorConfig.ControlAddress, + OpenStatusAddress = doorConfig.OpenStatusAddress + }); + } + } + + return snapshot.AsReadOnly(); + } + } + + /// + /// 瑙f瀽闂ㄦ爣璇嗙瀛楃涓诧紙鏍煎紡锛氭帶鍒跺櫒绱㈠紩.闂ㄧ储寮曪級 + /// + /// 闂ㄦ爣璇嗙瀛楃涓诧紝鏍煎紡锛氭帶鍒跺櫒绱㈠紩.闂ㄧ储寮 + /// 濡傛灉瑙f瀽鎴愬姛杩斿洖(鎺у埗鍣ㄧ储寮, 闂ㄧ储寮)锛屽惁鍒欒繑鍥瀗ull + private (int ControllerIndex, int DoorIndex)? ParseDoorIdentifier(string doorIdentifier) + { + if (string.IsNullOrWhiteSpace(doorIdentifier)) + { + return null; + } + + var parts = doorIdentifier.Split('.'); + if (parts.Length != 2) + { + return null; + } + + if (int.TryParse(parts[0].Trim(), out int controllerIndex) && + int.TryParse(parts[1].Trim(), out int doorIndex)) + { + return (controllerIndex, doorIndex); + } + + return null; + } + + /// + /// 璇诲彇闂ㄧ姸鎬 + /// + /// 闂ㄦ帶鍒跺櫒绱㈠紩 + /// 闂ㄧ储寮 + /// true=鎵撳紑锛宖alse=鍏抽棴 + public bool GetDoorState(int controllerIndex, int doorIndex) + { + lock (_syncLock) + { + var controller = _doorControllers.FirstOrDefault(c => c.Index == controllerIndex); + if (controller != null && controller.DoorConfigs.ContainsKey(doorIndex)) + { + // 鍙闂帶鍒跺櫒涓殑闂ㄧ姸鎬佸瓧娈碉紝涓嶇洿鎺ヨ繘琛岄氫俊 + var state = controller.GetDoorState(doorIndex); + return state == DoorState.Open; + } + } + return false; + } + + /// + /// 鍐欏叆闂ㄦ帶鍒剁姸鎬 + /// + /// 闂ㄦ帶鍒跺櫒绱㈠紩 + /// 闂ㄧ储寮 + /// true=鎵撳紑锛宖alse=鍏抽棴 + public void SetDoorControlTarget(int controllerIndex, int doorIndex, bool open) + { + lock (_syncLock) + { + var controller = _doorControllers.FirstOrDefault(c => c.Index == controllerIndex); + if (controller != null && controller.DoorConfigs.ContainsKey(doorIndex)) + { + // 浠呰缃棬鐨勭洰鏍囨帶鍒剁姸鎬侊紝鐢遍棬鎺у埗鍣ㄥ唴閮ㄧ嚎绋嬭礋璐e疄闄呴氫俊 + controller.SetDoorControlTarget(doorIndex, open); + } + } + } + + /// + /// 璁剧疆鎵嬪姩鎺у埗鐩爣锛屾敮鎸佷繚鎸佹椂闂达紝杞﹁締鍗犵敤鏃剁姝㈡墜鍔ㄥ叧闂 + /// + /// 鎺у埗鍣ㄧ储寮 + /// 闂ㄧ储寮 + /// 鐩爣寮鍏 + /// 鎵嬪姩淇濇寔绉掓暟锛岄粯璁10绉 + /// 鎴愬姛杩斿洖true锛涘綋鍗犵敤涓斿皾璇曞叧闂椂杩斿洖false + public bool SetManualDoorControl(int controllerIndex, int doorIndex, bool open, int? holdSeconds = null) + { + lock (_syncLock) + { + // 杞﹁締鍗犵敤鏃剁姝㈡墜鍔ㄥ叧闂 + var key = (controllerIndex, doorIndex); + if (!open && carsInAreas.TryGetValue(key, out var cars) && cars.Count > 0) + { + return false; + } + + var seconds = holdSeconds.GetValueOrDefault(DefaultManualHoldSeconds); + _manualRequests[key] = new ManualControlRequest + { + Target = open, + ExpireAt = DateTime.Now.AddSeconds(seconds) + }; + return true; + } + } + + /// + /// 娓呴櫎鎵嬪姩鎺у埗锛屾仮澶嶈嚜鍔 + /// + public void ClearManualDoorControl(int controllerIndex, int doorIndex) + { + lock (_syncLock) + { + var key = (controllerIndex, doorIndex); + _manualRequests.Remove(key); + } + } + + /// + /// 娓呯┖杞﹁締鍗犵敤璁板綍 + /// + public void ClearCarsInArea(int controllerIndex, int doorIndex) + { + lock (_syncLock) + { + var key = (controllerIndex, doorIndex); + if (carsInAreas.ContainsKey(key)) + { + carsInAreas[key].Clear(); + } + } + } + + /// + /// 鑾峰彇闂ㄧ殑鎺у埗鐘舵侊紙鐩爣銆佹潵婧愩佹墜鍔ㄥ墿浣欐椂闂淬佸崰鐢ㄨ溅杈嗭級 + /// + public DoorControlStatus GetDoorControlStatus(int controllerIndex, int doorIndex) + { + lock (_syncLock) + { + var key = (controllerIndex, doorIndex); + + // 杞﹁締鍗犵敤 + IReadOnlyList cars; + if (carsInAreas.TryGetValue(key, out var list) && list != null) + { + cars = list.ToList().AsReadOnly(); + } + else + { + cars = new List().AsReadOnly(); + } + + // 褰撳墠鐩爣鏉ユ簮涓庡墿浣 + var source = ControlSource.Auto; + double? remaining = null; + bool target = false; + + if (_manualRequests.TryGetValue(key, out var manual)) + { + if (manual.ExpireAt > DateTime.Now) + { + source = ControlSource.Manual; + target = manual.Target; + remaining = (manual.ExpireAt - DateTime.Now).TotalSeconds; + } + else + { + _manualRequests.Remove(key); + } + } + + if (source == ControlSource.Auto) + { + var needOpen = _needOpen.TryGetValue(key, out var open) && open; + var hasCars = cars.Count > 0; + target = needOpen || hasCars; + } + + return new DoorControlStatus + { + Target = target, + Source = source, + ManualRemainingSeconds = remaining, + CarsInArea = cars + }; + } + } + + /// + /// 鑾峰彇闂ㄧ殑杞﹁締鍗犵敤鎯呭喌 + /// + /// 闂ㄦ帶鍒跺櫒绱㈠紩 + /// 闂ㄧ储寮 + /// 杞﹁締ID鍒楄〃锛屽鏋滄病鏈夎溅杈嗗垯杩斿洖绌哄垪琛 + public IReadOnlyList GetCarsInArea(int controllerIndex, int doorIndex) + { + lock (_syncLock) + { + var key = (controllerIndex, doorIndex); + if (carsInAreas.TryGetValue(key, out var cars) && cars != null) + { + return cars.ToList().AsReadOnly(); + } + return new List().AsReadOnly(); + } + } + + private bool BeforeLockEvent(AbstractCar car, int siteId) + { + var site = SimpleLib.GetSite(siteId); + + if (!site.fields.TryGetValue("EnterDoor", out string fieldStr)) return true; + var lastSiteId = car.status.holdingLocks.LastOrDefault(); + var lastSite = SimpleLib.GetSite(lastSiteId); + if (lastSite == null || !lastSite.fields.TryGetValue("PreEnterDoor",out var value)) return true; + if (value != fieldStr) return true; + var doorInfo = ParseDoorIdentifier(fieldStr); + if (!doorInfo.HasValue) return true; + var (controllerIndex, doorIndex) = doorInfo.Value; + lock (_syncLock) + { + _needOpen[(controllerIndex, doorIndex)] = true; + return GetDoorState(controllerIndex, doorIndex); + } + } + + private void AfterLeaveEvent(AbstractCar car, int siteId) + { + var site = SimpleLib.GetSite(siteId); + + if (!site.fields.TryGetValue("LeaveDoor", out string fieldStr)) return; + var carSiteId = car.status.holdingLocks.FirstOrDefault(); + var carSite = SimpleLib.GetSite(carSiteId); + if (carSite == null || !carSite.fields.TryGetValue("RearLeaveDoor",out var value)) return; + if (value != fieldStr) return; + var doorInfo = ParseDoorIdentifier(fieldStr); + if (!doorInfo.HasValue) return; + var (controllerIndex, doorIndex) = doorInfo.Value; + var key = (controllerIndex, doorIndex); + lock (_syncLock) + { + if (carsInAreas.TryGetValue(key, out var list)) + { + list.Remove(car.id); + } + } + } + + private void OnLockAcquiredEvent(AbstractCar car, int siteId) + { + var site = SimpleLib.GetSite(siteId); + if (!site.fields.TryGetValue("EnterDoor", out string fieldStr)) return; + if(car.status.holdingLocks.Length<2) return; + var preSiteId = car.status.holdingLocks[car.status.holdingLocks.Length-2]; + var preSite = SimpleLib.GetSite(preSiteId); + if (preSite == null || !preSite.fields.TryGetValue("PreEnterDoor", out var value)) return ; + if (value != fieldStr) return ; + var doorInfo = ParseDoorIdentifier(fieldStr); + if (!doorInfo.HasValue) return; + var (controllerIndex, doorIndex) = doorInfo.Value; + var key = (controllerIndex, doorIndex); + lock (_syncLock) + { + if (!carsInAreas.TryGetValue(key, out var list)) + { + list = new List(); + carsInAreas[key] = list; + } + if (!list.Contains(car.id)) + { + list.Add(car.id); + } + _needOpen[key] = false; + } + } + + /// + /// 鎵撳紑闂ㄦ帶鍒跺櫒绠$悊鐣岄潰 + /// + [MethodMember(Name = "鎵撳紑绠$悊鐣岄潰")] + [I18N.DocumentTranslation(Name = "Open Manager", locale = "en")] + public static void OpenViewer() + { + DoorManager.OpenViewer(); + } + + /// + /// 鎵撳紑闂ㄦ帶鐩戞帶鐣岄潰 + /// + [MethodMember(Name = "鎵撳紑鐩戞帶鐣岄潰")] + [I18N.DocumentTranslation(Name = "Open Monitor", locale = "en")] + public static void OpenMonitor() + { + try + { + var monitor = DoorMonitor.Instance; + + if (monitor.Visible) + { + if (monitor.WindowState == System.Windows.Forms.FormWindowState.Minimized) + { + monitor.WindowState = System.Windows.Forms.FormWindowState.Normal; + } + monitor.Activate(); + monitor.BringToFront(); + } + else + { + monitor.Show(); + monitor.Activate(); + } + + monitor.EnsureRefreshActive(); + } + catch (Exception ex) + { + System.Windows.Forms.MessageBox.Show($"鎵撳紑闂ㄦ帶鐩戞帶鐣岄潰澶辫触: {ex.Message}", "閿欒", + System.Windows.Forms.MessageBoxButtons.OK, System.Windows.Forms.MessageBoxIcon.Error); + } + } + } +} diff --git a/StandardScene.Core/ExtendDevice/Door/DoorModel.cs b/StandardScene.Core/ExtendDevice/Door/DoorModel.cs new file mode 100644 index 0000000..5732eac --- /dev/null +++ b/StandardScene.Core/ExtendDevice/Door/DoorModel.cs @@ -0,0 +1,63 @@ +using System; +using System.Collections.Generic; + +namespace StandardScene.ExtendDevice.Door +{ + /// + /// 闂ㄦ帶鍒跺櫒妯″瀷 + /// + public class DoorControllerModel + { + /// + /// 鎺у埗鍣ㄧ储寮 + /// + public int Index { get; set; } = 0; + + /// + /// IP鍦板潃 + /// + public string Ip { get; set; } = string.Empty; + + /// + /// 绔彛 + /// + public int Port { get; set; } = 502; + + /// + /// 鎺у埗鍣ㄧ被鍨嬶紙绫诲悕锛 + /// + public string Type { get; set; } = string.Empty; + + /// + /// 闂ㄥ垪琛 + /// + public List Doors { get; set; } = new List(); + } + + /// + /// 闂ㄦā鍨 + /// + public class DoorModel + { + /// + /// 闂ㄧ储寮 + /// + public int Index { get; set; } = 0; + + /// + /// 寮鍏虫帶鍒朵俊鍙峰湴鍧锛圡odbus 绾垮湀鍦板潃锛 + /// + public ushort ControlAddress { get; set; } = 0; + + /// + /// 寮鍒颁綅淇″彿鍦板潃锛圡odbus 绾垮湀鍦板潃锛 + /// + public ushort OpenStatusAddress { get; set; } = 0; + + /// + /// 绂佹瀵硅闂ㄤ笅鍙戜换浣曟帶鍒舵寚浠わ紙鎵撳紑鎴栧叧闂級銆 + /// 涓 true 鏃讹紝闂ㄦ帶閫昏緫涓嶄細瀵硅闂ㄨ皟鐢 WriteDoorControl銆 + /// + public bool NoControl { get; set; } = false; + } +} diff --git a/StandardScene.Core/ExtendDevice/Door/DoorMonitor.Designer.cs b/StandardScene.Core/ExtendDevice/Door/DoorMonitor.Designer.cs new file mode 100644 index 0000000..a705f2e --- /dev/null +++ b/StandardScene.Core/ExtendDevice/Door/DoorMonitor.Designer.cs @@ -0,0 +1,263 @@ +namespace StandardScene.ExtendDevice.Door +{ + partial class DoorMonitor + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.doorListView = new System.Windows.Forms.ListView(); + this.columnHeaderControllerIndex = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.columnHeaderDoorIndex = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.columnHeaderState = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.columnHeaderTarget = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.columnHeaderSource = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.columnHeaderManualRemain = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.columnHeaderCarsInArea = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.columnHeaderControlAddress = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.columnHeaderOpenStatusAddress = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.groupBoxControl = new System.Windows.Forms.GroupBox(); + this.btnClose = new System.Windows.Forms.Button(); + this.btnOpen = new System.Windows.Forms.Button(); + this.btnClearCars = new System.Windows.Forms.Button(); + this.labelDoorInfo = new System.Windows.Forms.Label(); + this.labelTitle = new System.Windows.Forms.Label(); + this.timerRefresh = new System.Windows.Forms.Timer(); + this.groupBoxControl.SuspendLayout(); + this.SuspendLayout(); + // + // doorListView + // + this.doorListView.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.doorListView.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; + this.doorListView.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] { + this.columnHeaderControllerIndex, + this.columnHeaderDoorIndex, + this.columnHeaderState, + this.columnHeaderTarget, + this.columnHeaderSource, + this.columnHeaderManualRemain, + this.columnHeaderCarsInArea, + this.columnHeaderControlAddress, + this.columnHeaderOpenStatusAddress}); + this.doorListView.Font = new System.Drawing.Font("寰蒋闆呴粦", 10.5F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + this.doorListView.FullRowSelect = true; + this.doorListView.GridLines = true; + this.doorListView.HeaderStyle = System.Windows.Forms.ColumnHeaderStyle.Nonclickable; + this.doorListView.HideSelection = false; + this.doorListView.Location = new System.Drawing.Point(15, 55); + this.doorListView.MultiSelect = false; + this.doorListView.Name = "doorListView"; + this.doorListView.OwnerDraw = true; + this.doorListView.Size = new System.Drawing.Size(800, 400); + this.doorListView.TabIndex = 0; + this.doorListView.UseCompatibleStateImageBehavior = false; + this.doorListView.View = System.Windows.Forms.View.Details; + this.doorListView.SelectedIndexChanged += new System.EventHandler(this.doorListView_SelectedIndexChanged); + // + // columnHeaderControllerIndex + // + this.columnHeaderControllerIndex.Text = "鎺у埗鍣ㄧ紪鐮"; + this.columnHeaderControllerIndex.Width = 120; + // + // columnHeaderDoorIndex + // + this.columnHeaderDoorIndex.Text = "闂ㄧ紪鐮"; + this.columnHeaderDoorIndex.Width = 100; + // + // columnHeaderState + // + this.columnHeaderState.Text = "鐘舵"; + this.columnHeaderState.Width = 100; + // + // columnHeaderTarget + // + this.columnHeaderTarget.Text = "鎺у埗鐩爣"; + this.columnHeaderTarget.Width = 100; + // + // columnHeaderSource + // + this.columnHeaderSource.Text = "鎺у埗鏉ユ簮"; + this.columnHeaderSource.Width = 100; + // + // columnHeaderManualRemain + // + this.columnHeaderManualRemain.Text = "鎵嬪姩鍓╀綑(s)"; + this.columnHeaderManualRemain.Width = 110; + // + // columnHeaderCarsInArea + // + this.columnHeaderCarsInArea.Text = "杞﹁締鍗犵敤"; + this.columnHeaderCarsInArea.Width = 150; + // + // columnHeaderControlAddress + // + this.columnHeaderControlAddress.Text = "鎺у埗鍦板潃"; + this.columnHeaderControlAddress.Width = 120; + // + // columnHeaderOpenStatusAddress + // + this.columnHeaderOpenStatusAddress.Text = "寮鍒颁綅鍦板潃"; + this.columnHeaderOpenStatusAddress.Width = 120; + // + // groupBoxControl + // + this.groupBoxControl.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.groupBoxControl.Controls.Add(this.btnClose); + this.groupBoxControl.Controls.Add(this.btnOpen); + this.groupBoxControl.Controls.Add(this.btnClearCars); + this.groupBoxControl.Controls.Add(this.labelDoorInfo); + this.groupBoxControl.Font = new System.Drawing.Font("寰蒋闆呴粦", 10.5F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + this.groupBoxControl.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(68)))), ((int)(((byte)(68)))), ((int)(((byte)(68))))); + this.groupBoxControl.Location = new System.Drawing.Point(15, 470); + this.groupBoxControl.Name = "groupBoxControl"; + this.groupBoxControl.Padding = new System.Windows.Forms.Padding(12, 10, 12, 12); + this.groupBoxControl.Size = new System.Drawing.Size(800, 120); + this.groupBoxControl.TabIndex = 1; + this.groupBoxControl.TabStop = false; + this.groupBoxControl.Text = "鎵嬪姩鎺у埗"; + // + // btnClose + // + this.btnClose.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(220)))), ((int)(((byte)(53)))), ((int)(((byte)(69))))); + this.btnClose.FlatAppearance.BorderSize = 0; + this.btnClose.FlatAppearance.MouseDownBackColor = System.Drawing.Color.FromArgb(((int)(((byte)(165)))), ((int)(((byte)(40)))), ((int)(((byte)(52))))); + this.btnClose.FlatAppearance.MouseOverBackColor = System.Drawing.Color.FromArgb(((int)(((byte)(187)))), ((int)(((byte)(45)))), ((int)(((byte)(59))))); + this.btnClose.FlatStyle = System.Windows.Forms.FlatStyle.Flat; + this.btnClose.Font = new System.Drawing.Font("寰蒋闆呴粦", 12F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + this.btnClose.ForeColor = System.Drawing.Color.White; + this.btnClose.Location = new System.Drawing.Point(450, 50); + this.btnClose.Name = "btnClose"; + this.btnClose.Size = new System.Drawing.Size(120, 50); + this.btnClose.TabIndex = 2; + this.btnClose.Text = "鍏抽棴"; + this.btnClose.UseVisualStyleBackColor = false; + this.btnClose.Click += new System.EventHandler(this.btnClose_Click); + // + // btnOpen + // + this.btnOpen.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(40)))), ((int)(((byte)(167)))), ((int)(((byte)(69))))); + this.btnOpen.FlatAppearance.BorderSize = 0; + this.btnOpen.FlatAppearance.MouseDownBackColor = System.Drawing.Color.FromArgb(((int)(((byte)(30)))), ((int)(((byte)(125)))), ((int)(((byte)(52))))); + this.btnOpen.FlatAppearance.MouseOverBackColor = System.Drawing.Color.FromArgb(((int)(((byte)(33)))), ((int)(((byte)(136)))), ((int)(((byte)(56))))); + this.btnOpen.FlatStyle = System.Windows.Forms.FlatStyle.Flat; + this.btnOpen.Font = new System.Drawing.Font("寰蒋闆呴粦", 12F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + this.btnOpen.ForeColor = System.Drawing.Color.White; + this.btnOpen.Location = new System.Drawing.Point(300, 50); + this.btnOpen.Name = "btnOpen"; + this.btnOpen.Size = new System.Drawing.Size(120, 50); + this.btnOpen.TabIndex = 1; + this.btnOpen.Text = "鎵撳紑"; + this.btnOpen.UseVisualStyleBackColor = false; + this.btnOpen.Click += new System.EventHandler(this.btnOpen_Click); + // + // btnClearCars + // + this.btnClearCars.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(108)))), ((int)(((byte)(117)))), ((int)(((byte)(125))))); + this.btnClearCars.FlatAppearance.BorderSize = 0; + this.btnClearCars.FlatStyle = System.Windows.Forms.FlatStyle.Flat; + this.btnClearCars.Font = new System.Drawing.Font("寰蒋闆呴粦", 10.5F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + this.btnClearCars.ForeColor = System.Drawing.Color.White; + this.btnClearCars.Location = new System.Drawing.Point(600, 50); + this.btnClearCars.Name = "btnClearCars"; + this.btnClearCars.Size = new System.Drawing.Size(140, 50); + this.btnClearCars.TabIndex = 3; + this.btnClearCars.Text = "娓呯┖鍗犵敤"; + this.btnClearCars.UseVisualStyleBackColor = false; + this.btnClearCars.Click += new System.EventHandler(this.btnClearCars_Click); + // + // labelDoorInfo + // + this.labelDoorInfo.AutoSize = true; + this.labelDoorInfo.Font = new System.Drawing.Font("寰蒋闆呴粦", 10.5F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + this.labelDoorInfo.Location = new System.Drawing.Point(20, 35); + this.labelDoorInfo.Name = "labelDoorInfo"; + this.labelDoorInfo.Size = new System.Drawing.Size(200, 24); + this.labelDoorInfo.TabIndex = 0; + this.labelDoorInfo.Text = "璇烽夋嫨瑕佹帶鍒剁殑闂"; + // + // labelTitle + // + this.labelTitle.AutoSize = true; + this.labelTitle.Font = new System.Drawing.Font("寰蒋闆呴粦", 16F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + this.labelTitle.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(51)))), ((int)(((byte)(51)))), ((int)(((byte)(51))))); + this.labelTitle.Location = new System.Drawing.Point(15, 12); + this.labelTitle.Name = "labelTitle"; + this.labelTitle.Size = new System.Drawing.Size(150, 42); + this.labelTitle.TabIndex = 2; + this.labelTitle.Text = "闂ㄦ帶鐩戞帶"; + // + // timerRefresh + // + this.timerRefresh.Interval = 1000; + this.timerRefresh.Tick += new System.EventHandler(this.timerRefresh_Tick); + // + // DoorMonitor + // + this.AutoScaleDimensions = new System.Drawing.SizeF(9F, 18F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(245)))), ((int)(((byte)(245)))), ((int)(((byte)(247))))); + this.ClientSize = new System.Drawing.Size(830, 600); + this.Controls.Add(this.labelTitle); + this.Controls.Add(this.groupBoxControl); + this.Controls.Add(this.doorListView); + this.Font = new System.Drawing.Font("寰蒋闆呴粦", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + this.MinimumSize = new System.Drawing.Size(830, 600); + this.Name = "DoorMonitor"; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen; + this.Text = "闂ㄦ帶鐩戞帶"; + this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.DoorMonitor_FormClosing); + this.Load += new System.EventHandler(this.DoorMonitor_Load); + this.groupBoxControl.ResumeLayout(false); + this.groupBoxControl.PerformLayout(); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private System.Windows.Forms.ListView doorListView; + private System.Windows.Forms.ColumnHeader columnHeaderControllerIndex; + private System.Windows.Forms.ColumnHeader columnHeaderDoorIndex; + private System.Windows.Forms.ColumnHeader columnHeaderState; + private System.Windows.Forms.ColumnHeader columnHeaderTarget; + private System.Windows.Forms.ColumnHeader columnHeaderSource; + private System.Windows.Forms.ColumnHeader columnHeaderManualRemain; + private System.Windows.Forms.ColumnHeader columnHeaderCarsInArea; + private System.Windows.Forms.ColumnHeader columnHeaderControlAddress; + private System.Windows.Forms.ColumnHeader columnHeaderOpenStatusAddress; + private System.Windows.Forms.GroupBox groupBoxControl; + private System.Windows.Forms.Label labelDoorInfo; + private System.Windows.Forms.Button btnOpen; + private System.Windows.Forms.Button btnClose; + private System.Windows.Forms.Button btnClearCars; + private System.Windows.Forms.Label labelTitle; + private System.Windows.Forms.Timer timerRefresh; + } +} diff --git a/StandardScene.Core/ExtendDevice/Door/DoorMonitor.cs b/StandardScene.Core/ExtendDevice/Door/DoorMonitor.cs new file mode 100644 index 0000000..93f977b --- /dev/null +++ b/StandardScene.Core/ExtendDevice/Door/DoorMonitor.cs @@ -0,0 +1,447 @@ +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Linq; +using System.Reflection; +using System.Windows.Forms; +using SimpleLite; + +namespace StandardScene.ExtendDevice.Door +{ + public partial class DoorMonitor : Form + { + private static DoorMonitor _instance = null; + private static readonly object _lock = new object(); + + private int _doorHoverIndex = -1; + private (int ControllerIndex, int DoorIndex)? _selectedDoor = null; + + private static readonly Color RowEvenColor = Color.FromArgb(250, 250, 252); + private static readonly Color RowOddColor = Color.White; + private static readonly Color RowHighlightColor = Color.FromArgb(230, 240, 255); + private static readonly Color TextRegularColor = Color.FromArgb(68, 68, 68); + private static readonly Color TextHighlightColor = Color.FromArgb(51, 51, 51); + private static readonly Color StateOpenColor = Color.FromArgb(40, 167, 69); + private static readonly Color StateClosedColor = Color.FromArgb(220, 53, 69); + + /// + /// 鑾峰彇鍗曚緥瀹炰緥 + /// + public static DoorMonitor Instance + { + get + { + if (_instance == null || _instance.IsDisposed) + { + lock (_lock) + { + if (_instance == null || _instance.IsDisposed) + { + _instance = new DoorMonitor(); + } + } + } + return _instance; + } + } + + /// + /// 绉佹湁鏋勯犲嚱鏁帮紝纭繚鍗曚緥妯″紡 + /// + private DoorMonitor() + { + InitializeComponent(); + } + + /// + /// 纭繚鍒锋柊瀹氭椂鍣ㄥ浜庢縺娲荤姸鎬侊紝骞剁珛鍗冲埛鏂颁竴娆 + /// + public void EnsureRefreshActive() + { + if (IsDisposed) + { + return; + } + + if (!timerRefresh.Enabled) + { + timerRefresh.Start(); + } + + RefreshDoorList(); + } + + private void DoorMonitor_Load(object sender, EventArgs e) + { + SetupListViewStyles(); + // 绂佺敤绯荤粺鐨勬偓鍋/鐑窡韪珮浜紝閬垮厤榧犳爣绉诲姩鏃剁煭鏆傚嚭鐜伴粯璁ら伄缃 + doorListView.HoverSelection = false; + doorListView.HotTracking = false; + EnsureRefreshActive(); + } + + /// + /// 璁剧疆ListView鐨勮瑙夋牱寮 + /// + private void SetupListViewStyles() + { + doorListView.OwnerDraw = true; + doorListView.BackColor = Color.White; + doorListView.DrawItem += DoorListView_DrawItem; + doorListView.DrawSubItem += DoorListView_DrawSubItem; + doorListView.DrawColumnHeader += DoorListView_DrawColumnHeader; + doorListView.MouseMove += DoorListView_MouseMove; + doorListView.MouseLeave += DoorListView_MouseLeave; + + // 鍚敤鍙岀紦鍐 + typeof(Control)?.GetProperty("DoubleBuffered", BindingFlags.Instance | BindingFlags.NonPublic)? + .SetValue(doorListView, true, null); + } + + private void DoorListView_MouseMove(object sender, MouseEventArgs e) + { + var hoveredItem = doorListView.GetItemAt(e.X, e.Y); + int newIndex = hoveredItem?.Index ?? -1; + + if (_doorHoverIndex != newIndex) + { + _doorHoverIndex = newIndex; + doorListView.Invalidate(); + } + } + + private void DoorListView_MouseLeave(object sender, EventArgs e) + { + if (_doorHoverIndex != -1) + { + _doorHoverIndex = -1; + doorListView.Invalidate(); + } + } + + private void DoorListView_DrawItem(object sender, DrawListViewItemEventArgs e) + { + var isHighlighted = e.Item.Selected + || e.ItemIndex == _doorHoverIndex + || (doorListView.Focused && (e.State & ListViewItemStates.Focused) != 0); + + var backColor = isHighlighted + ? RowHighlightColor + : (e.ItemIndex % 2 == 0 ? RowEvenColor : RowOddColor); + + using (var brush = new SolidBrush(backColor)) + { + e.Graphics.FillRectangle(brush, e.Bounds); + } + + var textColor = isHighlighted ? TextHighlightColor : TextRegularColor; + + TextRenderer.DrawText(e.Graphics, e.Item.Text, e.Item.Font, e.Bounds, + textColor, + TextFormatFlags.VerticalCenter | TextFormatFlags.Left | TextFormatFlags.EndEllipsis); + + e.DrawFocusRectangle(); + } + + private void DoorListView_DrawSubItem(object sender, DrawListViewSubItemEventArgs e) + { + var isHighlighted = e.Item.Selected + || e.ItemIndex == _doorHoverIndex + || (doorListView.Focused && (e.ItemState & ListViewItemStates.Focused) != 0); + + var backColor = isHighlighted + ? RowHighlightColor + : (e.ItemIndex % 2 == 0 ? RowEvenColor : RowOddColor); + + using (var brush = new SolidBrush(backColor)) + { + e.Graphics.FillRectangle(brush, e.Bounds); + } + + Color textColor = TextRegularColor; + + // 濡傛灉鏄姸鎬佸垪锛屾牴鎹姸鎬佽缃鑹 + if (e.ColumnIndex == 2) // 鐘舵佸垪 + { + var stateText = e.SubItem.Text; + if (stateText == "鎵撳紑") + { + textColor = StateOpenColor; + } + else if (stateText == "鍏抽棴") + { + textColor = StateClosedColor; + } + } + // 濡傛灉鏄洰鏍囨帶鍒跺垪锛屾寜鐩爣鐘舵佺潃鑹 + else if (e.ColumnIndex == 3) // 鎺у埗鐩爣鍒 + { + var targetText = e.SubItem.Text; + if (targetText == "寮") + { + textColor = StateOpenColor; + } + else + { + textColor = StateClosedColor; + } + } + // 鍏朵粬鍒椾娇鐢ㄩ粯璁ら鑹 + else + { + textColor = isHighlighted ? TextHighlightColor : TextRegularColor; + } + + TextRenderer.DrawText(e.Graphics, e.SubItem.Text, e.SubItem.Font, e.Bounds, + textColor, + TextFormatFlags.VerticalCenter | TextFormatFlags.Left | TextFormatFlags.EndEllipsis); + } + + private void DoorListView_DrawColumnHeader(object sender, DrawListViewColumnHeaderEventArgs e) + { + e.Graphics.FillRectangle(new SolidBrush(Color.FromArgb(245, 247, 250)), e.Bounds); + + e.Graphics.DrawLine(new Pen(Color.FromArgb(220, 220, 220)), + e.Bounds.Left, e.Bounds.Bottom - 1, e.Bounds.Right, e.Bounds.Bottom - 1); + + TextRenderer.DrawText(e.Graphics, e.Header.Text, + new Font("寰蒋闆呴粦", 10.5F, FontStyle.Bold), + e.Bounds, Color.FromArgb(68, 68, 68), + TextFormatFlags.VerticalCenter | TextFormatFlags.Left | TextFormatFlags.HorizontalCenter); + } + + /// + /// 鍒锋柊闂ㄥ垪琛 + /// + private void RefreshDoorList() + { + doorListView.Items.Clear(); + + // 淇濆瓨褰撳墠閫変腑鐨勯棬 + (int ControllerIndex, int DoorIndex)? previousSelected = _selectedDoor; + _selectedDoor = null; + labelDoorInfo.Text = "璇烽夋嫨瑕佹帶鍒剁殑闂"; + + // 鑾峰彇鎵鏈夐棬鎺у埗鍣 + var mission = SimpleProject.proj?.Missions?.OfType().FirstOrDefault(); + if (mission == null) + { + return; + } + + var doorSnapshot = mission.GetDoorMonitorSnapshot(); + if (doorSnapshot.Count == 0) + { + return; + } + + ListViewItem selectedItem = null; + + foreach (var door in doorSnapshot) + { + var stateText = door.State == DoorState.Open ? "鎵撳紑" : door.State == DoorState.Closed ? "鍏抽棴" : "鏈煡"; + var targetText = door.Target ? "寮" : "鍏"; + var sourceText = door.Source == DoorMission.ControlSource.Manual ? "鎵嬪姩" : "鑷姩"; + var remainText = door.Source == DoorMission.ControlSource.Manual && door.ManualRemainingSeconds.HasValue + ? Math.Ceiling(door.ManualRemainingSeconds.Value).ToString() + : "-"; + var carsText = door.CarsInArea.Count > 0 ? string.Join(", ", door.CarsInArea) : "鏃"; + + var item = new ListViewItem(door.ControllerIndex.ToString()); + item.SubItems.Add(door.DoorIndex.ToString()); + item.SubItems.Add(stateText); + item.SubItems.Add(targetText); + item.SubItems.Add(sourceText); + item.SubItems.Add(remainText); + item.SubItems.Add(carsText); + item.SubItems.Add(door.ControlAddress.ToString()); + item.SubItems.Add(door.OpenStatusAddress.ToString()); + item.Tag = (door.ControllerIndex, door.DoorIndex); + item.UseItemStyleForSubItems = false; + doorListView.Items.Add(item); + + // 濡傛灉涔嬪墠閫変腑鐨勯棬瀛樺湪锛屾仮澶嶉変腑鐘舵 + if (previousSelected.HasValue && + previousSelected.Value.ControllerIndex == door.ControllerIndex && + previousSelected.Value.DoorIndex == door.DoorIndex) + { + selectedItem = item; + } + } + + // 鎭㈠閫変腑鐘舵 + if (selectedItem != null) + { + selectedItem.Selected = true; + selectedItem.EnsureVisible(); + doorListView_SelectedIndexChanged(doorListView, EventArgs.Empty); + } + } + + /// + /// 闂ㄥ垪琛ㄩ夋嫨鏀瑰彉 + /// + private void doorListView_SelectedIndexChanged(object sender, EventArgs e) + { + if (doorListView.SelectedItems.Count > 0) + { + var tag = doorListView.SelectedItems[0].Tag; + if (tag != null && tag is ValueTuple) + { + var doorInfo = (ValueTuple)tag; + _selectedDoor = doorInfo; + labelDoorInfo.Text = $"鎺у埗鍣ㄧ紪鐮: {doorInfo.Item1}, 闂ㄧ紪鐮: {doorInfo.Item2}"; + + // 鏍规嵁鍗犵敤鐘舵佸喅瀹氬叧闂寜閽槸鍚﹀彲鐢 + var mission = SimpleProject.proj?.Missions?.OfType().FirstOrDefault(); + var carsInArea = mission?.GetCarsInArea(doorInfo.Item1, doorInfo.Item2) ?? Array.Empty(); + btnClose.Enabled = carsInArea.Count == 0; + } + else + { + _selectedDoor = null; + labelDoorInfo.Text = "璇烽夋嫨瑕佹帶鍒剁殑闂"; + btnClose.Enabled = true; + } + } + else + { + _selectedDoor = null; + labelDoorInfo.Text = "璇烽夋嫨瑕佹帶鍒剁殑闂"; + btnClose.Enabled = true; + } + } + + /// + /// 鎵撳紑闂 + /// + private void btnOpen_Click(object sender, EventArgs e) + { + if (!_selectedDoor.HasValue) + { + MessageBox.Show("璇峰厛閫夋嫨瑕佹帶鍒剁殑闂", "鎻愮ず", MessageBoxButtons.OK, MessageBoxIcon.Information); + return; + } + + try + { + var mission = SimpleProject.proj?.Missions?.OfType().FirstOrDefault(); + if (mission == null) + { + MessageBox.Show("鏈壘鍒伴棬鎺ц繘绋", "閿欒", MessageBoxButtons.OK, MessageBoxIcon.Error); + return; + } + + var (controllerIndex, doorIndex) = _selectedDoor.Value; + // 鎵嬪姩鎺у埗锛氶粯璁や繚鎸10绉 + mission.SetManualDoorControl(controllerIndex, doorIndex, true); + MessageBox.Show($"鎺у埗鍣 {controllerIndex} 闂 {doorIndex} 宸茶缃墜鍔ㄦ墦寮锛10绉掞級", "鎻愮ず", MessageBoxButtons.OK, MessageBoxIcon.Information); + } + catch (Exception ex) + { + MessageBox.Show($"璁剧疆闂ㄦ墦寮鐩爣澶辫触: {ex.Message}", "閿欒", MessageBoxButtons.OK, MessageBoxIcon.Error); + } + } + + /// + /// 鍏抽棴闂 + /// + private void btnClose_Click(object sender, EventArgs e) + { + if (!_selectedDoor.HasValue) + { + MessageBox.Show("璇峰厛閫夋嫨瑕佹帶鍒剁殑闂", "鎻愮ず", MessageBoxButtons.OK, MessageBoxIcon.Information); + return; + } + + try + { + var mission = SimpleProject.proj?.Missions?.OfType().FirstOrDefault(); + if (mission == null) + { + MessageBox.Show("鏈壘鍒伴棬鎺ц繘绋", "閿欒", MessageBoxButtons.OK, MessageBoxIcon.Error); + return; + } + + var (controllerIndex, doorIndex) = _selectedDoor.Value; + // 杞﹁締鍗犵敤鏃剁姝㈡墜鍔ㄥ叧闂 + var success = mission.SetManualDoorControl(controllerIndex, doorIndex, false); + if (!success) + { + MessageBox.Show("闂ㄥ瓨鍦ㄨ溅杈嗗崰鐢紝绂佹鎵嬪姩鍏抽棴銆", "鎻愮ず", MessageBoxButtons.OK, MessageBoxIcon.Information); + return; + } + MessageBox.Show($"鎺у埗鍣 {controllerIndex} 闂 {doorIndex} 宸茶缃墜鍔ㄥ叧闂紙10绉掞級", "鎻愮ず", MessageBoxButtons.OK, MessageBoxIcon.Information); + } + catch (Exception ex) + { + MessageBox.Show($"璁剧疆闂ㄥ叧闂洰鏍囧け璐: {ex.Message}", "閿欒", MessageBoxButtons.OK, MessageBoxIcon.Error); + } + } + + /// + /// 娓呯┖杞﹁締鍗犵敤 + /// + private void btnClearCars_Click(object sender, EventArgs e) + { + if (!_selectedDoor.HasValue) + { + MessageBox.Show("璇峰厛閫夋嫨瑕佹竻绌哄崰鐢ㄧ殑闂", "鎻愮ず", MessageBoxButtons.OK, MessageBoxIcon.Information); + return; + } + + try + { + var mission = SimpleProject.proj?.Missions?.OfType().FirstOrDefault(); + if (mission == null) + { + MessageBox.Show("鏈壘鍒伴棬鎺ц繘绋", "閿欒", MessageBoxButtons.OK, MessageBoxIcon.Error); + return; + } + + var (controllerIndex, doorIndex) = _selectedDoor.Value; + mission.ClearCarsInArea(controllerIndex, doorIndex); + MessageBox.Show($"鎺у埗鍣 {controllerIndex} 闂 {doorIndex} 宸叉竻绌哄崰鐢", "鎻愮ず", MessageBoxButtons.OK, MessageBoxIcon.Information); + RefreshDoorList(); + } + catch (Exception ex) + { + MessageBox.Show($"娓呯┖鍗犵敤澶辫触: {ex.Message}", "閿欒", MessageBoxButtons.OK, MessageBoxIcon.Error); + } + } + + /// + /// 瀹氭椂鍒锋柊 + /// + private void timerRefresh_Tick(object sender, EventArgs e) + { + RefreshDoorList(); + } + + /// + /// 绐椾綋鍏抽棴浜嬩欢 + /// + private void DoorMonitor_FormClosing(object sender, FormClosingEventArgs e) + { + if (e.CloseReason == CloseReason.UserClosing) + { + timerRefresh.Stop(); + e.Cancel = true; + this.Visible = false; + } + } + + protected override void OnVisibleChanged(EventArgs e) + { + base.OnVisibleChanged(e); + if (Visible) + { + EnsureRefreshActive(); + } + else + { + timerRefresh.Stop(); + } + } + } +} diff --git a/StandardScene.Core/ExtendDevice/Door/DoorMonitor.resx b/StandardScene.Core/ExtendDevice/Door/DoorMonitor.resx new file mode 100644 index 0000000..4391a28 --- /dev/null +++ b/StandardScene.Core/ExtendDevice/Door/DoorMonitor.resx @@ -0,0 +1,64 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 17, 17 + + diff --git a/StandardScene.Core/ExtendDevice/Door/DoorTypeAttribute.cs b/StandardScene.Core/ExtendDevice/Door/DoorTypeAttribute.cs new file mode 100644 index 0000000..65fa4a9 --- /dev/null +++ b/StandardScene.Core/ExtendDevice/Door/DoorTypeAttribute.cs @@ -0,0 +1,25 @@ +using System; + +namespace StandardScene.ExtendDevice.Door +{ + /// + /// 闂ㄦ帶鍒跺櫒绫诲瀷鐗规э紝鐢ㄤ簬鏍囪闂ㄦ帶鍒跺櫒绫诲瀷 + /// + [AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)] + public class DoorTypeAttribute : Attribute + { + /// + /// 绫诲瀷鍚嶇О + /// + public string Name { get; } + + /// + /// 鏋勯犲嚱鏁 + /// + /// 绫诲瀷鍚嶇О + public DoorTypeAttribute(string name) + { + Name = name ?? throw new ArgumentNullException(nameof(name)); + } + } +} diff --git a/StandardScene.Core/Heuristic.cs b/StandardScene.Core/Heuristic.cs new file mode 100644 index 0000000..94a4f56 --- /dev/null +++ b/StandardScene.Core/Heuristic.cs @@ -0,0 +1,41 @@ +锘縰sing SimpleCore.Compiler; +using SimpleCore; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Numerics; +using SimpleCore.Library; +using SimpleCore.Traffic; + +namespace StandardScene +{ + public class Heuristic:HeuristicsContainer + { + //[HeuristicDef] + //public bool NoPassShelf(SegmentPlan.SearchStat stat) + //{ + // if (stat.sequence.Length > 2 && + // SimpleLib.GetSite(stat.sequence[stat.sequence.Length - 2]).fields.ContainsKey("Shelf")) + // return false; + // return true; + //} + + [HeuristicDef] + public bool ToDestConstraint(SegmentPlan.SearchStat stat) + { + if (plan.Source.fields.TryGetValue("constraint", out var field)) + { + foreach (var constraint in field.Split('|')) + { + var (dstStr, idStr, _) = constraint.Split(':'); + if (plan.Destination.id.ToString() == dstStr && stat.CurrentSite.id.ToString() == idStr) + return false; + } + } + + return true; + } + } +} diff --git a/StandardScene.Core/InterLock/AbstractInterlockMission.cs b/StandardScene.Core/InterLock/AbstractInterlockMission.cs new file mode 100644 index 0000000..15484be --- /dev/null +++ b/StandardScene.Core/InterLock/AbstractInterlockMission.cs @@ -0,0 +1,413 @@ +using SimpleCore.Traffic; +using System; +using System.Collections.Generic; +using System.Linq; +using SimpleLite.RCS; +using SimpleLite.RCS.CarTypes; +using SimpleCore.Library; +using SimpleCore.PropType; +using SimpleCore; +using System.Threading; +using SimpleLite.CADTools; +using SimpleLite.Props; +using SimpleLite.UI; +using SimpleLite; +using System.Windows.Forms; +using Newtonsoft.Json; + +namespace StandardScene.InterLock +{ + public class MyDictionary where TItem : new() + { + private readonly Dictionary dictionary = new(); + + public TItem this[TKey key] + { + get + { + if (dictionary.TryGetValue(key, out var item)) return item; + var newItem = new TItem(); + dictionary[key] = newItem; + return newItem; + } + set => dictionary[key] = value; + } + + public ICollection Keys + { + get => dictionary.Keys; + } + } + + public class AbstractInterlockMission : Mission + { + public bool GetAskEnter(int siteId) + { + if (!SiteFilter(siteId)) return false; + lock (sync) return askEnter[siteId]; + } + + public void SetAllowEnter(int siteId, bool value) + { + if (!SiteFilter(siteId)) return; + lock (sync) allowEnter[siteId] = value; + } + + public bool GetAskExit(int siteId) + { + if (!SiteFilter(siteId)) return false; + lock (sync) return askExit[siteId]; + } + + public void SetAllowExit(int siteId, bool value) + { + if (!SiteFilter(siteId)) return; + lock (sync) allowExit[siteId] = value; + } + + public bool GetReportLeave(int siteId) + { + if (!SiteFilter(siteId)) return false; + lock (sync) return reportLeave[siteId]; + } + + public void SetAcknowledgeLeave(int siteId, bool value) + { + if (!SiteFilter(siteId)) return; + lock (sync) acknowledgeLeave[siteId] = value; + } + + public int GetInSiteCarId(int siteId) + { + if (!SiteFilter(siteId)) return -1; + lock (sync) return inSiteCarId[siteId]; + } + + // 璇锋眰杩涚珯锛堝啓锛 + [JsonIgnore] private readonly MyDictionary askEnter = new(); + // 鍏佽杩涚珯锛堣锛 + [JsonIgnore] private readonly MyDictionary allowEnter = new(); + + // 璇锋眰绂荤珯锛堝啓锛 + [JsonIgnore] private readonly MyDictionary askExit = new(); + // 鍏佽绂荤珯锛堣锛 + [JsonIgnore] private readonly MyDictionary allowExit = new(); + + // 鎶ュ憡绂诲紑锛堝啓锛 + [JsonIgnore] private readonly MyDictionary reportLeave = new(); + // 纭绂诲紑锛堣锛 + [JsonIgnore] private readonly MyDictionary acknowledgeLeave = new(); + + // 绔欑偣涓婃墍鍦ㄧ殑灏忚溅锛堢姸鎬侊級 + [JsonIgnore] private readonly MyDictionary inSiteCarId = new(); + + [JsonIgnore] private object sync = new(); + + public virtual string GetSiteDisplay(int siteId) + { + return $"{siteId}"; + } + + /// + /// 杩斿洖涓簍rue鐨勭珯鐐癸紝鏄綋鍓嶄簰閿佹満鍒跺叧娉ㄧ殑绔欑偣 + /// + /// + /// + public virtual bool SiteFilter(int siteId) + { + return true; + } + + /// + /// 浜掗攣绔欑偣杩涚珯鏉′欢 + /// + /// + /// + /// + private bool EnterCondition(AbstractCar car, int siteId) + { + // todo: 鏄惁鑰冭檻鎻愬墠鏇村鐢宠杩涘叆锛屽姞蹇妭鎷嶏紵 + if (car.status.pendingLocks.First() != siteId) return false; + //DateTime startTime = DateTime.Now; + //bool validTime = false; + + lock (sync) + { + // if (askEnter[siteId] == false) + // { + // Diagnosis.Post($"{car.name}({car.id}) 鐢宠杩涚珯 {GetSiteDisplay(siteId)}", "intertime", true); + // //startTime = DateTime.Now; + // //validTime = true; + // } + askEnter[siteId] = true; + if (reportLeave[siteId]) + { + Diagnosis.Post($"{car.name}({car.id})鏈鍑嗚杩涚珯{GetSiteDisplay(siteId)}锛屽墠杞︾寮淇″彿鏈纭", "interlock", true); + return false; + } + + if (allowEnter[siteId]) + { + Diagnosis.Post($"{car.name}({car.id})鍑嗚杩涚珯{GetSiteDisplay(siteId)}", "interlock", true); + // Diagnosis.Post($"{car.name}({car.id}) 鍑嗚杩涚珯 {GetSiteDisplay(siteId)}", "intertime", true); + lock (sync) askEnter[siteId] = false; + return true; + } + Diagnosis.Post($"{car.name}({car.id})鏈鍑嗚杩涚珯{GetSiteDisplay(siteId)}", "interlock", true); + return false; + } + } + + /// + /// 浜掗攣绔欑偣绂荤珯鏉′欢 + /// + /// + /// + /// + private bool ExitCondition(AbstractCar car, int siteId) + { + lock (sync) + { + askExit[siteId] = true; + + if (allowExit[siteId]) + { + Diagnosis.Post($"{car.name}({car.id})鍑嗚绂荤珯{GetSiteDisplay(siteId)}", "interlock", true); + askExit[siteId] = false; + return true; + } + Diagnosis.Post($"{car.name}({car.id})鏈鍑嗚绂荤珯{GetSiteDisplay(siteId)}", "interlock", true); + return false; + } + } + + /// + /// 鍛婄煡瀵规柟璁惧灏忚溅绂诲紑锛岀洿鍒板鏂硅澶囩‘璁ゆ敹鍒扮寮淇″彿 + /// + /// + /// + private void LeaveEvent(AbstractCar car, int siteId) + { + new Thread(() => + { + while (true) + { + Thread.Sleep(100); + lock (sync) + { + if (acknowledgeLeave[siteId]) // 宸茬粡纭灏忚溅绂诲紑 + { + Diagnosis.Post($"{GetSiteDisplay(siteId)}纭{car.name}({car.id})绂诲紑", "interlock", true); + break; + } + } + Diagnosis.Post($"{car.name}({car.id})鍛婄煡绂诲紑{GetSiteDisplay(siteId)}", "interlock", true); + lock (sync) reportLeave[siteId] = true; + } + + lock (sync) reportLeave[siteId] = false; + }).Start(); + } + + [JsonIgnore] private bool enableSimulation = false; + [JsonIgnore] public bool started = false; + [JsonIgnore] public Thread showT; + + [MethodMember(Name = "鍚姩杩涚▼", Description = "澶勭悊瀹夊叏浜掗攣")] + public override void Execute() + { + if (started) return; + // started = true; + status.status = "宸插惎鍔"; + + + + showT = new Thread(() => + { + while (true) + { + Thread.Sleep(300); + try + { + if (fields.TryGetValue("enableSimulation", out var userEnableSimulation)) + enableSimulation = bool.Parse(userEnableSimulation); + foreach (var site in SimpleLib.GetAllSites().Where(ss => SiteFilter(ss.id))) + { + var carId = -1; + foreach (var car in SimpleLib.GetAllCars()) + { + lock (TrafficControl.syncTrafficSequence) + { + if (car.status.holdingLocks.Length == 1 && car.status.holdingLocks[0] == site.id) + { + carId = car.id; + break; + } + } + } + + lock (sync) inSiteCarId[site.id] = carId; + } + + // if (!onDisplay) continue; + var painter = SimpleMonitor.getPainter("DemoInterlockMission"); + painter.clear(); + if (!onDisplay) continue; + //string str; + //lock (sync) + //{ + // var keys = askEnter.Keys.Concat(askExit.Keys).Concat(reportLeave.Keys).ToHashSet() + // .OrderBy(kk => int.Parse(SimpleLib.GetSite(kk).fields["ASNub"])).ToList(); + // str = $"绔欑偣\t\t璇锋眰杩涚珯\t鍏佽杩涚珯\t璇锋眰绂荤珯\t鍏佽绂荤珯\t涓婃姤绂诲紑\t纭绂诲紑\t灏忚溅id\n" + + // $"{string.Join("\n", keys.Select(key => + // $"{GetSiteDisplay(key)}\t{askEnter[key]}\t\t{allowEnter[key]}\t\t" + + // $"{askExit[key]}\t\t{allowExit[key]}\t\t" + + // $"{reportLeave[key]}\t\t{acknowledgeLeave[key]}\t\t{inSiteCarId[key]}"))}"; + //} + //painter.drawTextFixed(str, new SolidBrush(Color.Black), VirtualPainter.DrawPosition.RightTop, Color.AliceBlue); + } + catch (Exception ex) + { + Diagnosis.Post(ExceptionFormatter.FormatEx(ex), "interlock", true); + } + } + }) { Name = "InterlockMission" }; + + showT.Start(); + + + } + + [JsonIgnore] private bool onDisplay = true; + + [MethodMember(Name = "鍒囨崲鏄剧ず", Description = "鏄惁鍦ㄥ彸涓嬭鏄剧ず")] + public void SwitchAlwaysOnDisplay() + { + onDisplay = !onDisplay; + } + + private async void SimButton(string title, MyDictionary toManipulate) + { + //if (!enableSimulation) return; + G.pushStatus("璇烽夋嫨绔欑偣"); + var pt = await Program.UI.getPoint(new UIOps.getPointOptions() { site = true }); + var site = SimpleLib.GetSite(pt.site); + if (SiteFilter(site.id)) + { + var vv = await Program.UI.Input("璇疯緭鍏rue/false", title, "true"); + if (vv == null || !bool.TryParse(vv, out var allow)) + { + MessageBox.Show("杈撳叆閿欒"); + return; + } + lock (sync) toManipulate[site.id] = allow; + } + } + + [MethodMember(Name = "妯℃嫙鍑嗚杩涚珯")] + public void SimAllowEnter() + { + SimButton("妯℃嫙鍑嗚杩涘叆鐘舵", allowEnter); + } + + [MethodMember(Name = "妯℃嫙鍑嗚绂荤珯")] + public void SimAllowExit() + { + SimButton("妯℃嫙鍑嗚绂荤珯鐘舵", allowExit); + } + + [MethodMember(Name = "妯℃嫙纭绂诲紑")] + public void SimLeaveNoted() + { + SimButton("妯℃嫙纭绂诲紑鐘舵", acknowledgeLeave); + } + + [MethodMember(Name = "妯℃嫙鍙戦佺寮")] + public async void SimReportLeave() + { + // if (!enableSimulation) return; + G.pushStatus("璇烽夋嫨绔欑偣"); + var pt = await Program.UI.getPoint(new UIOps.getPointOptions() { site = true }); + var site = SimpleLib.GetSite(pt.site); + if (SiteFilter(site.id)) + { + lock (sync) reportLeave[site.id] = true; + } + new Thread(() => + { + var t1 = DateTime.Now; + while (true) + { + Thread.Sleep(100); + lock (sync) + { + var deltaT = (DateTime.Now - t1).TotalSeconds; + if (acknowledgeLeave[site.id]||deltaT>5) // 宸茬粡纭灏忚溅绂诲紑 + { + Diagnosis.Post($"{GetSiteDisplay(site.id)} 鍋滄鍙戦佺寮", "simlikai", true); + break; + } + } + Diagnosis.Post($"妯℃嫙鍛婄煡绂诲紑{GetSiteDisplay(site.id)}", "simlikai", true); + lock (sync) reportLeave[site.id] = true; + } + + lock (sync) reportLeave[site.id] = false; + }).Start(); + // SimButton("妯℃嫙纭绂诲紑鐘舵", reportLeave); + } + + public AbstractInterlockMission() + { + Diagnosis.Post($"InterlockMechanism loaded", "interlock", true); + TrafficControl.BeforeLock += (car, siteId) => + { + if (SiteFilter(siteId)) // EnterCondition + return EnterCondition(car, siteId); + + var site = SimpleLib.GetSite(siteId); + if (site.fields.ContainsKey("PreAskEnterSite") && int.TryParse(site.fields["PreAskEnterSite"], out int PreAskEnterSiteId)) + { + var presite = SimpleLib.GetSite(PreAskEnterSiteId); + if (presite != null && SiteFilter(PreAskEnterSiteId)) + { + Diagnosis.Post($"{car.name}({car.id})鍦▄siteId}-{GetSiteDisplay(siteId)}锛屾彁鍓嶇敵璇 {PreAskEnterSiteId} 鐨勮姹傝繘鍏", "interlock", true); + askEnter[PreAskEnterSiteId] = true; + } + + } + // ExitCondition + lock (TrafficControl.syncTrafficSequence) + if (!car.status.holdingLocks.Any(ss => SiteFilter(ss))) + return true; + + + var isNeighbor = false; + var lockSiteId = -1; + foreach (var trackId in site.relatedTracks) + { + var track = SimpleLib.GetTrack(trackId); + if (SiteFilter(track.siteA)) + { + isNeighbor = true; + lockSiteId = track.siteA; + break; + } + + if (SiteFilter(track.siteB)) + { + isNeighbor = true; + lockSiteId = track.siteB; + break; + } + } + + return !isNeighbor || ExitCondition(car, lockSiteId); + }; + TrafficControl.AfterLeave += (car, siteId) => + { + if (SiteFilter(siteId)) LeaveEvent(car, siteId); + }; + } + } +} diff --git a/StandardScene.Core/InterLock/TrafficInterlockMission.cs b/StandardScene.Core/InterLock/TrafficInterlockMission.cs new file mode 100644 index 0000000..2ff03be --- /dev/null +++ b/StandardScene.Core/InterLock/TrafficInterlockMission.cs @@ -0,0 +1,167 @@ +using LessokajiWeaverUtilities.Utilities; +using LoopViewerApp; +using Newtonsoft.Json; +using SimpleLite.RCS; +using SimpleLite.RCS.CarTypes; +using SimpleCore; +using SimpleCore.PropType; +using SimpleCore.Traffic; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Windows.Forms; + +namespace StandardScene.InterLock +{ + [MissionType(Name = "浜ら氱鍒", editor = typeof(TrafficInterlockMission))] + [I18N.DocumentTranslation(Name = "Traffic Mission", locale = "en")] + public class TrafficInterlockMission : Mission + { + public static List TrafficAreaList { get; set; } = new List(); + + public TrafficInterlockMission() + { + TrafficControl.BeforeLock += (car, siteId) => + { + return EnterArea(car, siteId); + }; + + TrafficControl.AfterLeave += (car, siteId) => + { + LeaveArea(car, siteId); + }; + } + + [MethodMember(Name = "鍚姩杩涚▼", Description = "寮濮嬩氦閫氱洃鎺")] + public void StartTrafficControl() + { + if (InitTrafficConfig()) { status.status = "宸插惎鍔"; } + } + + [MethodMember(Name = "鍋滄杩涚▼", Description = "鍋滄浜ら氱洃鎺")] + public void StopTrafficControl() + { + status.status = "宸插仠姝"; + } + + [MethodMember(Name = "鏌ョ湅绠″埗鍖")] + public void ShowViewer() + { + var viewer = new TrafficInterlockViewer(); + viewer.Show(); + } + + /// + /// 鍒濆鍖栦氦绠¢厤缃枃浠 + /// + /// + private bool InitTrafficConfig() + { + try + { + string ConfigPath = Path.Combine(Application.StartupPath, "Config/traffic.json"); + + if (File.Exists(ConfigPath)) + { + TrafficAreaList = JsonConvert.DeserializeObject>(File.ReadAllText(ConfigPath)); + + return true; + } + + return false; + } + catch (Exception ex) + { + Console.WriteLine("鍒濆鍖栦氦绠¢厤缃枃浠跺け璐: " + ex.Message); return false; + } + } + + /// + /// 鍒ゆ柇灏忚溅鏄惁鍙互杩涘叆鍖哄煙 + /// + /// + /// + /// + public bool EnterArea(AbstractCar car, int siteId) + { + var TrafficAreas = TrafficAreaList.FindAll(area => area.SiteList.Contains(siteId) && area.IsEnable); + + if (TrafficAreas.Count == 0) { return true; } + + if (!TrafficAreas.Exists(t => t.IsOccupy && t.ControllerName != car.id.ToString())) + { + lock (TrafficAreaList) + { + TrafficAreas.ForEach(t => { t.IsOccupy = true; t.ControllerName = car.id.ToString(); }); + } + + Console.WriteLine($"灏忚溅 {car.id} 杩涘叆浜嗕氦绠″尯鍩 {string.Join(",", TrafficAreas.Select(t => t.AreaName))}"); + + return true; + } + + return false; + } + + /// + /// 绂诲紑鍖哄煙 + /// + /// + /// + /// + public bool LeaveArea(AbstractCar car, int siteId) + { + var TrafficAreas = TrafficAreaList.FindAll(area => area.SiteList.Contains(siteId) && area.IsEnable); + + if (TrafficAreas.Count == 0) { return true; } + + foreach (var item in TrafficAreas) + { + var Sites = SimpleLib.GetAllSites().Where(s => item.SiteList.Contains(s.id)).ToList(); + + if (!Sites.Exists(t => t.status.owner == car.id)) + { + lock (TrafficAreaList) + { + item.ControllerName = string.Empty; item.IsOccupy = false; + } + + Console.WriteLine($"灏忚溅 {car.id} 绂诲紑浜嗕氦绠″尯鍩 {item.AreaName}"); + } + } + + return true; + } + } + + public class TrafficArea + { + /// + /// 鍖哄煙鍚嶇О + /// + public string AreaName { get; set; } + + /// + /// 鍖哄煙绔欑偣闆嗗悎 + /// + public List SiteList { get; set; } + + /// + /// 鎺у埗鏉 + /// + [JsonIgnore] + public string ControllerName { get; set; } + + /// + /// 鏄惁琚崰鐢 + /// + [JsonIgnore] + public bool IsOccupy { get; set; } + + /// + /// 鏄惁鍚敤 + /// + public bool IsEnable { get; set; } + } +} diff --git a/StandardScene.Core/InterLock/TrafficInterlockViewer.Designer.cs b/StandardScene.Core/InterLock/TrafficInterlockViewer.Designer.cs new file mode 100644 index 0000000..e4302aa --- /dev/null +++ b/StandardScene.Core/InterLock/TrafficInterlockViewer.Designer.cs @@ -0,0 +1,325 @@ +using System; +using System.Drawing; +using System.Windows.Forms; + +namespace LoopViewerApp +{ + partial class TrafficInterlockViewer + { + private System.ComponentModel.IContainer components = null; + + private ListView lstTasks; + private GroupBox grpEdit; + + private ColumnHeader colAreaName; + private ColumnHeader colSites; + private ColumnHeader colControlRight; + private ColumnHeader colIsOccupied; + private ColumnHeader colIsEnabled; + + private Label lblAreaName; + private TextBox txtAreaName; + private Label lblStationIds; + private TextBox txtStationIds; + private Label lblControlRight; + private TextBox txtControlRight; + private Label lblIsOccupied; + private CheckBox chkIsOccupied; + private Label lblIsEnabled; + private CheckBox chkIsEnabled; + private Label lblEditingHint; + private Button btnSave; + private Button btnRefresh; + private Button btnNew; + private Button btnDelete; + + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + private void InitializeComponent() + { + this.lstTasks = new System.Windows.Forms.ListView(); + this.colAreaName = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.colSites = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.colControlRight = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.colIsOccupied = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.colIsEnabled = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.grpEdit = new System.Windows.Forms.GroupBox(); + this.lblEditingHint = new System.Windows.Forms.Label(); + this.lblAreaName = new System.Windows.Forms.Label(); + this.txtAreaName = new System.Windows.Forms.TextBox(); + this.lblStationIds = new System.Windows.Forms.Label(); + this.txtStationIds = new System.Windows.Forms.TextBox(); + this.lblControlRight = new System.Windows.Forms.Label(); + this.txtControlRight = new System.Windows.Forms.TextBox(); + this.lblIsOccupied = new System.Windows.Forms.Label(); + this.chkIsOccupied = new System.Windows.Forms.CheckBox(); + this.lblIsEnabled = new System.Windows.Forms.Label(); + this.chkIsEnabled = new System.Windows.Forms.CheckBox(); + this.btnSave = new System.Windows.Forms.Button(); + this.btnRefresh = new System.Windows.Forms.Button(); + this.btnNew = new System.Windows.Forms.Button(); + this.btnDelete = new System.Windows.Forms.Button(); + this.grpEdit.SuspendLayout(); + this.SuspendLayout(); + // + // lstTasks + // + this.lstTasks.BackColor = System.Drawing.Color.White; + this.lstTasks.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] { + this.colAreaName, + this.colSites, + this.colControlRight, + this.colIsOccupied, + this.colIsEnabled}); + this.lstTasks.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(33)))), ((int)(((byte)(33)))), ((int)(((byte)(33))))); + this.lstTasks.FullRowSelect = true; + this.lstTasks.HideSelection = false; + this.lstTasks.Location = new System.Drawing.Point(12, 12); + this.lstTasks.Name = "lstTasks"; + this.lstTasks.OwnerDraw = true; + this.lstTasks.Size = new System.Drawing.Size(760, 320); + this.lstTasks.TabIndex = 0; + this.lstTasks.UseCompatibleStateImageBehavior = false; + this.lstTasks.View = System.Windows.Forms.View.Details; + this.lstTasks.DrawColumnHeader += new System.Windows.Forms.DrawListViewColumnHeaderEventHandler(this.lstTasks_DrawColumnHeader); + this.lstTasks.DrawItem += new System.Windows.Forms.DrawListViewItemEventHandler(this.lstTasks_DrawItem); + this.lstTasks.DrawSubItem += new System.Windows.Forms.DrawListViewSubItemEventHandler(this.lstTasks_DrawSubItem); + this.lstTasks.SelectedIndexChanged += new System.EventHandler(this.lstTasks_SelectedIndexChanged); + // + // colAreaName + // + this.colAreaName.Text = "鍖哄煙鍚嶇О"; + this.colAreaName.Width = 140; + // + // colSites + // + this.colSites.Text = "鍖哄煙绔欑偣闆嗗悎"; + this.colSites.TextAlign = System.Windows.Forms.HorizontalAlignment.Center; + this.colSites.Width = 280; + // + // colControlRight + // + this.colControlRight.Text = "鎺у埗鏉"; + this.colControlRight.TextAlign = System.Windows.Forms.HorizontalAlignment.Center; + this.colControlRight.Width = 120; + // + // colIsOccupied + // + this.colIsOccupied.Text = "鏄惁琚崰鐢"; + this.colIsOccupied.TextAlign = System.Windows.Forms.HorizontalAlignment.Center; + this.colIsOccupied.Width = 100; + // + // colIsEnabled + // + this.colIsEnabled.Text = "鏄惁鍚敤"; + this.colIsEnabled.TextAlign = System.Windows.Forms.HorizontalAlignment.Center; + this.colIsEnabled.Width = 100; + // + // grpEdit + // + this.grpEdit.Controls.Add(this.lblEditingHint); + this.grpEdit.Controls.Add(this.lblAreaName); + this.grpEdit.Controls.Add(this.txtAreaName); + this.grpEdit.Controls.Add(this.lblStationIds); + this.grpEdit.Controls.Add(this.txtStationIds); + this.grpEdit.Controls.Add(this.lblControlRight); + this.grpEdit.Controls.Add(this.txtControlRight); + this.grpEdit.Controls.Add(this.lblIsOccupied); + this.grpEdit.Controls.Add(this.chkIsOccupied); + this.grpEdit.Controls.Add(this.lblIsEnabled); + this.grpEdit.Controls.Add(this.chkIsEnabled); + this.grpEdit.Controls.Add(this.btnSave); + this.grpEdit.Controls.Add(this.btnRefresh); + this.grpEdit.Controls.Add(this.btnNew); + this.grpEdit.Controls.Add(this.btnDelete); + this.grpEdit.Font = new System.Drawing.Font("寰蒋闆呴粦", 10F, System.Drawing.FontStyle.Bold); + this.grpEdit.Location = new System.Drawing.Point(12, 345); + this.grpEdit.Name = "grpEdit"; + this.grpEdit.Size = new System.Drawing.Size(760, 165); + this.grpEdit.TabIndex = 1; + this.grpEdit.TabStop = false; + this.grpEdit.Text = "鏁版嵁鏂板/缂栬緫锛堢偣鍑昏〃鏍艰鍙湪姝ゆ煡鐪嬪苟缂栬緫璇ヨ鏁版嵁锛"; + // + // lblEditingHint + // + this.lblEditingHint.AutoSize = true; + this.lblEditingHint.Font = new System.Drawing.Font("寰蒋闆呴粦", 10F, System.Drawing.FontStyle.Bold); + this.lblEditingHint.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(0)))), ((int)(((byte)(120)))), ((int)(((byte)(215))))); + this.lblEditingHint.Location = new System.Drawing.Point(12, 125); + this.lblEditingHint.Name = "lblEditingHint"; + this.lblEditingHint.Size = new System.Drawing.Size(65, 19); + this.lblEditingHint.TabIndex = 0; + this.lblEditingHint.Text = "鏂板鍖哄煙"; + // + // lblAreaName + // + this.lblAreaName.AutoSize = true; + this.lblAreaName.Font = new System.Drawing.Font("寰蒋闆呴粦", 10F); + this.lblAreaName.Location = new System.Drawing.Point(12, 28); + this.lblAreaName.Name = "lblAreaName"; + this.lblAreaName.Size = new System.Drawing.Size(79, 20); + this.lblAreaName.TabIndex = 1; + this.lblAreaName.Text = "鍖哄煙鍚嶇О锛"; + // + // txtAreaName + // + this.txtAreaName.Font = new System.Drawing.Font("寰蒋闆呴粦", 10F); + this.txtAreaName.Location = new System.Drawing.Point(100, 24); + this.txtAreaName.Name = "txtAreaName"; + this.txtAreaName.Size = new System.Drawing.Size(200, 25); + this.txtAreaName.TabIndex = 2; + // + // lblStationIds + // + this.lblStationIds.AutoSize = true; + this.lblStationIds.Font = new System.Drawing.Font("寰蒋闆呴粦", 10F); + this.lblStationIds.Location = new System.Drawing.Point(320, 28); + this.lblStationIds.Name = "lblStationIds"; + this.lblStationIds.Size = new System.Drawing.Size(79, 20); + this.lblStationIds.TabIndex = 3; + this.lblStationIds.Text = "绔欑偣闆嗗悎锛"; + // + // txtStationIds + // + this.txtStationIds.Font = new System.Drawing.Font("寰蒋闆呴粦", 10F); + this.txtStationIds.Location = new System.Drawing.Point(418, 24); + this.txtStationIds.Name = "txtStationIds"; + this.txtStationIds.Size = new System.Drawing.Size(320, 25); + this.txtStationIds.TabIndex = 4; + // + // lblControlRight + // + this.lblControlRight.AutoSize = true; + this.lblControlRight.Font = new System.Drawing.Font("寰蒋闆呴粦", 10F); + this.lblControlRight.Location = new System.Drawing.Point(12, 58); + this.lblControlRight.Name = "lblControlRight"; + this.lblControlRight.Size = new System.Drawing.Size(65, 20); + this.lblControlRight.TabIndex = 5; + this.lblControlRight.Text = "鎺у埗鏉冿細"; + // + // txtControlRight + // + this.txtControlRight.Font = new System.Drawing.Font("寰蒋闆呴粦", 10F); + this.txtControlRight.Location = new System.Drawing.Point(100, 54); + this.txtControlRight.Name = "txtControlRight"; + this.txtControlRight.Size = new System.Drawing.Size(200, 25); + this.txtControlRight.TabIndex = 6; + // + // lblIsOccupied + // + this.lblIsOccupied.AutoSize = true; + this.lblIsOccupied.Font = new System.Drawing.Font("寰蒋闆呴粦", 10F); + this.lblIsOccupied.Location = new System.Drawing.Point(320, 58); + this.lblIsOccupied.Name = "lblIsOccupied"; + this.lblIsOccupied.Size = new System.Drawing.Size(93, 20); + this.lblIsOccupied.TabIndex = 7; + this.lblIsOccupied.Text = "鏄惁琚崰鐢細"; + // + // chkIsOccupied + // + this.chkIsOccupied.AutoSize = true; + this.chkIsOccupied.Font = new System.Drawing.Font("Segoe UI", 9F); + this.chkIsOccupied.Location = new System.Drawing.Point(418, 59); + this.chkIsOccupied.Name = "chkIsOccupied"; + this.chkIsOccupied.Size = new System.Drawing.Size(39, 19); + this.chkIsOccupied.TabIndex = 8; + this.chkIsOccupied.Text = "鏄"; + // + // lblIsEnabled + // + this.lblIsEnabled.AutoSize = true; + this.lblIsEnabled.Font = new System.Drawing.Font("寰蒋闆呴粦", 10F); + this.lblIsEnabled.Location = new System.Drawing.Point(500, 58); + this.lblIsEnabled.Name = "lblIsEnabled"; + this.lblIsEnabled.Size = new System.Drawing.Size(79, 20); + this.lblIsEnabled.TabIndex = 9; + this.lblIsEnabled.Text = "鏄惁鍚敤锛"; + // + // chkIsEnabled + // + this.chkIsEnabled.AutoSize = true; + this.chkIsEnabled.Checked = true; + this.chkIsEnabled.CheckState = System.Windows.Forms.CheckState.Checked; + this.chkIsEnabled.Font = new System.Drawing.Font("Segoe UI", 9F); + this.chkIsEnabled.Location = new System.Drawing.Point(585, 59); + this.chkIsEnabled.Name = "chkIsEnabled"; + this.chkIsEnabled.Size = new System.Drawing.Size(39, 19); + this.chkIsEnabled.TabIndex = 10; + this.chkIsEnabled.Text = "鏄"; + // + // btnSave + // + this.btnSave.BackColor = System.Drawing.Color.LightBlue; + this.btnSave.FlatStyle = System.Windows.Forms.FlatStyle.Flat; + this.btnSave.Font = new System.Drawing.Font("寰蒋闆呴粦", 11F, System.Drawing.FontStyle.Bold); + this.btnSave.Location = new System.Drawing.Point(260, 118); + this.btnSave.Name = "btnSave"; + this.btnSave.Size = new System.Drawing.Size(140, 40); + this.btnSave.TabIndex = 12; + this.btnSave.Text = "淇濆瓨"; + this.btnSave.UseVisualStyleBackColor = false; + this.btnSave.Click += new System.EventHandler(this.btnSave_Click); + // + // btnRefresh + // + this.btnRefresh.BackColor = System.Drawing.SystemColors.Control; + this.btnRefresh.FlatStyle = System.Windows.Forms.FlatStyle.Flat; + this.btnRefresh.Font = new System.Drawing.Font("寰蒋闆呴粦", 11F); + this.btnRefresh.Location = new System.Drawing.Point(410, 118); + this.btnRefresh.Name = "btnRefresh"; + this.btnRefresh.Size = new System.Drawing.Size(140, 40); + this.btnRefresh.TabIndex = 13; + this.btnRefresh.Text = "鍒锋柊"; + this.btnRefresh.UseVisualStyleBackColor = false; + this.btnRefresh.Click += new System.EventHandler(this.btnRefresh_Click); + // + // btnNew + // + this.btnNew.BackColor = System.Drawing.SystemColors.Control; + this.btnNew.FlatStyle = System.Windows.Forms.FlatStyle.Flat; + this.btnNew.Font = new System.Drawing.Font("寰蒋闆呴粦", 11F); + this.btnNew.Location = new System.Drawing.Point(560, 118); + this.btnNew.Name = "btnNew"; + this.btnNew.Size = new System.Drawing.Size(140, 40); + this.btnNew.TabIndex = 14; + this.btnNew.Text = "鏂板"; + this.btnNew.UseVisualStyleBackColor = false; + this.btnNew.Click += new System.EventHandler(this.btnNew_Click); + // + // btnDelete + // + this.btnDelete.BackColor = System.Drawing.SystemColors.Control; + this.btnDelete.FlatStyle = System.Windows.Forms.FlatStyle.Flat; + this.btnDelete.Font = new System.Drawing.Font("寰蒋闆呴粦", 11F); + this.btnDelete.Location = new System.Drawing.Point(110, 118); + this.btnDelete.Name = "btnDelete"; + this.btnDelete.Size = new System.Drawing.Size(140, 40); + this.btnDelete.TabIndex = 11; + this.btnDelete.Text = "鍒犻櫎"; + this.btnDelete.UseVisualStyleBackColor = false; + this.btnDelete.Click += new System.EventHandler(this.btnDelete_Click); + // + // TrafficInterlockViewer + // + this.ClientSize = new System.Drawing.Size(784, 521); + this.Controls.Add(this.lstTasks); + this.Controls.Add(this.grpEdit); + this.Font = new System.Drawing.Font("寰蒋闆呴粦", 9F); + this.MinimumSize = new System.Drawing.Size(700, 450); + this.Name = "TrafficInterlockViewer"; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen; + this.Text = "浜ら氳仈閿佸尯鍩熺鐞"; + this.grpEdit.ResumeLayout(false); + this.grpEdit.PerformLayout(); + this.ResumeLayout(false); + + } + } +} diff --git a/StandardScene.Core/InterLock/TrafficInterlockViewer.cs b/StandardScene.Core/InterLock/TrafficInterlockViewer.cs new file mode 100644 index 0000000..c8fb195 --- /dev/null +++ b/StandardScene.Core/InterLock/TrafficInterlockViewer.cs @@ -0,0 +1,370 @@ +using Newtonsoft.Json; +using StandardScene.InterLock; // 鏁版嵁绫诲瀷閲囩敤 TrafficInterlockMission 涓殑 TrafficArea +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Drawing; +using System.IO; +using System.Linq; +using System.Windows.Forms; + +namespace LoopViewerApp +{ + public partial class TrafficInterlockViewer : Form + { + /// -1 琛ㄧず鏂板妯″紡锛>=0 琛ㄧず姝e湪缂栬緫瀵瑰簲绱㈠紩 + private int _editingIndex = -1; + + /// 閫変腑琛屽彉鍖栨椂鏄惁鍏佽鍔犺浇鍒扮紪杈戝尯锛堥伩鍏嶅湪淇濆瓨/鍙栨秷鏃堕噸澶嶅埛鏂帮級 + private bool _allowLoadFromSelection = true; + + public TrafficInterlockViewer() + { + InitializeComponent(); + + if (LicenseManager.UsageMode == LicenseUsageMode.Designtime) + return; + + try + { + RenderListView(); + ClearPanelInputs(); + } + catch (Exception ex) + { + System.Diagnostics.Debug.WriteLine($"TrafficInterlockViewer init error: {ex}"); + } + } + + #region 琛ㄦ牸缁樺埗锛堝彧璇诲睍绀猴級 + + private void lstTasks_DrawColumnHeader(object sender, DrawListViewColumnHeaderEventArgs e) + { + try + { + // 涓 LoopViewer 涓鑷达細娣辫摑琛ㄥご + 鐧借壊鍔犵矖瀛椾綋 + using (var backBrush = new SolidBrush(Color.FromArgb(63, 81, 181))) + using (var textBrush = new SolidBrush(Color.White)) + using (var font = new Font("寰蒋闆呴粦", 9, FontStyle.Bold)) + { + e.Graphics.FillRectangle(backBrush, e.Bounds); + var sf = new StringFormat { LineAlignment = StringAlignment.Center, Alignment = StringAlignment.Near }; + var rect = e.Bounds; + rect.Inflate(-8, 0); + e.Graphics.DrawString(e.Header.Text, font, textBrush, rect, sf); + using (var pen = new Pen(Color.FromArgb(200, 200, 200))) + e.Graphics.DrawLine(pen, e.Bounds.Left, e.Bounds.Bottom - 1, e.Bounds.Right, e.Bounds.Bottom - 1); + } + } + catch + { + e.DrawBackground(); + e.DrawText(); + } + } + + private void lstTasks_DrawItem(object sender, DrawListViewItemEventArgs e) + { + // 鐢 DrawSubItem 缁熶竴缁樺埗 + } + + private void lstTasks_DrawSubItem(object sender, DrawListViewSubItemEventArgs e) + { + try + { + bool selected = e.Item.Selected; + Rectangle bounds = e.Bounds; + // 涓 LoopViewer 涓鑷达細閫変腑琛岃摑鑹插己璋冿紝浜ゆ浛琛岃儗鏅紝娣辩伆鏂囧瓧 + Color selectedBack = Color.FromArgb(0, 120, 215); + Color selectedFore = Color.White; + Color evenBack = Color.White; + Color oddBack = Color.FromArgb(250, 251, 253); + Color normalFore = Color.FromArgb(33, 33, 33); + + if (selected) + { + using (var selBrush = new SolidBrush(selectedBack)) + e.Graphics.FillRectangle(selBrush, bounds); + } + else + { + using (var back = new SolidBrush(e.ItemIndex % 2 == 0 ? evenBack : oddBack)) + e.Graphics.FillRectangle(back, bounds); + } + + string text = e.SubItem?.Text ?? string.Empty; + Color fore = selected ? selectedFore : normalFore; + var textRect = bounds; + textRect.Inflate(-6, 0); + using (var font = new Font("寰蒋闆呴粦", 9)) + TextRenderer.DrawText(e.Graphics, text, font, textRect, fore, TextFormatFlags.Left | TextFormatFlags.VerticalCenter); + } + catch + { + e.DrawBackground(); + e.DrawText(); + } + } + + #endregion + + #region 鍒楄〃娓叉煋涓庝繚瀛 + + private void RenderListView() + { + try + { + if (lstTasks == null) return; + _allowLoadFromSelection = false; + lstTasks.BeginUpdate(); + lstTasks.Items.Clear(); + foreach (var a in TrafficInterlockMission.TrafficAreaList) + { + var stationStr = a.SiteList != null && a.SiteList.Count > 0 ? string.Join(", ", a.SiteList) : ""; + + var lvi = new ListViewItem(new[] + { + a.AreaName ?? "", + stationStr, + a.ControllerName ?? "", + a.IsOccupy ? "鏄" : "鍚", + a.IsEnable ? "鏄" : "鍚" + }); + + lstTasks.Items.Add(lvi); + } + lstTasks.EndUpdate(); + _allowLoadFromSelection = true; + } + catch (Exception ex) + { + _allowLoadFromSelection = true; + System.Diagnostics.Debug.WriteLine($"RenderListView failed: {ex}"); + } + } + + private void SaveToConfig() + { + try + { + string configPath = Path.Combine(Application.StartupPath, "Config", "traffic.json"); + string dir = Path.GetDirectoryName(configPath); + if (!string.IsNullOrEmpty(dir) && !Directory.Exists(dir)) + Directory.CreateDirectory(dir); + File.WriteAllText(configPath, JsonConvert.SerializeObject(TrafficInterlockMission.TrafficAreaList, Formatting.Indented)); + } + catch (Exception ex) + { + MessageBox.Show("淇濆瓨澶辫触锛" + ex.Message); + } + } + + #endregion + + #region 鐐瑰嚮琛ㄦ牸琛 鈫 缂栬緫鍖哄睍绀鸿琛屾暟鎹 + + private void lstTasks_SelectedIndexChanged(object sender, EventArgs e) + { + if (!_allowLoadFromSelection || lstTasks == null || lstTasks.SelectedIndices.Count == 0) return; + int idx = lstTasks.SelectedIndices[0]; + if (idx < 0 || idx >= TrafficInterlockMission.TrafficAreaList.Count) return; + _editingIndex = idx; + LoadAreaToPanel(TrafficInterlockMission.TrafficAreaList[idx]); + } + + #endregion + + #region 缂栬緫鍖猴細鍔犺浇 / 娓呯┖ + + private void LoadAreaToPanel(TrafficArea a) + { + if (a == null) return; + try + { + if (lblEditingHint != null) + lblEditingHint.Text = $"缂栬緫锛歿a.AreaName}"; + if (txtAreaName != null) + txtAreaName.Text = a.AreaName ?? ""; + if (txtStationIds != null) + txtStationIds.Text = a.SiteList != null && a.SiteList.Count > 0 + ? string.Join(", ", a.SiteList) + : ""; + if (txtControlRight != null) + txtControlRight.Text = a.ControllerName ?? ""; + if (chkIsOccupied != null) + chkIsOccupied.Checked = a.IsOccupy; + if (chkIsEnabled != null) + chkIsEnabled.Checked = a.IsEnable; + } + catch (Exception ex) + { + System.Diagnostics.Debug.WriteLine($"LoadAreaToPanel error: {ex}"); + } + } + + private void ClearPanelInputs() + { + try + { + _editingIndex = -1; + if (lblEditingHint != null) + lblEditingHint.Text = "鏂板鍖哄煙"; + if (txtAreaName != null) + txtAreaName.Text = ""; + if (txtStationIds != null) + txtStationIds.Text = ""; + if (txtControlRight != null) + txtControlRight.Text = ""; + if (chkIsOccupied != null) + chkIsOccupied.Checked = false; + if (chkIsEnabled != null) + chkIsEnabled.Checked = true; + } + catch (Exception ex) + { + System.Diagnostics.Debug.WriteLine($"ClearPanelInputs error: {ex}"); + } + } + + #endregion + + #region 瑙f瀽绔欑偣闆嗗悎瀛楃涓 "1,2,3" -> List + + private static List ParseStationIds(string text) + { + var list = new List(); + if (string.IsNullOrWhiteSpace(text)) return list; + foreach (var part in text.Split(new[] { ',', ';', ' ', '\t' }, StringSplitOptions.RemoveEmptyEntries)) + { + if (int.TryParse(part.Trim(), out int id)) + list.Add(id); + } + return list; + } + + #endregion + + #region 鎸夐挳锛氫繚瀛 / 鍒锋柊 / 鏂板 / 鍒犻櫎 + + private void btnSave_Click(object sender, EventArgs e) + { + try + { + string areaName = txtAreaName?.Text?.Trim() ?? ""; + if (string.IsNullOrEmpty(areaName)) + { + MessageBox.Show("璇疯緭鍏ュ尯鍩熷悕绉般"); + return; + } + + var stationIds = ParseStationIds(txtStationIds?.Text ?? ""); + string controlRight = txtControlRight?.Text?.Trim() ?? ""; + bool isOccupied = chkIsOccupied?.Checked ?? false; + bool isEnabled = chkIsEnabled?.Checked ?? true; + + if (_editingIndex >= 0 && _editingIndex < TrafficInterlockMission.TrafficAreaList.Count) + { + lock (TrafficInterlockMission.TrafficAreaList) + { + var existing = TrafficInterlockMission.TrafficAreaList[_editingIndex]; + existing.AreaName = areaName; + existing.SiteList = stationIds; + existing.ControllerName = controlRight; + existing.IsOccupy = isOccupied; + existing.IsEnable = isEnabled; + } + } + else + { + lock (TrafficInterlockMission.TrafficAreaList) + { + TrafficInterlockMission.TrafficAreaList.Add(new TrafficArea + { + AreaName = areaName, + SiteList = stationIds, + ControllerName = controlRight, + IsOccupy = isOccupied, + IsEnable = isEnabled + }); + } + } + + SaveToConfig(); + RenderListView(); + ClearPanelInputs(); + } + catch (Exception ex) + { + System.Diagnostics.Debug.WriteLine($"btnSave_Click error: {ex}"); + MessageBox.Show("鎿嶄綔澶辫触锛" + ex.Message); + } + } + + private void btnRefresh_Click(object sender, EventArgs e) + { + try + { + RenderListView(); + } + catch (Exception ex) + { + System.Diagnostics.Debug.WriteLine($"btnRefresh_Click error: {ex}"); + } + } + + private void btnNew_Click(object sender, EventArgs e) + { + if (lstTasks != null) + lstTasks.SelectedIndices.Clear(); + ClearPanelInputs(); + // 杩涘叆鏂板妯″紡锛氬~鍐欎笅鏂圭紪杈戝尯鍚庣偣鍑烩滀繚瀛樷濆嵆鍙柊澧炰竴鏉℃暟鎹 + } + + private void btnDelete_Click(object sender, EventArgs e) + { + try + { + if (lstTasks == null || lstTasks.SelectedIndices.Count == 0) + { + MessageBox.Show("璇峰厛鍦ㄤ笂鏂瑰垪琛ㄤ腑閫夋嫨瑕佸垹闄ょ殑鍖哄煙銆"); + return; + } + + var dialogResult = MessageBox.Show( + "纭畾瑕佸垹闄ら変腑鐨勫尯鍩熷悧锛", + "纭鍒犻櫎", + MessageBoxButtons.YesNo, + MessageBoxIcon.Warning); + + if (dialogResult != DialogResult.Yes) + return; + + var indices = lstTasks.SelectedIndices.Cast() + .OrderByDescending(i => i) + .ToList(); + + lock (TrafficInterlockMission.TrafficAreaList) + { + foreach (var idx in indices) + { + if (idx >= 0 && idx < TrafficInterlockMission.TrafficAreaList.Count) + { + TrafficInterlockMission.TrafficAreaList.RemoveAt(idx); + } + } + } + + SaveToConfig(); + RenderListView(); + ClearPanelInputs(); + } + catch (Exception ex) + { + System.Diagnostics.Debug.WriteLine($"btnDelete_Click error: {ex}"); + MessageBox.Show("鍒犻櫎澶辫触锛" + ex.Message); + } + } + + #endregion + } +} diff --git a/StandardScene.Core/InterLock/TrafficInterlockViewer.resx b/StandardScene.Core/InterLock/TrafficInterlockViewer.resx new file mode 100644 index 0000000..1af7de1 --- /dev/null +++ b/StandardScene.Core/InterLock/TrafficInterlockViewer.resx @@ -0,0 +1,120 @@ +锘 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/StandardScene.Core/LadderLogic.cs b/StandardScene.Core/LadderLogic.cs new file mode 100644 index 0000000..d1b4cb6 --- /dev/null +++ b/StandardScene.Core/LadderLogic.cs @@ -0,0 +1,107 @@ +锘縰sing SimpleCore.Library; +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Threading.Tasks; + + +namespace StandardScene +{ + public class LadderLogic + { + /// below defines some useful functions... + + private static ConcurrentDictionary pressedDT = new ConcurrentDictionary(); + // if active for millis, trigger once. + public static void TriggerOnce(bool active, int millis, Action trigger,int index=0, + [CallerFilePath] string sourceFilePath = "", [CallerLineNumber] int sourceLineNumber = 0) + { + var id = $"{sourceFilePath}:{sourceLineNumber+ index}"; + if (!active) + { + if (pressedDT.TryGetValue(id, out var pair1) && pair1.state != 1) + pressedDT.TryRemove(id, out _); + return; + } + if (pressedDT.TryGetValue(id, out var pair)) + { + if (pair.state != 0) return; + if ((DateTime.Now - pair.dt).TotalMilliseconds > millis) + { + pressedDT[id] = (pair.dt, 1); + Task.Run(() => + { + try + { + trigger(); + } + catch (Exception ex) + { + Diagnosis.Post($"Ex={ExceptionFormatter.FormatEx(ex)}", $"timed_trigger-{id}"); + pressedDT.TryRemove(id, out _); + } + + pressedDT[id] = (pair.dt, 2); + }); + } + } + else + pressedDT[id] = (DateTime.Now, 0); + } + + private static ConcurrentDictionary isochronous = new ConcurrentDictionary(); + public static object lockobj = new object(); + public static void IsochronousFork(Action action, + [CallerFilePath] string sourceFilePath = "", [CallerLineNumber] int sourceLineNumber = 0) + { + var id = $"{sourceFilePath}:{sourceLineNumber}"; + lock (lockobj) + { + if (isochronous.TryGetValue(id, out var calling) && calling) return; + isochronous[id] = true; + } + + Task.Run(() => + { + try + { + action(); + } + catch (Exception ex) + { + Diagnosis.Post($"Ex={ExceptionFormatter.FormatEx(ex)}", $"isochrounous_fork-{id}"); + } + isochronous[id] = false; + }); + } + + private static Dictionary keepTrack = new Dictionary(); + /// + /// including first call. + /// + /// + /// + /// action(T1 old) + public static void TriggerIfChanged(Func getter, Action action) + { + var id = getter.Method; + var val = getter.Invoke(); + if (keepTrack.TryGetValue(id, out var t) && t is T1 old) + { + if (val.Equals(old)) return; + action(old); + } + else action(default); + keepTrack[id] = val; + } + + + public static void FlipFlop(ref T1 target, int millis, params T1[] vs) + { + target = vs[(((long)DateTime.Now.TimeOfDay.TotalMilliseconds) / millis) % vs.Length]; + } + + } +} \ No newline at end of file diff --git a/StandardScene.Core/Model/ChargingSetting.cs b/StandardScene.Core/Model/ChargingSetting.cs new file mode 100644 index 0000000..a7a9931 --- /dev/null +++ b/StandardScene.Core/Model/ChargingSetting.cs @@ -0,0 +1,31 @@ +锘縰sing System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace StandardScene.Model +{ + public class ChargingSetting + { + // 鍏呯數鐢甸噺涓嬮檺锛岃〃绀鸿澶囧厖鐢垫椂鏈浣庣殑鐢甸噺闄愬埗 + public int CarMinBattery { get; set; } + + // 鍏呯數鐢甸噺涓婇檺锛岃〃绀鸿澶囧厖鐢垫椂鏈楂樼殑鐢甸噺闄愬埗 + public int CarMaxBattery { get; set; } + + // 鍏呯數瀹夊叏鐢甸噺锛岃〃绀鸿澶囧厖鐢垫椂鐨勫畨鍏ㄧ數閲忛槇鍊硷紝浣庝簬姝ゅ煎彲鑳戒細褰卞搷璁惧鐨勬甯镐娇鐢 + public int CarIdleChargeBattery { get; set; } + + // 鍏呯數鐢甸噺鏃堕暱锛岃〃绀鸿澶囧厖鐢垫墍闇鐨勬椂闂撮暱搴 + public int CarIdleSecond { get; set; } + + // 鍏佽浠诲姟鎵撴柇鐨勫厖鐢电數閲忎笅闄愶紝琛ㄧず鍦ㄦ墽琛屾煇浜涗换鍔℃椂锛岃澶囧彲浠ュ蹇嶇殑鏈浣庣數閲忛檺鍒 + public int TaskAvailableBattery { get; set; } + + // 闂叉椂鍏呯數锛岃〃绀鸿澶囧湪绌洪棽鐘舵佷笅鏄惁杩涜鍏呯數 + public bool IsChargingDuringIdleTime { get; set; } + + + } +} diff --git a/StandardScene.Core/Model/EnvelopeSetting.cs b/StandardScene.Core/Model/EnvelopeSetting.cs new file mode 100644 index 0000000..735c974 --- /dev/null +++ b/StandardScene.Core/Model/EnvelopeSetting.cs @@ -0,0 +1,15 @@ +锘縰sing System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace StandardScene.Model +{ + public class EnvelopeSetting + { + public string Name { get; set; } + public string Description { get; set; } + public string Value { get; set; } + } +} diff --git a/StandardScene.Core/Model/LoopTask.cs b/StandardScene.Core/Model/LoopTask.cs new file mode 100644 index 0000000..0c01994 --- /dev/null +++ b/StandardScene.Core/Model/LoopTask.cs @@ -0,0 +1,76 @@ +锘縰sing System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace StandardScene.Model +{ + /// + /// 浠诲姟绫诲埆 + /// + public enum TaskKind + { + Loop, + BranchPoint, + JoinPoint + } + + /// + /// 浠诲姟鍚姩绫诲瀷锛欰PI/PLC/鎸夐挳鐩/鑷姩寰幆 + /// + public enum TaskStartType + { + Api, + Plc, + ButtonBox, + AutoLoop, + Charge + } + + public class LoopTask + { + /// + /// 浠诲姟鍞竴鏍囪瘑ID锛堣嚜澧烇級 + /// + public int Id { get; set; } = 0; + + /// + /// 浠诲姟绫诲埆锛堟灇涓撅細寰幆/鍒嗘祦鐐/姹囧悎鐐癸級 + /// + public TaskKind Kind { get; set; } = TaskKind.Loop; + + /// + /// 褰撳墠鐐 ID锛堟暣鏁帮級 + /// + public int CurrentStationId { get; set; } = -1; + + /// + /// 鐩爣鐐 ID锛堟暣鏁帮級 + /// + public int TargetStationId { get; set; } = -1; + + /// + /// 娴侀噺鎺у埗锛堟暣鏁帮紝鍙唬琛ㄤ俊鍙风骇鍒垨绛栫暐缂栧彿锛 + /// + public int TrafficControl { get; set; } = 0; + + /// + /// 浼樺厛绾э紙鏁存暟锛 + /// + public int Priority { get; set; } = 1; + + /// + /// 鏄惁涓洪斿緞鐐癸紙甯冨皵锛 + /// + public bool IsViaPoint { get; set; } = false; + + /// + /// 鍚姩绫诲瀷锛堟灇涓撅細Api/Plc/ButtonBox/AutoLoop锛 + /// + public TaskStartType StartType { get; set; } = TaskStartType.AutoLoop; + + + public string[] tags = Array.Empty(); + } +} diff --git a/StandardScene.Core/Model/Map.cs b/StandardScene.Core/Model/Map.cs new file mode 100644 index 0000000..ec9ff92 --- /dev/null +++ b/StandardScene.Core/Model/Map.cs @@ -0,0 +1,241 @@ +using SimpleCore.PropType; +using SimpleCore; +using System.Collections.Generic; +using System.Dynamic; +using SimpleLite.CADTools; +using SimpleLite.Props; +using SimpleLite.UI; +using System.Numerics; +using System.Drawing; +using System; +using System.Drawing.Imaging; +using System.IO; +using System.Linq; +using System.Net.Http; +using System.Threading.Tasks; +using System.Windows.Forms; +using Newtonsoft.Json; +using SimpleLite.RCS; +using SimpleLite.RCS.CarTypes; + +namespace StandardScene.Model +{ + public class Map + { + public List Sites { get; set; } + public List Tracks { get; set; } + public List CircularArcTracks { get; set; } + public List BezierTracks { get; set; } + + public static Map GetMap() + { + var map = new Map + { + Sites = new List() + }; + Site[] allSites = SimpleLib.GetAllSites(); + foreach (var site in allSites) + { + dynamic theSite = new ExpandoObject(); + theSite.Id = ((Prop)site).id; + theSite.Name = ((Prop)site).name; + theSite.X = site.x; + theSite.Y = site.y; + theSite.Fields = new Dictionary(); + foreach (var kv in site.fields) + { + theSite.Fields.Add(kv.Key, kv.Value); + } + theSite.Type = theSite.Fields.ContainsKey("Standby") ? "standby" : theSite.Fields.ContainsKey("Charge") ? "charge" : theSite.Fields.ContainsKey("Shelf") ? "shelf" : "default"; + map.Sites.Add(theSite); + } + + map.Tracks = new List(); + map.CircularArcTracks = new List(); + map.BezierTracks = new List(); + var allTracks = SimpleLib.GetAllTracks(); + foreach (var track in allTracks) + { + switch (track) + { + case UITrack uiTrack: + { + dynamic theTrack = new ExpandoObject(); + theTrack.Id = uiTrack.id; + theTrack.A = uiTrack.siteA; + theTrack.B = uiTrack.siteB; + theTrack.Fields = new Dictionary(); + foreach (KeyValuePair kv in uiTrack.fields) + { + theTrack.Fields.Add(kv.Key, kv.Value); + } + + theTrack.Direction = uiTrack.direction switch + { + 1 => "a2b", + 2 => "b2a", + _ => "both" + }; + theTrack.Type = 0; + map.Tracks.Add(theTrack); + break; + } + case UICircularArcTrack circularArcTrack: + { + dynamic theTrack = new ExpandoObject(); + theTrack.Id = circularArcTrack.id; + theTrack.A = circularArcTrack.siteA; + theTrack.B = circularArcTrack.siteB; + theTrack.Fields = new Dictionary(); + foreach (var kv in circularArcTrack.fields) + { + theTrack.Fields.Add(kv.Key, kv.Value); + } + + theTrack.Direction = circularArcTrack.direction switch + { + 1 => "a2b", + 2 => "b2a", + _ => "both" + }; + //娣诲姞鍘熺偣鍧愭爣銆佸崐寰勩佸紑濮嬭搴︺佺粨鏉熻搴 + theTrack.Type = 1; + theTrack.CenterX = circularArcTrack.Arc.Center.X; + theTrack.CenterY = circularArcTrack.Arc.Center.Y; + theTrack.Radius = circularArcTrack.Arc.Radius; + theTrack.AngleStart = circularArcTrack.Arc.AngleStart; + theTrack.AngleEnd = circularArcTrack.Arc.AngleEnd; + //鑾峰彇鍦嗗姬鐨勬帶鍒剁偣 + var controlPoint = ArcHelper.CalculateControlPoint( + circularArcTrack.Arc.PointStart.X, circularArcTrack.Arc.PointStart.Y, + circularArcTrack.Arc.PointEnd.X, circularArcTrack.Arc.PointEnd.Y, + circularArcTrack.Arc.Center.X, circularArcTrack.Arc.Center.Y, + circularArcTrack.Arc.Radius, + circularArcTrack.Arc.AngleStart, circularArcTrack.Arc.AngleEnd); + theTrack.ControlPointsX = controlPoint.X; + theTrack.ControlPointsY = controlPoint.Y; + map.CircularArcTracks.Add(theTrack); + break; + } + case UIBezierTrack bezierTrack: + { + dynamic theTrack = new ExpandoObject(); + theTrack.Id = bezierTrack.id; + theTrack.A = bezierTrack.siteA; + theTrack.B = bezierTrack.siteB; + theTrack.Fields = new Dictionary(); + foreach (var kv in bezierTrack.fields) + { + theTrack.Fields.Add(kv.Key, kv.Value); + } + + theTrack.Direction = bezierTrack.direction switch + { + 1 => "a2b", + 2 => "b2a", + _ => "both" + }; + //鏍规嵁typeInfo鑾峰彇鎺у埗鐐逛釜鏁 + theTrack.Type = 2; + var array = bezierTrack.typeInfo.Split(','); + var num = int.Parse(array[1]);//鑾峰彇鎺у埗鐐圭殑涓暟(鍖呮嫭璧风偣鍜岀粓鐐) + var list = new List(); + for (var i = 0; i < num; i++) + { + //鍙褰曚腑闂寸殑鎺у埗鐐 + if (i <= 0 || i >= num - 1) continue; + float x = float.Parse(array[2 + i * 2]); + float y = float.Parse(array[3 + i * 2]); + list.Add(new Vector2(x, y)); + } + theTrack.controlPoints = list; + map.BezierTracks.Add(theTrack); + break; + } + default: + break; + } + } + + return map; + } + } + + public class ArcHelper + { + /// + /// 鑾峰彇鍦嗗姬鎺у埗鐐 + /// + /// + public static PointF CalculateControlPoint(float pointStartX, float pointStartY, float pointEndX, float pointEndY, float centerX, float centerY, float radius, float angleStart, float angleEnd) + { + PointF center = new PointF(centerX, centerY); + double startAngle = angleStart * 3.14159 / 180.0; + double endAngel = angleEnd * 3.14159 / 180.0; + if (angleStart > angleEnd)//濡傛灉璧峰瑙掑害灏忎簬缁撴潫瑙掑害灏卞姞涓360搴 + { + endAngel += 2 * 3.14159; + } + PointF midPoint = new PointF( + (float)(center.X + radius * Math.Cos((startAngle + endAngel) / 2)), + (float)(center.Y + radius * Math.Sin((startAngle + endAngel) / 2))); + return midPoint; + } + } + + public class LidarMap + { + public string LidarMapsBase64 { get; set; } + public float DistanceX { get; set; } + public float DistanceY { get; set; } + public float Ratio { get; set; } + private static readonly HttpClient hc = new HttpClient(); + public static async Task GetLidarMap() + { + var par = JsonConvert.DeserializeAnonymousType(await hc.GetStringAsync($"http://127.0.0.1:4321/getMapParameters"), + new { up = 0f, down = 0f, left = 0f, right = 0f, pt = 0 }); + var lengthWidthRatio =Math.Abs((par.right - par.left) / (par.up - par.down)); + var height = 1024;var width = 1024; + if (lengthWidthRatio > 1) + { + height = (int)(1024 / lengthWidthRatio); + } + else + { + width = (int)(1024 * lengthWidthRatio); + } + var mapBmp= new Bitmap((await hc.GetStreamAsync($"http://127.0.0.1:4321/getMapPng?width={width}&height={height}"))); + for(int x =0;x 1?Math.Abs(par.left-par.right)/1024: Math.Abs(par.up - par.down)/1024, + }; + } + + static string BitmapToBase64(Bitmap bmp, ImageFormat format) + { + using MemoryStream ms = new MemoryStream(); + bmp.Save(ms, format); // 淇濆瓨鏍煎紡鍙互鏄疨ng, Jpeg绛 + byte[] imageBytes = ms.ToArray(); + string base64String = Convert.ToBase64String(imageBytes); + return base64String; + } + } +} diff --git a/StandardScene.Core/Model/MapStructure.cs b/StandardScene.Core/Model/MapStructure.cs new file mode 100644 index 0000000..01327ed --- /dev/null +++ b/StandardScene.Core/Model/MapStructure.cs @@ -0,0 +1,226 @@ +锘縰sing System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace StandardScene.Model +{ + + public class MapStructure + { + public FassMap Map { get; set; } + public List Nodes { get; set; } + public List Edges { get; set; } + public List Zones { get; set; } + public List Tags { get; set; } + } + + public class FassMap + { + public int Index { get; set; } + public string Id { get; set; } + public string Kind { get; set; } + public string Type { get; set; } + public Base Base { get; set; } + public Image Image { get; set; } + public List Extends { get; set; } + } + + public class Base + { + public bool Visible { get; set; } + public Size Size { get; set; } + public double GlobalAlpha { get; set; } + public int LineWidth { get; set; } + public List LineDash { get; set; } + public string StrokeStyle { get; set; } + public string FillStyle { get; set; } + public Center Center { get; set; } + } + + public class Size + { + public double W { get; set; } + public double H { get; set; } + } + + public class Center + { + public double X { get; set; } + public double Y { get; set; } + } + + public class Image + { + public bool Visible { get; set; } + public double GlobalAlpha { get; set; } + public string Src { get; set; } + public bool Origin { get; set; } + public bool Manual { get; set; } + public ManualPoint ManualPoint { get; set; } + public ManualSize ManualSize { get; set; } + } + + public class ManualPoint + { + public double X { get; set; } + public double Y { get; set; } + } + + public class ManualSize + { + public double W { get; set; } + public double H { get; set; } + } + + public class FassNode + { + public int Index { get; set; } + public string Id { get; set; } + public string Kind { get; set; } + public string Type { get; set; } + public NodeBase Base { get; set; } + public Code Code { get; set; } + public Name Name { get; set; } + public Image Image { get; set; } + public Lock Lock { get; set; } + public Data Data { get; set; } + public List Extends { get; set; } + } + + public class NodeBase + { + public bool Visible { get; set; } + public Point Point { get; set; } + public Size Size { get; set; } + public double GlobalAlpha { get; set; } + public int LineWidth { get; set; } + public List LineDash { get; set; } + public string StrokeStyle { get; set; } + public string FillStyle { get; set; } + public Center Center { get; set; } + } + + public class Point + { + public double X { get; set; } + public double Y { get; set; } + } + + public class Code + { + public bool Visible { get; set; } + public double GlobalAlpha { get; set; } + public string Font { get; set; } + public string FillStyle { get; set; } + public string Text { get; set; } + } + + public class Name + { + public bool Visible { get; set; } + public double GlobalAlpha { get; set; } + public string Font { get; set; } + public string FillStyle { get; set; } + public string Text { get; set; } + } + + public class Lock + { + public bool Enable { get; set; } + } + + public class Data + { + public string NodeId { get; set; } + public int SequenceId { get; set; } + public string NodeDescription { get; set; } + public bool Released { get; set; } + public NodePosition NodePosition { get; set; } + public List Actions { get; set; } + } + + public class NodePosition + { + public double X { get; set; } + public double Y { get; set; } + public string MapId { get; set; } + } + + public class FassAction + { + public bool Action0 { get; set; } + public string ActionId { get; set; } + public List ActionParameters { get; set; } + public string ActionType { get; set; } + public string BlockingType { get; set; } + public int SortNumber { get; set; } + } + + public class FassActionParameter + { + public bool Parameter0 { get; set; } + public string Key { get; set; } + public string Value { get; set; } + } + + public class Edge + { + public int Index { get; set; } + public string Id { get; set; } + public string Kind { get; set; } + public string Type { get; set; } + public EdgeBase Base { get; set; } + public Code Code { get; set; } + public Name Name { get; set; } + public Lock Lock { get; set; } + public EdgeData Data { get; set; } + public List Extends { get; set; } + } + + public class EdgeBase + { + public bool Visible { get; set; } + public Point Point { get; set; } + public Size Size { get; set; } + public double GlobalAlpha { get; set; } + public int LineWidth { get; set; } + public List LineDash { get; set; } + public string StrokeStyle { get; set; } + public string FillStyle { get; set; } + public Center Center { get; set; } + public bool IsOneway { get; set; } + public double Width { get; set; } + public Node StartNode { get; set; } + public Node EndNode { get; set; } + } + + public class EdgeData + { + public string EdgeId { get; set; } + public int SequenceId { get; set; } + public string EdgeDescription { get; set; } + public bool Released { get; set; } + public string StartNodeId { get; set; } + public string EndNodeId { get; set; } + public double MaxSpeed { get; set; } + public double MaxHeight { get; set; } + public double MinHeight { get; set; } + public double Orientation { get; set; } + public string OrientationType { get; set; } + public string Direction { get; set; } + public bool RotationAllowed { get; set; } + public double MaxRotationSpeed { get; set; } + public double Length { get; set; } + public Trajectory Trajectory { get; set; } + public List Actions { get; set; } + } + + public class Trajectory + { + public double Degree { get; set; } + public List KnotVector { get; set; } + public List ControlPoints { get; set; } + } +} diff --git a/StandardScene.Core/Model/MissionState.cs b/StandardScene.Core/Model/MissionState.cs new file mode 100644 index 0000000..9e83269 --- /dev/null +++ b/StandardScene.Core/Model/MissionState.cs @@ -0,0 +1,48 @@ +锘 +using System; + +namespace StandardScene.Model +{ + public class MissionState + { + public string MissionId { get; set; } + public string CarCode {get; set; } + + public DateTime TriggerTime { get; set; } + + public enum MissionStateEnum + { + /// + /// 鍒涘缓浠诲姟銆 + /// + Created = 1, + + /// + /// 瀛愪换鍔″惎鍔ㄣ + /// + Started = 2, + + /// + /// 瀛愪换鍔″畬鎴愩 + /// + Finished = 3, + + /// + /// 瀛愪换鍔″け璐ャ + /// + Failed = 4, + + /// + /// 瀛愪换鍔$殑鍙栬揣瀹屾垚锛屽彂鐢熷湪Started涔嬪悗銆 + /// + Fetched = 5, + + /// + /// 瀛愪换鍔$殑鏀捐揣瀹屾垚锛屽彂鐢熷湪Started涔嬪悗銆 + /// + Put = 6 + } + + public MissionStateEnum State { get; set; } + } +} diff --git a/StandardScene.Core/Model/PlanRulesSetting.cs b/StandardScene.Core/Model/PlanRulesSetting.cs new file mode 100644 index 0000000..147ebf7 --- /dev/null +++ b/StandardScene.Core/Model/PlanRulesSetting.cs @@ -0,0 +1,17 @@ +锘縰sing System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace StandardScene.Model +{ + public class PlanRulesSetting + { + public string Name { get; set; } + public string Description { get; set; } + public string Value { get; set; } + + public string NodeId { get; set; } + } +} diff --git a/StandardScene.Core/Model/SimpleConfig.cs b/StandardScene.Core/Model/SimpleConfig.cs new file mode 100644 index 0000000..c10b49c --- /dev/null +++ b/StandardScene.Core/Model/SimpleConfig.cs @@ -0,0 +1,16 @@ +锘縰sing System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace StandardScene.Model +{ + public class SimpleConfig + { + public string Autoload { get; set; } + public int Port { get; set; } + public string Ip { get; set; } + public bool AllowMultiple { get; set; } + } +} diff --git a/StandardScene.Core/Model/SimpleMap.cs b/StandardScene.Core/Model/SimpleMap.cs new file mode 100644 index 0000000..10dfa97 --- /dev/null +++ b/StandardScene.Core/Model/SimpleMap.cs @@ -0,0 +1,124 @@ +锘縰sing System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace StandardScene.Model +{ + public class Configuration + { + public Conf conf { get; set; } + public Dictionary Missions { get; set; } + public Dictionary Maps { get; set; } + public Dictionary Sites { get; set; } + public Dictionary Tracks { get; set; } + public Dictionary Cars { get; set; } // Assuming Cars is an empty object + public Dictionary PrologScripts { get; set; } // Assuming PrologScripts is an empty object + + public Configuration() + { + // Initialize Conf with default values + conf = new Conf + { + PRICE_RANGE = 10000.0, + fields = new Dictionary(), + Car_UpdateInterval = 300, + Search_AllowDestOnRoute = false, + Search_RouteOnDestClearance = 0, + Search_MaxVisitPerSite = 1, + Search_MaxDupVisit = 1, + Search_MaxKeepResult = 16, + Traffic_MaxHoldingLocks = 3, + Traffic_DeadLockSearchDepth = 0, + DebugTypes = "S", + Auto_Reprogram = false + }; + + Missions = new Dictionary(); + Maps = new Dictionary(); + Sites = new Dictionary(); + Tracks = new Dictionary(); + Cars = new Dictionary(); + PrologScripts = new Dictionary(); + } + } + + public class Conf + { + public double PRICE_RANGE { get; set; } + public Dictionary fields { get; set; } // Assuming fields is an empty object + public int Car_UpdateInterval { get; set; } + public bool Search_AllowDestOnRoute { get; set; } + public int Search_RouteOnDestClearance { get; set; } + public int Search_MaxVisitPerSite { get; set; } + public int Search_MaxDupVisit { get; set; } + public int Search_MaxKeepResult { get; set; } + public int Traffic_MaxHoldingLocks { get; set; } + public int Traffic_DeadLockSearchDepth { get; set; } + public string DebugTypes { get; set; } + public bool Auto_Reprogram { get; set; } + } + + public class SimpleMission + { + public string type { get; set; } + public MissionOptions options { get; set; } + } + + public class MissionOptions + { + public bool autoStart { get; set; } + public int id { get; set; } + public string layerName { get; set; } + public string name { get; set; } + public Dictionary fields { get; set; } // Assuming fields is an empty object + } + + public class SimpleMap + { + public string type { get; set; } + public MapOptions options { get; set; } + } + + public class MapOptions + { + public string filename { get; set; } + public int id { get; set; } + public string layerName { get; set; } + public string name { get; set; } + public Dictionary fields { get; set; } // Assuming fields is an empty object + } + + public class SimpleSite + { + public string color { get; set; } + public string displaySetting { get; set; } + public double x { get; set; } + public double y { get; set; } + public int id { get; set; } + public string layerName { get; set; } + public string name { get; set; } + public Dictionary fields { get; set; } + public List mustFree { get; set; } // Assuming mustFree is an empty array + + + } + + public class Track + { + public string displaySetting { get; set; } + public int siteA { get; set; } + public int siteB { get; set; } + public int direction { get; set; } + public int id { get; set; } + public string layerName { get; set; } + public string name { get; set; } + public Dictionary fields { get; set; } + public int _siteA { get; set; } + public int _siteB { get; set; } + + public string typeInfo { get; set; } + // public Dictionary fields { get; set; } + } +} diff --git a/StandardScene.Core/Model/TaskModel.cs b/StandardScene.Core/Model/TaskModel.cs new file mode 100644 index 0000000..a90eeed --- /dev/null +++ b/StandardScene.Core/Model/TaskModel.cs @@ -0,0 +1,262 @@ +using SimpleLite.RCS; +using SimpleLite.RCS.CarTypes; +using System; +using System.Collections.Generic; +using System.Linq; + +namespace StandardScene.Model +{ + public class TaskRequest + { + public string CarCode { get; set; } + public string CarType { get; set; } + public string TaskCode { get; set; } + public string MissionType { get; set; } + public string TaskType { get; set; } + public string Material { get; set; } + public ContainerSize ContainerSize { get; set; } + public List Nodes { get; set; } + public int priority { get; set; } + } + + public class ContainerSize + { + public double Length { get; set; } + public double Width { get; set; } + public double Height { get; set; } + } + + public class Node + { + public int Code { get; set; } + //public List Actions { get; set; } + } + + public class Action + { + public string Code { get; set; } + public string ActionType { get; set; } + public string BlockingType { get; set; } + public List Parameters { get; set; } + } + + public class ActionParameter + { + public string Key { get; set; } + public string Value { get; set; } + } + + public class BaseRespose + { + public bool Success { get; set; } + public int Code { get; set; } = 200; + public string Message { get; set; } + public T Data { get; set; } + } + + public class BaseRespose + { + public bool Success { get; set; } = true; + public int Code { get; set; } = 200; + public string Message { get; set; } + } + public class CarInfo + { + + + public string Code { get; set; } + public string Name { get; set; } + public double Voltage { get; set; } + + public string Address { get; set; } + + public string Speed { get; set; } + + public int siteId { get; set; } + + } + public class CarStateInfo + { + public string Code { get; set; } + public string Name { get; set; } + public string CurrState { get; set; } + public bool IsOnline { get; set; } = false; + public double Battery { get; set; } + public double ElectricCurrent { get; set; } + public double Voltage { get; set; } + public double X { get; set; } + public double Y { get; set; } + public double Theta { get; set; } + public double Speed { get; set; } + public int Load { get; set; } + public string CurrNodeCode { get; set; } + public string StartNodeCode { get; set; } + public string EndNodeCode { get; set; } + public string CurrEdgeCode { get; set; } = ""; + public string StartEdgeCode { get; set; } = ""; + public string EndEdgeCode { get; set; } = ""; + public List HoldingLocks { get; set; } + public List PendingLocks { get; set; } + public List BlockedBy { get; set; } + public string BlockingTime { get; set; } = ""; + public string TrafficMessage { get; set; } = ""; + public int AquiringLock { get; set; } = -1; + public bool StopAccept { get; set; } = false; + + public List Alarms { get; set; } + + public List Tasks { get; set; } + + public List Actions { get; set; } + + } + + public class BlockedByItem + { + public string CarCode { get; set; } + public int Type { get; set; } + } + + public class CarAlarm + { + public string Code { get; set; } + public string Name { get; set; } + } + + public class TaskInfo + { + public string Code { get; set; } + public string State { get; set; } + public List Nodes { get; set; } + } + + public class TaskAction + { + public string Code { get; set; } + public string ActionType { get; set; } + public string BlockingType { get; set; } + public string State { get; set; } + } + + public class ResponseNode + { + public string Code { get; set; } + //public List Actions { get; set; } + } + + public class TaskCancel + { + public string TaskCode { get; set; } + public string MissionType { get; set; } + public string TaskStatus { get; set; } + } + public class TaskRecord + { + public string TaskId { get; set; } + public string Name { get; set; } + public string CarId { get; set; } + public string CarName { get; set; } + public string SrcSiteId { get; set; } + public string DestSiteId { get; set; } + public int Priority { get; set; } = 0; + public string State { get; set; } + public DateTime? StartTime { get; set; } + public DateTime? EndTime { get; set; } + public DateTime Created { get; set; } + + } + + public class CarBaseInfo + { + public string Code { get; set; } + public string Name { get; set; } + public string CarType { get; set; } + public double Length { get; set; } + public double Width { get; set; } + public string CurrNodeCode { get; set; } + public string CurrState { get; set; } + public double Battery { get; set; } + public string Address { get; set; } + public double Speed { get; set; } + public double X { get; set; } + public double Y { get; set; } + public double Theta { get; set; } + public double Voltage { get; set; } + public double ElectricCurrent { get; set; } + public bool IsOnline { get; set; } = false; + public Dictionary Additional { get; set; } + + public CarBaseInfo() + { + Additional = new Dictionary(); + } + + public static CarBaseInfo FromCar(Car car) + { + var info = new CarBaseInfo() + { + Code = car.id.ToString(), + Name = car.name, + Address = car.address.Equals("/") ? "127.0.0.1" : car.address, + CurrNodeCode = car.status.holdingLocks.FirstOrDefault().ToString(), + X = car.x, + Y = car.y, + Theta = car.th, + Speed = car.speed + }; + //info.Length = car.latestShape.lx; + //info.Width = car.latestShape.ly; + info.CarType = car.fields.ContainsKey("CarType") ? car.fields["CarType"] : "Car"; + info.Battery = car.status.enums.ContainsKey("Soc") ? int.Parse(car.status.enums["Soc"]) : 100; + info.Voltage = car.status.enums.ContainsKey("Voltage") ? double.Parse(car.status.enums["Voltage"]) : 0; + info.ElectricCurrent = car.status.enums.ContainsKey("ElectricCurrent") ? double.Parse(car.status.enums["ElectricCurrent"]) : 0; + var isOnline = false; + string carState = SwitchCarState(car, ref isOnline); + info.IsOnline = isOnline; + info.CurrState = carState; + return info; + } + + public static string SwitchCarState(Car car, ref bool isOnline) + { + string state = "Stopping"; + if (car.GetLastSite() == -1) + { + isOnline = false; + return state; + } + if (Commons.GetVehicleStatus(car)!= VehicleStatus.Offline) + { + if (Commons.GetVehicleStatus(car) is VehicleStatus.Normal or VehicleStatus.NeedInit) + state = "Stopping"; //Idle + var lp = car.status.programs.latest; + if (lp != null) + { + if ( + lp.status.state >= SimpleCore.Compiler.CarProgram.StatusEnum.Programming + && lp.status.state <= SimpleCore.Compiler.CarProgram.StatusEnum.Running + ) + { + state = "Running"; //Executing + } + } + //瀛樺湪鎶ヨ淇℃伅 + if (car.status.enums.ContainsKey("AlarmInfo")) + { + if (!string.IsNullOrEmpty(car.status.enums["AlarmInfo"])) + { + state = "Faulting"; //Malfunction + } + } + if (car.tags.Contains("charging")) + state = "Charging"; + isOnline = true; + } + else + { + isOnline = false; + } + return state; + } + } +} diff --git a/StandardScene.Core/Model/TrafficControlSetting.cs b/StandardScene.Core/Model/TrafficControlSetting.cs new file mode 100644 index 0000000..b9ac8fc --- /dev/null +++ b/StandardScene.Core/Model/TrafficControlSetting.cs @@ -0,0 +1,15 @@ +锘縰sing System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace StandardScene.Model +{ + public class TrafficControlSetting + { + public string Name { get; set; } + public string Description { get; set; } + public string Value { get; set; } + } +} diff --git a/StandardScene.Core/Model/VehicleStatus.cs b/StandardScene.Core/Model/VehicleStatus.cs new file mode 100644 index 0000000..16a557a --- /dev/null +++ b/StandardScene.Core/Model/VehicleStatus.cs @@ -0,0 +1,16 @@ +锘縰sing System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace StandardScene.Model +{ + public enum VehicleStatus + { + Unknown = 0, + Normal = 1, + NeedInit = 2, + Offline = 3, + } +} diff --git a/StandardScene.Core/Properties/AssemblyInfo.cs b/StandardScene.Core/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..8a28f3d --- /dev/null +++ b/StandardScene.Core/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +锘縰sing System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("StandardScene")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("StandardScene")] +[assembly: AssemblyCopyright("Copyright 漏 2024")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a Type in this assembly from +// COM, set the ComVisible attribute to true on that Type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("7a745509-1593-4044-ba49-9b6b0a35b505")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/StandardScene.Core/Properties/InternalsVisibleTo.cs b/StandardScene.Core/Properties/InternalsVisibleTo.cs new file mode 100644 index 0000000..09b0f67 --- /dev/null +++ b/StandardScene.Core/Properties/InternalsVisibleTo.cs @@ -0,0 +1,8 @@ +using System.Runtime.CompilerServices; + +// 绋嬪簭闆嗘媶鍒嗭紙璺嚎涔欙級锛氳鍚勫崼鏄 dll 鑳借闂 Core 鐨 internal 绫诲瀷锛堝瓧娈佃/宸ュ叿绛夛級锛 +// 浠庤屼互"绾惉杩"鏂瑰紡澶栫Щ浠g爜锛岄伩鍏嶅ぇ閲 internal鈫抪ublic 鐨勪镜鍏ュ紡鏀瑰姩銆 +[assembly: InternalsVisibleTo("StandardScene.Protocol.VDA5050")] +[assembly: InternalsVisibleTo("StandardScene.Devices")] +[assembly: InternalsVisibleTo("StandardScene.Magnetic")] +[assembly: InternalsVisibleTo("StandardScene.QrLidar")] diff --git a/StandardScene.Core/Ref/leegKeys-sdk.dll b/StandardScene.Core/Ref/leegKeys-sdk.dll new file mode 100644 index 0000000..f05fc0b Binary files /dev/null and b/StandardScene.Core/Ref/leegKeys-sdk.dll differ diff --git a/StandardScene.Core/Scheduler/HeartBeatMission.cs b/StandardScene.Core/Scheduler/HeartBeatMission.cs new file mode 100644 index 0000000..2f600d5 --- /dev/null +++ b/StandardScene.Core/Scheduler/HeartBeatMission.cs @@ -0,0 +1,73 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net.Http; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using LessokajiWeaverUtilities.Utilities; +using Newtonsoft.Json; +using SimpleLite.RCS; +using SimpleLite.RCS.CarTypes; +using SimpleCore; +using StandardScene.Model; + +namespace StandardScene.Scheduler +{ + [MissionType(Name = "璋冨害蹇冭烦杩涚▼",editor = typeof(HeartBeatMission))] + [I18N.DocumentTranslation(Name = "Heart Beat Mission", locale = "en")] + public class HeartBeatMission:Mission + { + [JsonIgnore] private bool _started = false; + private Thread _myThread; + private HttpClient _httpClient; + + public static Mission Create() + { + return new HeartBeatMission(); + } + [MethodMember(Name = "鍚姩杩涚▼", Description = "寮濮嬩笅鍙戣皟搴﹀績璺")] + public override void Execute() + { + if(_started) return; + status.status = "宸插惎鍔"; + int count = 0; + _httpClient = new HttpClient(); + _myThread = new Thread(() => + { + _started = true; + while (_started) + { + Thread.Sleep(1000); + count++; + status.status = $"宸插惎鍔:{count}"; + var carList = SimpleLib.GetAllCars(); + foreach (var car in carList) + { + if (car is not GhostCar gCar || Commons.GetVehicleStatus(gCar)!= VehicleStatus.Normal) continue; + try + { + //((ClumsyCar)car).ImmediateCommand("pilot.SimpleSignal=1"); + var ip = gCar.address; + _httpClient.GetStringAsync($"http://{ip}:8008/setValue?FieldName=SimpleSignal&Value=1"); + } + catch (Exception e) + { + Console.WriteLine($"http error" + e.Message + e.StackTrace); + } + } + } + }); + _myThread.Start(); + } + + [MethodMember(Name = "鍏抽棴杩涚▼", Description = "鍋滄涓嬪彂蹇冭烦")] + public void Stop() + { + _started = false; + _myThread?.Join(2000); + _myThread = null; + status.status = "宸插叧闂"; + } + } +} diff --git a/StandardScene.Core/Scheduler/NodeIsEnableMission.cs b/StandardScene.Core/Scheduler/NodeIsEnableMission.cs new file mode 100644 index 0000000..5ffe335 --- /dev/null +++ b/StandardScene.Core/Scheduler/NodeIsEnableMission.cs @@ -0,0 +1,144 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using SimpleLite.RCS; +using SimpleLite.RCS.CarTypes; +using SimpleCore; +using Newtonsoft.Json; +using SimpleLite.CADTools; +using SimpleLite.Props; +using SimpleLite.UI; +using SimpleCore.Library; +using System.Threading.Tasks; +using System.Diagnostics; +using SimpleCore.Traffic; +using SimpleCore.PropType; +using System.Net.Http; +using System.Reflection; +using LessokajiWeaverUtilities.Utilities; +using SimpleLite.RCS.Signal; + + +namespace StandardScene.Scheduler +{ + [MissionType(Name = "閿佺偣涓婁紶杩锋瘋", editor = typeof(NodeIsEnableMission))] + [I18N.DocumentTranslation(Name = "NodeState Upload Mission", locale = "en")] + public class NodeIsEnableMission : Mission + { + public static Mission Create() + { + return new NodeIsEnableMission(); + } + + public int[] stringToInt(string str) + { + return str.Split(' ').Select(s => (int)Convert.ToInt16(s, 10)).ToArray(); + } + + //鏄惁杩炴帴鎴愬姛 + public bool iscon { get; set; } + + + public class NOdeIsEnableMissionStatus : MissionStatus + { + public static List staions = new List(); + + public float readOnceTime = 0; + public float readAllDataTime = 0; + public float writeOnceTime = 0; + public float writeAllDataTime = 0; + + + } + + public override MissionStatus status { get; set; } = new NOdeIsEnableMissionStatus(); + + + [JsonIgnore] public Thread myThread; + + [JsonIgnore] private bool started = false; + + [JsonIgnore] private int iterationComm = 0; + + + [JsonIgnore] private object sync = new(); + [JsonIgnore] public static HttpClient hc = new HttpClient() { Timeout = TimeSpan.FromSeconds(2) }; + + [MethodMember(Name = "鍚姩杩涚▼", Description = "寮濮嬪鐞嗕笂涓嬫枡闃熷垪")] + public override void Execute() + { + started = true; + Task.Factory.StartNew(() => { + HttpPostData httpPostData = new HttpPostData(); + Dictionary> Allsignal = new Dictionary>(); + //Dictionary stationSignals = new Dictionary(); + while (started) + { + try + { + //鑾峰彇鎵鏈夐攣鐐圭殑杞 + var sites = SimpleLib + .GetAllSites() + .Where(site => site.tags.Contains("unavailable")) + .ToList(); + HashSet staions = new HashSet(); + foreach (var site in sites) + { + staions.Add(site.id); + if (site.fields.ContainsKey("mustFree")&& site.mustFree !=null && site.mustFree.Length>0) + { + //閬嶅巻鏁扮粍娣诲姞绂佺敤鐐 + for (int i = 0; i < site.mustFree.Length; i++) + { + staions.Add(site.mustFree[i]) ; + + } + + } + } + Diagnosis.Log($"鍚戣糠姣傛彁渚涚鐢ㄧ珯鐐", "siteIsEnable", true); + httpPostData.UploadListNode(staions); + + + Thread.Sleep(500); + } + catch (Exception e) + { + + Diagnosis.Post($"{ExceptionFormatter.FormatEx(e)}", "绂佺敤绔欑偣"); + } + + } + + + }); + } + + [MethodMember(Name = "鍏抽棴鏈烘瀯杩涚▼", Description = "鍏抽棴鏈烘瀯杩涚▼")] + public void Stop() + { + started = false; + status.status = $"宸插仠姝-寰幆"; + } + + + public static string GetListsAsStrings(object obj) + { + Dictionary result = new Dictionary(); + Type type = obj.GetType(); + FieldInfo[] fieldInfo= type.GetFields(); + foreach (FieldInfo property in fieldInfo) + { + if (property.FieldType== typeof(List)) + { + List list = (List)property.GetValue(obj); + result[property.Name] = String.Join(",", list.Select(b => b ? "1" : "0")); + + } + } + return JsonConvert.SerializeObject(result); + } + + } +} diff --git a/StandardScene.Core/Scheduler/RegionalTrafficControlMission.cs b/StandardScene.Core/Scheduler/RegionalTrafficControlMission.cs new file mode 100644 index 0000000..e6b5277 --- /dev/null +++ b/StandardScene.Core/Scheduler/RegionalTrafficControlMission.cs @@ -0,0 +1,432 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using SimpleLite.RCS; +using SimpleLite.RCS.CarTypes; +using SimpleCore; +using Newtonsoft.Json; +using SimpleLite.CADTools; +using SimpleLite.Props; +using SimpleLite.UI; +using SimpleCore.Library; +using System.Threading.Tasks; +using System.Diagnostics; +using SimpleCore.Traffic; +using SimpleCore.PropType; +using LessokajiWeaverUtilities.Utilities; + +namespace StandardScene.Scheduler +{ + [MissionType(Name = "鍖哄煙娴侀噺鐩戞帶", editor = typeof(RegionalTrafficControlMission))] + [I18N.DocumentTranslation(Name = "Regional Traffic Control Mission", locale = "en")] + public class RegionalTrafficControlMission : Mission + { + public static Mission Create() + { + return new RegionalTrafficControlMission(); + } + + /// + /// 鍖哄煙娴侀噺鐩戞帶鐘舵 + /// + public class RegionalTrafficControlMissionStatus : MissionStatus + { + /// + /// 鍖哄煙杞﹁締缁熻淇℃伅锛氬尯鍩熺紪鍙 -> 褰撳墠杞﹁締鏁伴噺 + /// + public Dictionary RegionVehicleCounts { get; set; } = new Dictionary(); + + /// + /// 鍖哄煙闄愬埗淇℃伅锛氬尯鍩熺紪鍙 -> 鏈澶у厑璁歌溅杈嗘暟 + /// + public Dictionary RegionMaxCounts { get; set; } = new Dictionary(); + + /// + /// 琚樆姝㈢殑杞﹁締缁熻锛氬尯鍩熺紪鍙 -> 琚樆姝㈡鏁 + /// + public Dictionary BlockedCounts { get; set; } = new Dictionary(); + } + + public override MissionStatus status { get; set; } = new RegionalTrafficControlMissionStatus(); + + [JsonIgnore] private bool started = false; + + [JsonIgnore] private object sync = new object(); + + /// + /// 鍖哄煙鍐呯殑杞﹁締璁板綍锛氬尯鍩熺紪鍙 -> 杞﹁締ID鍒楄〃 + /// + [JsonIgnore] private readonly Dictionary> carsInRegions = new Dictionary>(); + + /// + /// 鍚姩杩涚▼ + /// + [MethodMember(Name = "鍚姩杩涚▼", Description = "寮濮嬪尯鍩熸祦閲忕洃鎺")] + public override void Execute() + { + if (started) + { + Diagnosis.Post("鍖哄煙娴侀噺鐩戞帶宸插湪杩愯涓", "鍖哄煙娴侀噺鐩戞帶"); + return; + } + + started = true; + status.status = "杩愯涓"; + + // 璁㈤槄浜ら氭帶鍒朵簨浠 + TrafficControl.BeforeLock += BeforeLockEvent; + TrafficControl.AfterLeave += AfterLeaveEvent; + TrafficControl.OnLockAcquired += OnLockAcquiredEvent; + + // 鍚姩缁熻鏇存柊浠诲姟 + Task.Factory.StartNew(() => + { + while (started) + { + try + { + UpdateRegionStatistics(); + Thread.Sleep(500); + } + catch (Exception e) + { + Diagnosis.Post($"{ExceptionFormatter.FormatEx(e)}", "鍖哄煙娴侀噺鐩戞帶"); + } + } + }); + + Diagnosis.Post("鍖哄煙娴侀噺鐩戞帶宸插惎鍔", "鍖哄煙娴侀噺鐩戞帶", true); + } + + /// + /// 鍋滄杩涚▼ + /// + [MethodMember(Name = "鍋滄杩涚▼", Description = "鍋滄鍖哄煙娴侀噺鐩戞帶")] + public void Stop() + { + if (!started) + { + return; + } + + started = false; + + // 鍙栨秷浜嬩欢璁㈤槄 + TrafficControl.BeforeLock -= BeforeLockEvent; + TrafficControl.AfterLeave -= AfterLeaveEvent; + TrafficControl.OnLockAcquired -= OnLockAcquiredEvent; + + status.status = "宸插仠姝"; + Diagnosis.Post("鍖哄煙娴侀噺鐩戞帶宸插仠姝", "鍖哄煙娴侀噺鐩戞帶", true); + } + + /// + /// 鑾峰彇绔欑偣鐨勫尯鍩熶俊鎭 + /// + /// 绔欑偣 + /// 鍖哄煙鏍囪瘑鍜岄檺鍒舵暟閲忥紝濡傛灉绔欑偣涓嶅睘浜庝换浣曞尯鍩熷垯杩斿洖null + private (string regionId, int maxCount)? GetRegionInfo(Site site) + { + if (site == null || site.fields == null) + { + return null; + } + + // 鏌ユ壘鎵鏈変互Region寮澶寸殑瀛楁 + var regionField = site.fields.FirstOrDefault(kvp => kvp.Key.StartsWith("Region", StringComparison.OrdinalIgnoreCase)); + + if (regionField.Key == null) + { + return null; // 绔欑偣涓嶅睘浜庝换浣曞尯鍩 + } + + string regionId = regionField.Key; // 渚嬪 "Region1" + + // 瀛楁鍊煎氨鏄檺鍒舵暟閲 + if (!int.TryParse(regionField.Value, out int maxCount)) + { + return null; // 鏃犳硶瑙f瀽闄愬埗鏁伴噺 + } + + return (regionId, maxCount); + } + + /// + /// 妫鏌ヨ溅杈嗘槸鍚﹀凡缁忓湪鎸囧畾鍖哄煙鍐 + /// + private bool IsCarAlreadyInRegion(AbstractCar car, string regionId) + { + if (car?.status == null) + { + return false; + } + + // 妫鏌ヨ溅杈嗗綋鍓嶅崰鐢ㄧ殑绔欑偣鏄惁灞炰簬璇ュ尯鍩 + if (car.status.holdingLocks != null) + { + foreach (var lockedSiteId in car.status.holdingLocks) + { + var lockedSite = SimpleLib.GetSite(lockedSiteId); + var regionInfo = GetRegionInfo(lockedSite); + if (regionInfo.HasValue && regionInfo.Value.regionId == regionId) + { + return true; // 杞﹁締宸茬粡鍦ㄥ尯鍩熷唴 + } + } + } + + return false; + } + + /// + /// BeforeLock浜嬩欢澶勭悊锛氭鏌ヨ溅杈嗘槸鍚﹀彲浠ヨ繘鍏ュ尯鍩 + /// + private bool BeforeLockEvent(AbstractCar car, int siteId) + { + try + { + var site = SimpleLib.GetSite(siteId); + if (site == null) + { + return true; + } + + // 鑾峰彇绔欑偣鐨勫尯鍩熶俊鎭 + var regionInfo = GetRegionInfo(site); + if (!regionInfo.HasValue) + { + return true; // 绔欑偣涓嶅睘浜庝换浣曞尯鍩燂紝鍏佽閫氳繃 + } + + var (regionId, maxCount) = regionInfo.Value; + + // 濡傛灉鏈澶ф暟閲忎负0鎴栬礋鏁帮紝琛ㄧず涓嶉檺鍒 + if (maxCount <= 0) + { + return true; + } + + // 妫鏌ヨ溅杈嗘槸鍚﹀凡缁忓湪鍖哄煙鍐 + lock (sync) + { + if (IsCarAlreadyInRegion(car, regionId)) + { + // 杞﹁締宸茬粡鍦ㄥ尯鍩熷唴锛屽厑璁搁氳 + return true; + } + + // 杞﹁締鍗冲皢杩涘叆鍖哄煙锛岄渶瑕佹鏌ユ槸鍚﹁秴杩囬檺鍒 + // 鎺掗櫎褰撳墠杞﹁締鏈韩锛屽洜涓哄畠杩樻病鏈夎繘鍏ュ尯鍩 + int currentCount = CountCarsInRegion(regionId, excludeCarId: car.id); + + // 濡傛灉褰撳墠杞﹁締鏁伴噺宸茶揪鍒版垨瓒呰繃闄愬埗锛岄樆姝㈣繘鍏 + if (currentCount >= maxCount) + { + var statusObj = (RegionalTrafficControlMissionStatus)status; + if (!statusObj.BlockedCounts.ContainsKey(regionId)) + { + statusObj.BlockedCounts[regionId] = 0; + } + statusObj.BlockedCounts[regionId]++; + + Diagnosis.Log($"杞﹁締 {car.id} 灏濊瘯杩涘叆鍖哄煙 {regionId} 琚樆姝紝褰撳墠杞﹁締鏁: {currentCount}/{maxCount}", "鍖哄煙娴侀噺鐩戞帶", true); + return false; // 闃绘杩涘叆 + } + + return true; // 鍏佽杩涘叆 + } + } + catch (Exception e) + { + Diagnosis.Post($"BeforeLockEvent閿欒: {ExceptionFormatter.FormatEx(e)}", "鍖哄煙娴侀噺鐩戞帶"); + return true; // 鍙戠敓閿欒鏃跺厑璁搁氳繃锛岄伩鍏嶉樆濉炵郴缁 + } + } + + /// + /// AfterLeave浜嬩欢澶勭悊锛氳溅杈嗙寮鍖哄煙鏃舵洿鏂扮粺璁 + /// + private void AfterLeaveEvent(AbstractCar car, int siteId) + { + try + { + var site = SimpleLib.GetSite(siteId); + if (site == null) + { + return; + } + + // 鑾峰彇绔欑偣鐨勫尯鍩熶俊鎭 + var regionInfo = GetRegionInfo(site); + if (!regionInfo.HasValue) + { + return; // 绔欑偣涓嶅睘浜庝换浣曞尯鍩 + } + + string regionId = regionInfo.Value.regionId; + + // 浠庡尯鍩熻溅杈嗗垪琛ㄤ腑绉婚櫎璇ヨ溅杈 + lock (sync) + { + if (carsInRegions.TryGetValue(regionId, out var carSet)) + { + carSet.Remove(car.id); + if (carSet.Count == 0) + { + carsInRegions.Remove(regionId); + } + } + } + } + catch (Exception e) + { + Diagnosis.Post($"AfterLeaveEvent閿欒: {ExceptionFormatter.FormatEx(e)}", "鍖哄煙娴侀噺鐩戞帶"); + } + } + + /// + /// OnLockAcquired浜嬩欢澶勭悊锛氳溅杈嗘垚鍔熼攣瀹氱珯鐐规椂锛屽皢鍏跺姞鍏ュ尯鍩熺粺璁 + /// + private void OnLockAcquiredEvent(AbstractCar car, int siteId) + { + try + { + var site = SimpleLib.GetSite(siteId); + if (site == null) + { + return; + } + + // 鑾峰彇绔欑偣鐨勫尯鍩熶俊鎭 + var regionInfo = GetRegionInfo(site); + if (!regionInfo.HasValue) + { + return; // 绔欑偣涓嶅睘浜庝换浣曞尯鍩 + } + + string regionId = regionInfo.Value.regionId; + + // 灏嗚溅杈嗘坊鍔犲埌鍖哄煙杞﹁締鍒楄〃涓 + lock (sync) + { + if (!carsInRegions.TryGetValue(regionId, out var carSet)) + { + carSet = new HashSet(); + carsInRegions[regionId] = carSet; + } + carSet.Add(car.id); + } + } + catch (Exception e) + { + Diagnosis.Post($"OnLockAcquiredEvent閿欒: {ExceptionFormatter.FormatEx(e)}", "鍖哄煙娴侀噺鐩戞帶"); + } + } + + /// + /// 缁熻鎸囧畾鍖哄煙鍐呯殑杞﹁締鏁伴噺 + /// + /// 鍖哄煙鏍囪瘑 + /// 瑕佹帓闄ょ殑杞﹁締ID锛堝彲閫夛紝鐢ㄤ簬鎺掗櫎鍗冲皢杩涘叆鐨勮溅杈嗭級 + /// 鍖哄煙鍐呯殑杞﹁締鏁伴噺 + private int CountCarsInRegion(string regionId, int excludeCarId = -1) + { + lock (sync) + { + // 棣栧厛浠庡凡璁板綍鐨勮溅杈嗗垪琛ㄤ腑缁熻 + int count = 0; + if (carsInRegions.TryGetValue(regionId, out var recordedCars)) + { + count = recordedCars.Count(carId => carId != excludeCarId); + } + + // 鍚屾椂妫鏌ユ墍鏈夌珯鐐癸紝缁熻灞炰簬璇ュ尯鍩熶笖褰撳墠鏈夎溅杈嗗崰鐢ㄧ殑绔欑偣 + var regionSites = SimpleLib.GetAllSites() + .Where(s => + { + var regionInfo = GetRegionInfo(s); + return regionInfo.HasValue && regionInfo.Value.regionId == regionId; + }) + .ToList(); + + // 缁熻杩欎簺绔欑偣涓婄殑杞﹁締锛堝彧缁熻宸查攣瀹氱殑锛屼笉鍖呮嫭姝e湪鍓嶅線鐨勶級 + // 鍥犱负姝e湪鍓嶅線鐨勮溅杈嗚繕娌℃湁鐪熸杩涘叆鍖哄煙 + var carsInRegionSites = SimpleLib.GetAllCars() + .OfType() + .Where(car => + { + if (car?.status == null) + return false; + + // 鎺掗櫎鎸囧畾杞﹁締 + if (car.id == excludeCarId) + return false; + + // 鍙粺璁″凡缁忓湪鍖哄煙鍐呯殑绔欑偣锛坔oldingLocks锛 + // 涓嶇粺璁endingLocks锛屽洜涓洪偅浜涜溅杈嗚繕娌℃湁鐪熸杩涘叆鍖哄煙 + bool inRegionSite = car.status.holdingLocks?.Any(siteId => + { + var site = SimpleLib.GetSite(siteId); + var regionInfo = GetRegionInfo(site); + return regionInfo.HasValue && regionInfo.Value.regionId == regionId; + }) == true; + + return inRegionSite; + }) + .Select(car => car.id) + .ToHashSet(); + + // 杩斿洖涓よ呬腑鐨勮緝澶у硷紝纭繚缁熻鍑嗙‘ + return Math.Max(count, carsInRegionSites.Count); + } + } + + /// + /// 鏇存柊鍖哄煙缁熻淇℃伅 + /// + private void UpdateRegionStatistics() + { + try + { + lock (sync) + { + var statusObj = (RegionalTrafficControlMissionStatus)status; + statusObj.RegionVehicleCounts.Clear(); + statusObj.RegionMaxCounts.Clear(); + + // 鑾峰彇鎵鏈夋湁鍖哄煙瀛楁鐨勭珯鐐癸紝鎸夊尯鍩熷垎缁 + var regionSites = SimpleLib.GetAllSites() + .Select(s => + { + var regionInfo = GetRegionInfo(s); + return regionInfo.HasValue ? (site: s, regionId: regionInfo.Value.regionId, maxCount: regionInfo.Value.maxCount) : (site: (Site)null, regionId: (string)null, maxCount: 0); + }) + .Where(x => x.site != null && !string.IsNullOrEmpty(x.regionId)) + .GroupBy(x => x.regionId) + .ToList(); + + foreach (var group in regionSites) + { + string regionId = group.Key; + var firstItem = group.First(); + + // 鑾峰彇鏈澶у厑璁告暟閲忥紙浠庣涓涓珯鐐圭殑瀛楁鍊艰幏鍙栵級 + int maxCount = firstItem.maxCount; + + // 缁熻褰撳墠杞﹁締鏁伴噺 + int currentCount = CountCarsInRegion(regionId); + + statusObj.RegionMaxCounts[regionId] = maxCount; + statusObj.RegionVehicleCounts[regionId] = currentCount; + } + } + } + catch (Exception e) + { + Diagnosis.Post($"UpdateRegionStatistics閿欒: {ExceptionFormatter.FormatEx(e)}", "鍖哄煙娴侀噺鐩戞帶"); + } + } + } +} + diff --git a/StandardScene.Core/Scheduler/SecuritySignalMission.cs b/StandardScene.Core/Scheduler/SecuritySignalMission.cs new file mode 100644 index 0000000..55dd6b8 --- /dev/null +++ b/StandardScene.Core/Scheduler/SecuritySignalMission.cs @@ -0,0 +1,201 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using SimpleLite.RCS; +using SimpleLite.RCS.CarTypes; +using SimpleCore; +using Newtonsoft.Json; +using SimpleLite.CADTools; +using SimpleLite.Props; +using SimpleLite.UI; +using SimpleCore.Library; +using System.Threading.Tasks; +using System.Diagnostics; +using IoTClient.Clients.PLC; +using IoTClient.Enums; +using SimpleCore.Traffic; +using SimpleCore.PropType; +using System.Net.Http; +using System.Reflection; +using LessokajiWeaverUtilities.Utilities; + + +namespace StandardScene.Scheduler +{ + [MissionType(Name = "瀹夊叏淇″彿浜や簰", editor = typeof(SecuritySignalMission))] + [I18N.DocumentTranslation(Name = "Security Signal Mission", locale = "en")] + public class SecuritySignalMission : Mission + { + public static Mission Create() + { + return new SecuritySignalMission(); + } + + public int[] stringToInt(string str) + { + return str.Split(' ').Select(s => (int)Convert.ToInt16(s, 10)).ToArray(); + } + + public class NOdeIsEnableMissionStatus : MissionStatus + { + public static List staions = new List() { "One","Two","Three","Four","Five"}; + public static List signalDB = new List() { "beat", "request", "allowRequest", "entering", "inplace","requestLeave" ,"allowLeave","leaving","safeLeave","AGVAlarm","PlcAlarm"}; + + public List One = Enumerable.Repeat(false, signalDB.Count()).ToList(); + + public Dictionary> signal = new Dictionary>(); + + + public List Two = Enumerable.Repeat(false, signalDB.Count()).ToList(); + + + public List Three = Enumerable.Repeat(false, signalDB.Count()).ToList(); + public List Four = Enumerable.Repeat(false, signalDB.Count()).ToList(); + + + public List Five = Enumerable.Repeat(false, signalDB.Count()).ToList(); + public List Six = Enumerable.Repeat(false, signalDB.Count()).ToList(); + public List Seven = Enumerable.Repeat(false, signalDB.Count()).ToList(); + public List Eight = Enumerable.Repeat(false, signalDB.Count()).ToList(); + public float readOnceTime = 0; + public float readAllDataTime = 0; + public float writeOnceTime = 0; + public float writeAllDataTime = 0; + + + } + + public override MissionStatus status { get; set; } = new NOdeIsEnableMissionStatus(); + + + [JsonIgnore] public Thread myThread; + + [JsonIgnore] private bool started = false; + + [JsonIgnore] private int iterationComm = 0; + + + [JsonIgnore] private object sync = new(); + [JsonIgnore] public static HttpClient hc = new HttpClient() { Timeout = TimeSpan.FromSeconds(2) }; + + [MethodMember(Name = "鍚姩杩涚▼", Description = "寮濮嬪鐞嗕笂涓嬫枡闃熷垪")] + public override void Execute() + { + started = true; + Task.Factory.StartNew(() => { + HttpPostData httpPostData = new HttpPostData(); + Dictionary> Allsignal = new Dictionary>(); + //Dictionary stationSignals = new Dictionary(); + while (started) + { + try + { + + foreach (var station in NOdeIsEnableMissionStatus.staions) + { + Dictionary stationSignals = new Dictionary(); + foreach (var signalName in NOdeIsEnableMissionStatus.signalDB) + { + // 鏍规嵁station鍜宻ignalName鑾峰彇瀵瑰簲鐨刡ool鍊 + bool signalValue; + switch (station) + { + case "One": + signalValue = ((NOdeIsEnableMissionStatus)status).One[(NOdeIsEnableMissionStatus.signalDB).IndexOf(signalName)]; + break; + case "Two": + signalValue = ((NOdeIsEnableMissionStatus)status).Two[(NOdeIsEnableMissionStatus.signalDB).IndexOf(signalName)]; + break; + case "Three": + signalValue = ((NOdeIsEnableMissionStatus)status).Three[(NOdeIsEnableMissionStatus.signalDB).IndexOf(signalName)]; + break; + case "Four": + signalValue = ((NOdeIsEnableMissionStatus)status).Four[(NOdeIsEnableMissionStatus.signalDB).IndexOf(signalName)]; + break; + case "Five": + signalValue = ((NOdeIsEnableMissionStatus)status).Five[(NOdeIsEnableMissionStatus.signalDB).IndexOf(signalName)]; + break; + case "Six": + signalValue = ((NOdeIsEnableMissionStatus)status).Six[(NOdeIsEnableMissionStatus.signalDB).IndexOf(signalName)]; + break; + case "Seven": + signalValue = ((NOdeIsEnableMissionStatus)status).Seven[(NOdeIsEnableMissionStatus.signalDB).IndexOf(signalName)]; + break; + case "Eight": + signalValue = ((NOdeIsEnableMissionStatus)status).Eight[(NOdeIsEnableMissionStatus.signalDB).IndexOf(signalName)]; + break; + default: + throw new Exception("Invalid station name"); + } + + // 妫鏌ュ瓧鍏镐腑鏄惁瀛樺湪鎸囧畾鐨勯敭锛屽鏋滃瓨鍦ㄥ垯淇敼鍏跺硷紝鍚﹀垯娣诲姞鏂扮殑閿煎 + if (stationSignals.ContainsKey(signalName)) + { + stationSignals[signalName] = signalValue; + } + else + { + stationSignals.Add(signalName, signalValue); + } + } + if (Allsignal.ContainsKey(station)) + { + Allsignal[station] = stationSignals; + } + else { + Allsignal.Add(station, stationSignals); + } + + } + string result=JsonConvert.SerializeObject(Allsignal); + //string result = GetListsAsStrings((NOdeIsEnableMissionStatus)status); + + + Diagnosis.Log($"鍚戣糠姣傛彁渚涘畨鍏ㄤ俊鍙---{result}", "signalinfo", true); + + + httpPostData.UploadSignal(result); + Thread.Sleep(500); + } + catch (Exception e) + { + + Diagnosis.Post($"{ExceptionFormatter.FormatEx(e)}", "瀹夊叏淇″彿"); + } + + } + + + }); + } + + [MethodMember(Name = "鍏抽棴鏈烘瀯杩涚▼", Description = "鍏抽棴鏈烘瀯杩涚▼")] + public void Stop() + { + started = false; + status.status = $"宸插仠姝-寰幆"; + } + Stopwatch stopwatch3 = new Stopwatch(); + Stopwatch stopwatch2 = new Stopwatch(); + + + public static string GetListsAsStrings(object obj) + { + Dictionary result = new Dictionary(); + Type type = obj.GetType(); + FieldInfo[] fieldInfo= type.GetFields(); + foreach (FieldInfo property in fieldInfo) + { + if (property.FieldType== typeof(List)) + { + List list = (List)property.GetValue(obj); + result[property.Name] = String.Join(",", list.Select(b => b ? "1" : "0")); + + } + } + return JsonConvert.SerializeObject(result); + } + + } +} diff --git a/StandardScene.Core/StandardCADTool.cs b/StandardScene.Core/StandardCADTool.cs new file mode 100644 index 0000000..604b08c --- /dev/null +++ b/StandardScene.Core/StandardCADTool.cs @@ -0,0 +1,294 @@ +锘縰sing Newtonsoft.Json; +using SimpleLite; +using SimpleLite.RCS; +using SimpleLite.RCS.CarTypes; +using SimpleLite.CADTools; +using SimpleLite.Props; +using SimpleLite.UI; +using SimpleCore; +using SimpleCore.Library; +using SimpleCore.PropType; +using StandardScene.Model; +using System; +using System.Collections.Generic; +using System.Diagnostics.Eventing.Reader; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Forms; + +namespace StandardScene +{ + [CADToolDescriptor(name = "澶嶅埗鐩爣绔欑偣鎵鏈夊瓧娈")] + public class CopySiteFieldsFromTarget : CADTool + { + public override async void Invoke() + { + var sel = SimpleMonitor.selected.OfType().ToList(); + var targetPoint = await Program.UI.getPoint(new UIOps.getPointOptions() { site = true }); + var targetSite = SimpleLib.GetSite(targetPoint.site); + + var ignoreKey = new string[] { "mustFree" }; + if (sel.Count <= 0) return; + foreach (var site in sel) + { + foreach (var field in targetSite.fields) + { + if (ignoreKey.Contains(field.Key)) continue; + site.fields[field.Key] = field.Value; + } + } + } + } + + [CADToolDescriptor(name = "鍖哄煙娴侀噺绠℃帶-闄愬埗杩涘叆鍖哄煙鐨勮溅鏁伴噺")] + public class RegionalTrafficControl : CADTool + { + // 閲嶅啓璋冪敤鏂规硶锛氭墽琛屽尯鍩熸祦閲忕鎺х殑杞﹁締鏁伴噺闄愬埗閰嶇疆 + public override async void Invoke() + { + // 鑾峰彇閫変腑鐨勬墍鏈夌珯鐐筓I瀵硅薄 + var selectedSites = SimpleMonitor.selected.OfType().ToList(); + // 寮瑰嚭杈撳叆妗嗭紝鎻愮ず鐢ㄦ埛鎸夈屽尯鍩熺紪鍙,闄愬埗鏁伴噺銆嶆牸寮忚緭鍏ワ紝鍙栨秷鍒欑洿鎺ヨ繑鍥 + var inputDialogResult = InputBox.ShowDialog("璇疯緭鍏ュ尯鍩熺紪鍙峰拰闄愬埗鏁伴噺锛屾牸寮忥細1,3"); + if (inputDialogResult != SimpleLite.DialogResult.OK) return; + + // 鎷嗗垎杈撳叆鐨勫尯鍩熺紪鍙峰拰闄愬埗鏁伴噺 + var inputValues = InputBox.ResultValue.Split(','); + // 鑻ユ棤閰嶇疆淇℃伅锛岀洿鎺ヨ繑鍥 + if (inputValues.Length <= 1) return; + // 瑙f瀽鍖哄煙缂栧彿锛堟诞鐐瑰瀷淇濈暀鍘熺被鍨嬶紝鍏煎鍚庣画鎵╁睍锛 + var areaNumber = float.Parse(inputValues[0]); + // 瑙f瀽鍖哄煙杞﹁締闄愬埗鏁伴噺锛堟诞鐐瑰瀷淇濈暀鍘熺被鍨嬶紝鍏煎闈炴暣鏁伴厤缃級 + var limitVehicleCount = float.Parse(inputValues[1]); + // 閬嶅巻鎵鏈夐変腑绔欑偣锛屼负鍏舵坊鍔犲尯鍩熸祦閲忕鎺х殑瀛楁閰嶇疆 + foreach (var currentSite in selectedSites) + { + // 閰嶇疆瀛楁锛歛rea+鍖哄煙缂栧彿 浣滀负閿紝闄愬埗鏁伴噺浣滀负鍊 + if (!currentSite.fields.ContainsKey($"Region{areaNumber}")) + { + currentSite.fields.Add($"Region{areaNumber}", limitVehicleCount.ToString()); + + } + + } + } + } + + + [CADToolDescriptor(name = "澶嶅埗鐩爣绔欑偣鎵鏈夊瓧娈(涓嶈鐩)")] + public class CopySiteFieldsFromTargetNoOverwrite : CADTool + { + public override async void Invoke() + { + var sel = SimpleMonitor.selected.OfType().ToList(); + var targetPoint = await Program.UI.getPoint(new UIOps.getPointOptions() { site = true }); + var targetSite = SimpleLib.GetSite(targetPoint.site); + var ignore = new string[] { "mustFree" }; + if (sel.Count <= 0) return; + foreach (var site in sel) + { + foreach (var field in targetSite.fields) + { + if (!site.fields.ContainsKey(field.Key)) + { + site.fields[field.Key] = field.Value; + } + } + } + } + } + + [CADToolDescriptor(name = "澶嶅埗鐩爣绔欑偣棰滆壊")] + public class BatchChangeSiteColor : CADTool + { + public override async void Invoke() + { + var sel = SimpleMonitor.selected.OfType().ToList(); + var targetPoint = await Program.UI.getPoint(new UIOps.getPointOptions() { site = true }); + var targetSite = (UISite)SimpleLib.GetSite(targetPoint.site); + if (sel.Count <= 0) return; + foreach (var site in sel) + { + site.color = targetSite.color; + } + } + } + + [CADToolDescriptor(name = "瀵煎叆FASS鍦板浘")] + public class ImportFASSMap : CADTool + { + public override void Invoke() + { + //鎵撳紑鏂囦欢閫夋嫨妗 + using (var ofd = new System.Windows.Forms.OpenFileDialog()) + { + ofd.InitialDirectory = "C:\\"; + ofd.Filter = "FASS鍦板浘鏂囦欢(*.json)|*.json"; + if (ofd.ShowDialog() == System.Windows.Forms.DialogResult.OK) + { + string filePath = ofd.FileName; + //瑙f瀽鏂囦欢鍐呭 + string jsonString = File.ReadAllText(filePath); + MapStructure mapStructure = JsonConvert.DeserializeObject(jsonString); + Model.Configuration configuration = ConvertMapStructureToConfiguration(mapStructure); + // 杈撳嚭 JSON 瀛楃涓插埌鏂囨湰鏂囦欢 + // 灏 MapStructure 瀵硅薄搴忓垪鍖栦负 JSON 瀛楃涓 + // 璁剧疆鏍煎紡鍖栭夐」 + string jsonSsring = JsonConvert.SerializeObject(configuration); + + // 鑾峰彇妗岄潰璺緞 + string desktopPath = + AppDomain.CurrentDomain + .BaseDirectory; // Environment.GetFolderPath(Environment.SpecialFolder.Desktop); + string outPath = Path.Combine(desktopPath, "output.json"); // 杈撳嚭鏂囦欢璺緞 + + // 杈撳嚭 JSON 瀛楃涓插埌妗岄潰涓婄殑鏂囨湰鏂囦欢 + File.WriteAllText(outPath, jsonSsring); + + + Console.WriteLine($"JSON 鏁版嵁宸叉垚鍔熷啓鍏ュ埌 {outPath}"); + //todo:鍒嗘瀽鏂囦欢鍐呭锛屽苟瀵煎叆绔欑偣銆佽矾寰勪俊鎭 + } + } + } + + + + public static Model.Configuration ConvertMapStructureToConfiguration(MapStructure mapStructure) + { + Model.Configuration config = new Model.Configuration(); + List index = new List(); + + // 澶勭悊 Sites + foreach (var node in mapStructure.Nodes) + { + SimpleSite site = new SimpleSite + { + id = int.Parse(node.Code.Text), // 灏 node.Code.Text 璧嬪肩粰 SimpleSite 鐨 Id + name = node.Name.Text, // 灏 node.Name.Text 璧嬪肩粰 SimpleSite 鐨 Name + x = node.Base.Point.X, // 灏 node.Base.Point.X 璧嬪肩粰 SimpleSite 鐨 X + y = node.Base.Point.Y, // 灏 node.Base.Point.Y 璧嬪肩粰 SimpleSite 鐨 Y + color = "defaultColor", // 榛樿棰滆壊绀轰緥锛屾偍鍙互鏍规嵁闇瑕佹洿鏀 + displaySetting = "defaultDisplay", // 榛樿鏄剧ず璁剧疆绀轰緥锛屾偍鍙互鏍规嵁闇瑕佹洿鏀 + fields = new Dictionary(), // Assuming fields is an empty object + mustFree = new List() // Assuming mustFree is an empty array + }; + + config.Sites[node.Code.Text] = site; // 灏 SimpleSite 娣诲姞鍒 Sites 瀛楀吀涓 + AddInDescendingOrder(index, int.Parse(node.Code.Text)); + } + + // 澶勭悊 Tracks + foreach (var edge in mapStructure.Edges) + { + + int id = index[0] + 1; + AddInDescendingOrder(index, id); + StandardScene.Model.Track track = new StandardScene.Model.Track + { + id = id, // 灏 edge.Index 璧嬪肩粰 Track 鐨 Id + name = "NoName", // 灏 edge.Name.Text 璧嬪肩粰 Track 鐨 Name + siteA = int.Parse(mapStructure.Nodes.Find(n => n.Id == edge.Data.StartNodeId).Code + .Text), // 鎵惧埌 StartNode 鐨勭储寮 + siteB = int.Parse(mapStructure.Nodes.Find(n => n.Id == edge.Data.EndNodeId).Code + .Text), // 鎵惧埌 EndNode 鐨勭储寮 + _siteA = int.Parse(mapStructure.Nodes.Find(n => n.Id == edge.Data.StartNodeId).Code.Text), + _siteB = int.Parse(mapStructure.Nodes.Find(n => n.Id == edge.Data.EndNodeId).Code.Text), + fields = new Dictionary(), + typeInfo = "0", + layerName = "g", + displaySetting = "" + }; + + + config.Tracks[id.ToString()] = track; // 灏 Track 娣诲姞鍒 Tracks 瀛楀吀涓 + + } + + return config; + } + + static void AddInDescendingOrder(List list, int number) + { + // 鎵惧埌鎻掑叆浣嶇疆 + int i = 0; + while (i < list.Count && list[i] >= number) + { + i++; + } + + list.Insert(i, number); // 鍦ㄦ壘鍒扮殑浣嶇疆鎻掑叆 + } + } + + [CADToolDescriptor(name = "鎵归噺闂磋窛鐢熸垚绔欑偣")] + public class BulkIntervalSiteCreator : CADTool + { + public override async void Invoke() + { + + try + { + G.pushStatus("璇烽夋嫨鍙傝冪偣"); + var target = await Program.UI.getPoint(); + var templateSite = SimpleLib.GetAllSites().OrderBy(p => LessMath.dist(target.x, target.y, p.x, p.y)) + .FirstOrDefault(); + // 妫鏌ユ槸鍚︽壘鍒版ā鏉跨珯鐐 + if (templateSite == null) + { + G.pushStatus("鏈壘鍒板弬鑰冪珯鐐"); + return; + } + + // 瀛樺偍鎵鏈夌敓鎴愮殑绔欑偣锛堢敤浜庡鐞嗙粍闂磋繛鎺ワ級 + List generatedSites = new List(); + if (InputBox.ShowDialog("璇疯緭鍏ラ渶瑕佺敓鎴愮殑浜岀淮鐮佹暟閲忥紙蹇呴』鏄伓鏁版暟閲忥級浠ュ強绔欑偣闂磋窛鍜屽欢浼歌搴,锛屾牸寮忎负\"6,1000,0\"銆") != + SimpleLite.DialogResult.OK) return; + generatedSites.Add((UISite)templateSite); + var values = InputBox.ResultValue.Split(',').Select(ss => float.Parse(ss)).ToArray(); + // 楠岃瘉鏁伴噺鏄惁涓哄伓鏁 + if (values[0] % 2 != 0) + { + G.pushStatus("鏁伴噺蹇呴』鏄伓鏁"); + return; + } + + for (int i = 0; i < values[0] / 2; i = i + 2) + { + var curPos = Tuple.Create(templateSite.x, templateSite.y, values[2]); + var targetPos = LessMath.Transform2D(curPos, Tuple.Create(values[1] * (i + 1), 0f, 0f)); + var targetPos2 = LessMath.Transform2D(curPos, Tuple.Create(values[1] * (i + 2), 0f, 0f)); + + var siteA = new UISite() { id = Prop.GenerateID(), x = targetPos.Item1, y = targetPos.Item2 }; + ((UISite)siteA).color = ""; // + var siteB = new UISite() { id = Prop.GenerateID(), x = targetPos2.Item1, y = targetPos2.Item2 }; + SimpleLib.SetSite(siteA); + SimpleLib.SetSite(siteB); + generatedSites.Add(siteA); + generatedSites.Add(siteB); + + } + + for (int i = 0; i < generatedSites.Count - 1; i++) + { + Commons.AddOrUpdateSiteField(generatedSites[i], "tag", "0"); + // 姣忎袱涓浉閭荤珯鐐归兘鍒涘缓璺緞锛堝寘鍚粍鍐呭拰缁勯棿锛 + SimpleLib.SetTrack(new UITrack( + generatedSites[i].id, + generatedSites[i + 1].id + )); + } + } + catch (Exception e) + { + Console.WriteLine(e); + } + + + } + } + + // SyncQrMap锛堝悓姝ヤ簩缁寸爜鍦板浘鍒板皬杞︼級宸茶縼鍑鸿嚦 StandardScene.QrLidar\Cad\SyncQrMap.cs锛坰cene.qrlidar 骞冲彴锛夈 +} diff --git a/StandardScene.Core/StandardScene.Core.csproj b/StandardScene.Core/StandardScene.Core.csproj new file mode 100644 index 0000000..e57e7e9 --- /dev/null +++ b/StandardScene.Core/StandardScene.Core.csproj @@ -0,0 +1,64 @@ +锘 + + + net8.0-windows + Library + true + StandardScene + StandardScene + latest + true + AnyCPU;x64 + x64 + true + false + disable + disable + false + + $(NoWarn);NU1701;CS0618;CS0612;MSB3277;CA1416 + + {HintPathFromItem};{TargetFrameworkDirectory};{RawFileName};{GAC} + + + + + + + + E:\Work\Core\Simple-FR\Simple\SimpleLite\bin\Debug\SimpleLite.dll + + + E:\Work\Core\Simple-FR\Simple\SimpleCore\bin\Debug\netstandard2.0\SimpleCore.dll + + + D:\MDCS\Dependencies\Commons\CommonUsage.dll + + + D:\MDCS\Dependencies\Commons\MDCSToolBox.dll + + + E:\Work\Core\Simple-FR\Simple\tools\Topaz.dll + + + E:\Work\Core\Simple-FR\Simple\SimpleLite\bin\Debug\LessokajiWeaverUtilities.dll + + + Ref\leegKeys-sdk.dll + + + + + + + + + + + + + + + + diff --git a/StandardScene.Core/TCP/AsyncTcpClient.cs b/StandardScene.Core/TCP/AsyncTcpClient.cs new file mode 100644 index 0000000..6a8fd0e --- /dev/null +++ b/StandardScene.Core/TCP/AsyncTcpClient.cs @@ -0,0 +1,502 @@ +using SimpleCore.Library; +using System; +using System.Diagnostics; +using System.Globalization; +using System.Net; +using System.Net.NetworkInformation; +using System.Net.Sockets; +using System.Text; +using System.Threading; +using static System.Windows.Forms.VisualStyles.VisualStyleElement.ToolTip; + +namespace StandardScene.TCP +{ + /// + /// 寮傛 TCP 瀹㈡埛绔 + /// + public class AsyncTcpClient : IDisposable + { + private sealed class DatagramReadState + { + public TcpClient Client { get; set; } + public byte[] Buffer { get; set; } + } + + public event EventHandler> DatagramReceived; // 鎺ユ敹鍒版暟鎹姤鏂囦簨浠 + public event EventHandler> PlaintextReceived; // 鎺ユ敹鍒版暟鎹姤鏂囨槑鏂囦簨浠 + public event EventHandler ServerConnected; // 涓庢湇鍔″櫒鐨勮繛鎺ュ凡寤虹珛浜嬩欢 + public event EventHandler ServerDisconnected; // 涓庢湇鍔″櫒鐨勮繛鎺ュ凡鏂紑浜嬩欢 + public event EventHandler ServerExceptionOccurred; // 涓庢湇鍔″櫒鐨勮繛鎺ュ彂鐢熷紓甯镐簨浠 + + private TcpClient tcpClient; + private bool disposed = false; + private int retries = 0; // 閲嶈繛璁℃暟 + + private readonly object _reconnectGate = new object(); + private bool _closing = false; + private bool _isConnecting = false; + private bool _isReconnecting = false; + private Timer _reconnectTimer; + + public AsyncTcpClient(IPAddress remoteIPAddress, int remotePort) + { + this.Addresses = remoteIPAddress; + this.Port = remotePort; + this.Encoding = Encoding.Default; + this.Retries = 3; + this.RetryInterval = 5; + } + + /// + /// 鏄惁宸蹭笌鏈嶅姟鍣ㄥ缓绔嬭繛鎺 + /// + public bool Connected + { + get + { + try + { + return this.tcpClient != null && this.tcpClient.Connected; + } + catch + { + return false; + } + } + } + + /// + /// 杩滅鏈嶅姟鍣ㄧ殑IP鍦板潃鍒楄〃 + /// + public IPAddress Addresses { get; private set; } + + /// + /// 杩滅鏈嶅姟鍣ㄧ殑绔彛 + /// + public int Port { get; private set; } + + /// + /// 杩炴帴閲嶈瘯娆℃暟 + /// + public int Retries { get; set; } + + /// + /// 杩炴帴閲嶈瘯闂撮殧 + /// + public int RetryInterval { get; set; } + + /// + /// 杩滅鏈嶅姟鍣ㄧ粓缁撶偣 + /// + public IPEndPoint RemoteIPEndPoint + { + get + { + return new IPEndPoint(this.Addresses, this.Port); + } + } + + /// + /// 閫氫俊鎵浣跨敤鐨勭紪鐮 + /// + public Encoding Encoding { get; set; } + uint on = 1; + + /// + /// 杩炴帴鍒版湇鍔″櫒 + /// + /// + public AsyncTcpClient Connect() + { + lock (_reconnectGate) + { + _closing = false; + } + + if (this.Connected) + { + return this; + } + + this.ConnectInternal(resetRetries: true); + return this; + } + + private void ConnectInternal(bool resetRetries) + { + TcpClient client; + + lock (_reconnectGate) + { + if (_closing || disposed) + { + return; + } + + // Prevent multiple in-flight connection attempts. + if (_isConnecting || _isReconnecting) + { + return; + } + + _isConnecting = true; + if (resetRetries) + { + retries = 0; + } + + client = new TcpClient(); + this.tcpClient = client; + } + + try + { + client.Client.IOControl(IOControlCode.KeepAliveValues, KeepAlive(1, 500, 500), null); + client.BeginConnect(this.Addresses, this.Port, new AsyncCallback(this.HandleTcpServerConnected), client); + } + catch + { + try { client.Close(); } catch { } + + lock (_reconnectGate) + { + _isConnecting = false; + } + + if (!_closing && !disposed) + { + ScheduleReconnect("connect begin failed"); + } + } + } + + private void HandleRemoteDisconnect(TcpClient client, string reason) + { + if (!ReferenceEquals(client, this.tcpClient)) + { + return; + } + + try { client.Close(); } catch { } + + this.RaiseServerDisconnected(this.Addresses, this.Port); + + if (!_closing && !disposed) + { + ScheduleReconnect(reason); + } + } + + private void ScheduleReconnect(string reason) + { + lock (_reconnectGate) + { + if (_closing || disposed) + { + return; + } + + if (this.Connected) + { + return; + } + + if (_isConnecting || _isReconnecting) + { + return; + } + + // Keep reconnecting until the client is closed/disposed. + // Preserve the counter only for logging (avoid int overflow by wrapping). + if (retries == int.MaxValue) + { + retries = 0; + } + retries++; + _isReconnecting = true; + + if (_reconnectTimer != null) + { + try { _reconnectTimer.Dispose(); } catch { } + _reconnectTimer = null; + } + + Diagnosis.Post($"[AsyncTcpClient] schedule reconnect attempt {retries}/{this.Retries} in {this.RetryInterval}s. Reason={reason}"); + + Timer t = null; + t = new Timer(_ => + { + try + { + lock (_reconnectGate) + { + _isReconnecting = false; + _reconnectTimer = null; + } + + this.ConnectInternal(resetRetries: false); + } + catch { } + finally + { + try { t.Dispose(); } catch { } + } + }, null, TimeSpan.FromSeconds((double)this.RetryInterval), Timeout.InfiniteTimeSpan); + + _reconnectTimer = t; + } + } + + private byte[] KeepAlive(int onOff, int keepAliveTime, int keepAliveInterval) + { + byte[] buffer = new byte[12]; + BitConverter.GetBytes(onOff).CopyTo(buffer, 0); + BitConverter.GetBytes(keepAliveTime).CopyTo(buffer, 4); + BitConverter.GetBytes(keepAliveInterval).CopyTo(buffer, 8); + return buffer; + } + + /// + /// 鍏抽棴涓庢湇鍔″櫒鐨勮繛鎺 + /// + /// 寮傛TCP瀹㈡埛绔 + public AsyncTcpClient Close() + { + TcpClient clientToClose = null; + bool wasConnected = false; + + lock (_reconnectGate) + { + _closing = true; + retries = 0; + _isConnecting = false; + _isReconnecting = false; + + if (_reconnectTimer != null) + { + try { _reconnectTimer.Dispose(); } catch { } + _reconnectTimer = null; + } + + clientToClose = this.tcpClient; + wasConnected = clientToClose != null && clientToClose.Connected; + this.tcpClient = null; + } + + if (clientToClose != null) + { + try { clientToClose.Close(); } catch { } + } + + if (wasConnected) + { + this.RaiseServerDisconnected(this.Addresses, this.Port); + } + return this; + } + + private void HandleTcpServerConnected(IAsyncResult ar) + { + TcpClient client = (TcpClient)ar.AsyncState; + try + { + if (!ReferenceEquals(client, this.tcpClient)) + { + // Stale connect callback for an old TcpClient instance. + try { client.Close(); } catch { } + return; + } + + client.EndConnect(ar); + this.RaiseServerConnected(this.Addresses, this.Port); + + lock (_reconnectGate) + { + this.retries = 0; + _isConnecting = false; + _isReconnecting = false; + + if (_reconnectTimer != null) + { + try { _reconnectTimer.Dispose(); } catch { } + _reconnectTimer = null; + } + } + + byte[] buffer = new byte[client.ReceiveBufferSize]; + var state = new DatagramReadState { Client = client, Buffer = buffer }; + client.GetStream().BeginRead(buffer, 0, buffer.Length, new AsyncCallback(this.HandleDatagramReceived), state); + } + catch (Exception ex) + { + if (!ReferenceEquals(client, this.tcpClient)) + { + return; + } + + lock (_reconnectGate) + { + _isConnecting = false; + } + + if (!_closing && !disposed) + { + Diagnosis.Post($" HandleTcpServerConnected {this.Addresses} {this.Port} 杩炴帴鏂紑,灏濊瘯閲嶈繛..."); + ScheduleReconnect("connect failed"); + } + } + } + + private void HandleDatagramReceived(IAsyncResult ar) + { + DatagramReadState state = null; + TcpClient client = null; + byte[] buffer = null; + + try + { + state = (DatagramReadState)ar.AsyncState; + client = state.Client; + buffer = state.Buffer; + + if (!ReferenceEquals(client, this.tcpClient)) + { + // Stale read callback for an old TcpClient instance. + return; + } + + NetworkStream stream = client.GetStream(); + int numberOfReadBytes = 0; + try + { + numberOfReadBytes = stream.EndRead(ar); + } + catch + { + numberOfReadBytes = 0; + } + + if (numberOfReadBytes == 0) + { + HandleRemoteDisconnect(client, "zero-byte read"); + return; + } + + byte[] receivedBytes = new byte[numberOfReadBytes]; + Buffer.BlockCopy(buffer, 0, receivedBytes, 0, numberOfReadBytes); + this.RaiseDatagramReceived(client, receivedBytes); + this.RaisePlaintextReceived(client, receivedBytes); + + // then start reading from the network again + stream.BeginRead(buffer, 0, buffer.Length, new AsyncCallback(this.HandleDatagramReceived), state); + } + catch (Exception ex) + { + Trace.WriteLine(ex.Message); + + if (client != null && ReferenceEquals(client, this.tcpClient) && !_closing && !disposed) + { + HandleRemoteDisconnect(client, "read failed"); + } + } + } + + private void RaiseDatagramReceived(TcpClient sender, byte[] datagram) + { + if (this.DatagramReceived != null) + { + this.DatagramReceived(this, new TcpDatagramReceivedEventArgs(sender, datagram)); + } + } + + private void RaisePlaintextReceived(TcpClient sender, byte[] datagram) + { + if (this.PlaintextReceived != null) + { + //this.PlaintextReceived(this, new TcpDatagramReceivedEventArgs(sender, this.Encoding.GetString(datagram, 0, datagram.Length))); + this.PlaintextReceived(this, new TcpDatagramReceivedEventArgs(sender, datagram)); + } + } + + private void RaiseServerConnected(IPAddress ipAddresses, int port) + { + if (this.ServerConnected != null) + { + this.ServerConnected(this, new TcpServerConnectedEventArgs(ipAddresses, port)); + } + } + + private void RaiseServerDisconnected(IPAddress ipAddresses, int port) + { + if (this.ServerDisconnected != null) + { + this.ServerDisconnected(this, new TcpServerDisconnectedEventArgs(ipAddresses, port)); + } + } + + private void RaiseServerExceptionOccurred(IPAddress ipAddresses, int port, Exception innerException) + { + if (this.ServerExceptionOccurred != null) + { + this.ServerExceptionOccurred(this, new TcpServerExceptionOccurredEventArgs(ipAddresses, port, innerException)); + } + } + + /// + /// 鍙戦佹姤鏂 + /// + /// + public void Send(byte[] datagram) + { + if (datagram == null) + { + throw new ArgumentNullException("datagram"); + } + if (!this.Connected) + { + this.RaiseServerDisconnected(this.Addresses, this.Port); + throw new InvalidProgramException("This client has not connected to server."); + } + this.tcpClient.GetStream().BeginWrite(datagram, 0, datagram.Length, new AsyncCallback(this.HandleDatagramWritten), this.tcpClient); + } + + private void HandleDatagramWritten(IAsyncResult ar) + { + ((TcpClient)ar.AsyncState).GetStream().EndWrite(ar); + } + + public void Send(string datagram) + { + this.Send(this.Encoding.GetBytes(datagram)); + } + + /// + /// 閲婃斁闈炴墭绠¤祫婧 + /// + public void Dispose() + { + this.Dispose(true); + GC.SuppressFinalize(this); + } + + protected virtual void Dispose(bool disposing) + { + if (!this.disposed) + { + this.disposed = true; + + if (disposing) + { + try + { + this.Close(); + } + catch// (SocketException ex) + { + } + } + } + } + + } +} \ No newline at end of file diff --git a/StandardScene.Core/TCP/TcpDatagramReceivedEventArgs.cs b/StandardScene.Core/TCP/TcpDatagramReceivedEventArgs.cs new file mode 100644 index 0000000..2e7e608 --- /dev/null +++ b/StandardScene.Core/TCP/TcpDatagramReceivedEventArgs.cs @@ -0,0 +1,22 @@ +锘縰sing System; +using System.Net.Sockets; + +namespace StandardScene.TCP +{ + /// + /// 鎺ユ敹鍒版暟鎹姤鏂囦簨浠 + /// + /// + public class TcpDatagramReceivedEventArgs : EventArgs + { + public TcpDatagramReceivedEventArgs(TcpClient tcpClient, T datagram) + { + this.TcpClient = tcpClient; + this.Datagram = datagram; + } + + public TcpClient TcpClient { get; private set; } + + public T Datagram { get; private set; } + } +} \ No newline at end of file diff --git a/StandardScene.Core/TCP/TcpServerConnectedEventArgs.cs b/StandardScene.Core/TCP/TcpServerConnectedEventArgs.cs new file mode 100644 index 0000000..8d0232d --- /dev/null +++ b/StandardScene.Core/TCP/TcpServerConnectedEventArgs.cs @@ -0,0 +1,31 @@ +锘縰sing System; +using System.Globalization; +using System.Net; + +namespace StandardScene.TCP +{ + /// + /// 涓庢湇鍔″櫒鐨勮繛鎺ュ凡寤虹珛浜嬩欢 + /// + public class TcpServerConnectedEventArgs : EventArgs + { + public TcpServerConnectedEventArgs(IPAddress ipAddress, int port) + { + if (ipAddress == null) + { + throw new ArgumentNullException("ipAddress"); + } + this.Address = ipAddress; + this.Port = port; + } + + public IPAddress Address { get; private set; } + + public int Port { get; private set; } + + public override string ToString() + { + return this.Address + ":" + this.Port.ToString(CultureInfo.InvariantCulture); + } + } +} \ No newline at end of file diff --git a/StandardScene.Core/TCP/TcpServerDisconnectedEventArgs.cs b/StandardScene.Core/TCP/TcpServerDisconnectedEventArgs.cs new file mode 100644 index 0000000..3f06c03 --- /dev/null +++ b/StandardScene.Core/TCP/TcpServerDisconnectedEventArgs.cs @@ -0,0 +1,31 @@ +锘縰sing System; +using System.Globalization; +using System.Net; + +namespace StandardScene.TCP +{ + /// + /// 涓庢湇鍔″櫒鐨勮繛鎺ュ凡鏂紑浜嬩欢 + /// + public class TcpServerDisconnectedEventArgs : EventArgs + { + public TcpServerDisconnectedEventArgs(IPAddress ipAddress, int port) + { + if (ipAddress == null) + { + throw new ArgumentNullException("ipAddress"); + } + this.Address = ipAddress; + this.Port = port; + } + + public IPAddress Address { get; private set; } + + public int Port { get; private set; } + + public override string ToString() + { + return this.Address + ":" + this.Port.ToString(CultureInfo.InvariantCulture); + } + } +} \ No newline at end of file diff --git a/StandardScene.Core/TCP/TcpServerExceptionOccurredEventArgs.cs b/StandardScene.Core/TCP/TcpServerExceptionOccurredEventArgs.cs new file mode 100644 index 0000000..471e563 --- /dev/null +++ b/StandardScene.Core/TCP/TcpServerExceptionOccurredEventArgs.cs @@ -0,0 +1,34 @@ +锘縰sing System; +using System.Globalization; +using System.Net; + +namespace StandardScene.TCP +{ + /// + /// 涓庢湇鍔″櫒鐨勮繛鎺ュ彂鐢熷紓甯镐簨浠 + /// + public class TcpServerExceptionOccurredEventArgs : EventArgs + { + public TcpServerExceptionOccurredEventArgs(IPAddress ipAddresses, int port, Exception innerException) + { + if (ipAddresses == null) + { + throw new ArgumentNullException("ipAddress"); + } + this.Address = ipAddresses; + this.Port = port; + this.Exception = innerException; + } + + public IPAddress Address { get; private set; } + + public int Port { get; private set; } + + public Exception Exception { get; private set; } + + public override string ToString() + { + return this.Address + ":" + this.Port.ToString(CultureInfo.InvariantCulture); + } + } +} \ No newline at end of file diff --git a/StandardScene.Core/Utils/JsonParser.cs b/StandardScene.Core/Utils/JsonParser.cs new file mode 100644 index 0000000..2b152d6 --- /dev/null +++ b/StandardScene.Core/Utils/JsonParser.cs @@ -0,0 +1,50 @@ +锘縰sing Newtonsoft.Json; +using SimpleCore.Library; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using StandardScene.Chained; + +namespace StandardScene.Utils +{ + public static class JsonParser + { + public static object fileSync = new object(); + + public static void WriteJsonFile(TransportDelivery obj) + { + try + { + lock (fileSync) + File.WriteAllText($"log/tasklist/{obj.TaskId}.json", + JsonConvert.SerializeObject(obj, Formatting.Indented)); + } + catch (Exception e) + { + Diagnosis.Log("write tasklist error" + ExceptionFormatter.FormatEx(e), "error", true); + } + } + + public static string ReadJsonFile(string path) + { + lock (fileSync) + { + return File.ReadAllText(path); + } + } + + // Json->Object + //DataContractJsonSerializer jsonSerializer = new DataContractJsonSerializer(typeof(ChainedDeliveryMission.Delivery)); + public static TransportDelivery Deserialize(string json) + { + TransportDelivery obj = new TransportDelivery(); + + using MemoryStream ms = new MemoryStream(Encoding.UTF8.GetBytes(json)); + JsonConvert.PopulateObject(json, obj); + return obj; + } + } +} diff --git a/StandardScene.Core/Utils/JsonTool.cs b/StandardScene.Core/Utils/JsonTool.cs new file mode 100644 index 0000000..6a126b6 --- /dev/null +++ b/StandardScene.Core/Utils/JsonTool.cs @@ -0,0 +1,37 @@ +锘縰sing Newtonsoft.Json.Linq; +using Newtonsoft.Json.Serialization; +using Newtonsoft.Json; + +namespace StandardScene.Utils +{ + public static class JsonTool + { + private static readonly JsonSerializerSettings DefaultJsonSerializerSettings = new JsonSerializerSettings() + { + ContractResolver = new CamelCasePropertyNamesContractResolver(), + ReferenceLoopHandling = ReferenceLoopHandling.Ignore, + DateFormatHandling = DateFormatHandling.MicrosoftDateFormat, + DateFormatString = "yyyy-MM-dd HH:mm:ss" + }; + public static string ToJson(this object obj) + { + return JsonConvert.SerializeObject(obj, DefaultJsonSerializerSettings); + } + public static string ToJson(this object obj, JsonSerializerSettings jsonSerializerSettings) + { + return JsonConvert.SerializeObject(obj, jsonSerializerSettings); + } + public static T JsonTo(this string Json) + { + return JsonConvert.DeserializeObject(Json); + } + public static object JsonToObject(this string Json) + { + return JsonConvert.DeserializeObject(Json); + } + public static JObject JsonToJObject(this string Json) + { + return JObject.Parse(Json); + } + } +} diff --git a/StandardScene.Core/Utils/ModbusClass.cs b/StandardScene.Core/Utils/ModbusClass.cs new file mode 100644 index 0000000..fa72bee --- /dev/null +++ b/StandardScene.Core/Utils/ModbusClass.cs @@ -0,0 +1,120 @@ +锘縰sing System; +using System.IO.Ports; +using System.Linq; +using System.Net.Sockets; +using System.Threading; +using EasyModbus; + +namespace StandardScene.Utils +{ + + public class ModbusRtu + { + + public bool IsDebug { get; set; } + public byte[] ReceiveAfterSend; + public ModbusClient modbusRtu; + //modbusRtu + public void StartRtu(string com, int baudRate, Parity parity = Parity.None, StopBits stopBits = StopBits.One, int timeOut = 500) + { + modbusRtu = new ModbusClient(com); + modbusRtu.Baudrate = baudRate; + modbusRtu.Parity = parity; + modbusRtu.StopBits = stopBits; + modbusRtu.ConnectionTimeout = timeOut; + modbusRtu.Connect(); + } + //modbusTcp + public void StartTcpRtu(string ip, int port) + { + modbusRtu = new ModbusClient(ip, port); + modbusRtu.Connect(ip, port); + } + + public void Close() + { + modbusRtu.Disconnect(); + } + + #region ReadData + public bool[] ReadCoilBuffer_01(byte slaveAddress, ushort startAddress, ushort numberOfPoints)//01 璇诲彇鍗曚釜绾垮湀 + { + modbusRtu.UnitIdentifier = slaveAddress; + var data = modbusRtu.ReadCoils(startAddress, numberOfPoints); + return data; + } + public bool[] ReadDiscreteInputs_02(byte slaveAddress, ushort startAddress, ushort numberOfPoints)//02 璇诲彇杈撳叆绾垮湀/绂绘暎閲忕嚎鍦 + { + modbusRtu.UnitIdentifier = slaveAddress; + var data = modbusRtu.ReadDiscreteInputs(startAddress, numberOfPoints); + return data; + } + public int[] ReadRegisterBuffer_03(byte slaveAddress, ushort startAddress, ushort numberOfPoints)//03 璇诲彇淇濇寔瀵勫瓨鍣 + { + modbusRtu.UnitIdentifier = slaveAddress; + var data = modbusRtu.ReadHoldingRegisters(startAddress, numberOfPoints); + + return data; + } + public byte[] ReadRegisterBuffer_03_Byte(byte slaveAddress, ushort startAddress, ushort numberOfPoints)//03 璇诲彇淇濇寔瀵勫瓨鍣 + { + modbusRtu.UnitIdentifier = slaveAddress; + var data = modbusRtu.ReadHoldingRegisters(startAddress, numberOfPoints); + byte[] values = new byte[] { }; + foreach (var buff in data) + { + values = values.Concat(BitConverter.GetBytes((Int16)buff).AsEnumerable().Reverse()).ToArray(); + } + return values; + } + + + public byte[] ReadInputBuffer_04(byte slaveAddress, ushort startAddress, ushort numberOfPoints)//04 璇诲彇杈撳叆瀵勫瓨鍣 + { + + modbusRtu.UnitIdentifier = slaveAddress; + var data = modbusRtu.ReadInputRegisters(startAddress, numberOfPoints); + byte[] values = new byte[] { }; + foreach (var buff in data) + { + values = values.Concat(BitConverter.GetBytes((Int16)buff).AsEnumerable().Reverse()).ToArray(); + } + return values; + } + #endregion + + #region Write + public void WriteSingleCoil_05(byte slaveAddress, ushort startAddress, bool Buffer)//05 鍐欏崟涓嚎鍦 + { + ReceiveAfterSend = null; + modbusRtu.UnitIdentifier = slaveAddress; + modbusRtu.WriteSingleCoil(startAddress, Buffer); + ReceiveAfterSend = modbusRtu.receiveData; + } + public void WriteSingleRegister_06(byte slaveAddress, ushort startAddress, int Buffer)//06 鍐欏崟瀵勫瓨鍣 + { + ReceiveAfterSend = null; + modbusRtu.UnitIdentifier = slaveAddress; + modbusRtu.WriteSingleRegister(startAddress, Buffer); + ReceiveAfterSend = modbusRtu.receiveData; + } + public void WriteMultipleCoils_15(byte slaveAddress, ushort startAddress, bool[] Buffer)//15鍐欎竴缁勭嚎鍦 + { + ReceiveAfterSend = null; + modbusRtu.UnitIdentifier = slaveAddress; + modbusRtu.WriteMultipleCoils(startAddress, Buffer); + ReceiveAfterSend = modbusRtu.receiveData; + } + public void WriteMultipleRegisters_16(byte slaveAddress, ushort startAddress, int[] Buffers)//16 鍐欎竴缁勪繚鎸佸瘎瀛樺櫒 + { + ReceiveAfterSend = null; + modbusRtu.UnitIdentifier = slaveAddress; + modbusRtu.WriteMultipleRegisters(startAddress, Buffers); + ReceiveAfterSend = modbusRtu.receiveData; + + } + #endregion + + + } +} diff --git a/StandardScene.Core/Utils/WebAPIHelper.cs b/StandardScene.Core/Utils/WebAPIHelper.cs new file mode 100644 index 0000000..a377406 --- /dev/null +++ b/StandardScene.Core/Utils/WebAPIHelper.cs @@ -0,0 +1,163 @@ +锘縰sing SimpleCore.Library; +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Globalization; +using System.Net.Http; +using System.Threading.Tasks; + +namespace StandardScene.Utils +{ + public class WebAPIHelper + { + private static WebAPIHelper instance = null; + + public static WebAPIHelper Instance + { + get + { + return instance ?? (instance = new WebAPIHelper()); + } + } + + private ConcurrentDictionary clientPool = new ConcurrentDictionary(); + + private HttpClient getClient(string urlString) + { + var url = new Uri(urlString); + var key = $"{url.Host}:{url.Port}"; + return clientPool.GetOrAdd(key, _ => createClient()); + } + + private HttpClient createClient() + { + var client = new HttpClient(); + client.Timeout = TimeSpan.FromSeconds(3); + try + { + client.DefaultRequestHeaders.Add("User-Agent", "MDS/1.1"); + client.DefaultRequestHeaders.Add("Accept", "*/*"); + } + catch (Exception e) + { + Diagnosis.Log($"fail to add http client default header:{e}", "task", true); + } + return client; + } + + public async Task GetStringAsync(string url) + { + return await getClient(url).GetStringAsync(url); + } + + // /// + // /// 璇诲彇HTTP Get鍝嶅簲鍖呮枃銆 + // /// + // /// 杩斿洖缁撴灉鐨勭被鍨 + // /// 璧勬簮鍦板潃 + // /// 鍙夌殑HTTP澶村垪琛 + // /// 鍝嶅簲缁撴灉 + // public async Task GetAsync(string uriString, Dictionary headers = null) + // { + // T retv = default; + // + // HttpClient hc = getClient(uriString); + // var request = new HttpRequestMessage(HttpMethod.Get, uriString); + // if (headers != null) + // { + // foreach (var key in headers.Keys) + // { + // request.Headers.Add(key, headers[key]); + // } + // } + // var resp = await hc.SendAsync(request); + // resp.EnsureSuccessStatusCode(); + // + // var opt = new JsonSerializerOptions + // { + // PropertyNameCaseInsensitive = true + // }; + // opt.Converters.Add(new MyDateTimeConverter()); + // var json = await resp.Content.ReadAsStringAsync(); + // retv = JsonSerializer.Deserialize(json, opt); + // + // return retv; + // } + // + // private static JsonSerializerOptions jsonOptions = new JsonSerializerOptions + // { + // PropertyNamingPolicy = JsonNamingPolicy.CamelCase, + // WriteIndented = true + // }; + + // /// + // /// 璇诲彇HTTP POST缁撴灉銆 + // /// + // /// 鍝嶅簲缁撴灉绫诲瀷 + // /// POST瀵硅薄绫诲瀷 + // /// 璧勬簮鍦板潃 + // /// 璇锋眰鍖呮枃瀵硅薄 + // /// 鍙塇TTP澶村垪琛 + // /// 鏄惁鍐欏叆鏃ュ織锛岄粯璁や负鐪 + // /// 鍝嶅簲瀵硅薄 + // public async Task PostAsync(string uriString, T2 data, Dictionary headers = null, bool logging = true) + // { + // //if (logging) + // Diagnosis.Log($"post to {uriString}\n{JsonSerializer.Serialize(data)}", "task", true); + // T1 retv = default; + // + // HttpClient hc = getClient(uriString); + // var request = new HttpRequestMessage(HttpMethod.Post, uriString); + // if (headers != null) + // { + // foreach (var key in headers.Keys) + // { + // request.Headers.Add(key, headers[key]); + // } + // } + // + // HttpContent content = new StringContent(JsonSerializer.Serialize( + // data, + // jsonOptions + // )); + // content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/json"); + // request.Content = content; + // var resp = await hc.SendAsync(request); + // resp.EnsureSuccessStatusCode(); + // if (logging) + // Diagnosis.Post(JsonSerializer.Serialize(resp)); + // + // var opt = new JsonSerializerOptions + // { + // PropertyNameCaseInsensitive = true + // }; + // opt.Converters.Add(new MyDateTimeConverter()); + // var json = await resp.Content.ReadAsStringAsync(); + // Diagnosis.Log($"got post response\n{json}", "task", true); + // + // if (resp.StatusCode != System.Net.HttpStatusCode.OK) + // Diagnosis.Log($"post {uriString} return not ok\nStatusCode={resp.StatusCode.ToString()}\njson", "task", true); + // retv = JsonSerializer.Deserialize(json, opt); + // + // return retv; + // } + } + + // public class MyDateTimeConverter : JsonConverter + // { + // public override bool CanConvert(Type typeToConvert) + // { + // return typeToConvert == typeof(DateTime); + // } + // + // public override DateTime Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + // { + // return DateTime.ParseExact(reader.GetString(), "yyyy-MM-dd HH:mm:ss", CultureInfo.InvariantCulture); + // } + // + // public override void Write(Utf8JsonWriter writer, DateTime value, JsonSerializerOptions options) + // { + // writer.WriteStringValue(value.ToString("yyyy-MM-dd HH:mm:ss")); + // } + // } +} diff --git a/StandardScene.Core/WebApi.cs b/StandardScene.Core/WebApi.cs new file mode 100644 index 0000000..1ced9ea --- /dev/null +++ b/StandardScene.Core/WebApi.cs @@ -0,0 +1,2878 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Net.Http; +using System.Net.Http.Headers; +using System.Reflection; +using System.Security.Cryptography; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using System.Xml.Linq; +using MDCSToolBox.Clumsy.Movements; +using Nancy; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using SimpleLite; +using SimpleLite.RCS; +using SimpleLite.RCS.CarTypes; +using SimpleLite.CADTools; +using SimpleLite.Props; +using SimpleLite.UI; +using SimpleCore; +using SimpleCore.Library; +using SimpleCore.PropType; +using StandardScene.Chained; +using StandardScene.Charge; +using StandardScene.Model; +using StandardScene.Utils; +using static StandardScene.Chained.ChainedDeliveryMission; +using Map = StandardScene.Model.Map; +using Nancy.Session; +using StandardScene.InterLock; + +namespace StandardScene +{ + /// + /// 銆愯繃娓℃湡閬楃暀銆戣佸钩鍙 Nancy HTTP 鎺ュ彛锛40+ 绔偣锛夈傚鑸﹀悎鏋佸急锛氫粎浜岀淮鐮 QrMap锛堟暟鎹粛瀛 + /// 鏈被闈欐佸瓧娈碉紝鐢 scene.qrlidar 鎻掍欢鐨 SyncQrMap 宸ュ叿鍐欏叆锛変笌 getLidarMap銆 + /// 鎷嗗垎璁″垝 搂4.7锛氭殏鐣 Core 缁存寔鐜板満鍏煎锛屽悗缁愭杩佸線 SimpleLite 鐨 EmbedIO 鎺ュ彛锛圡IGU-API锛夛紝 + /// 鏂板姛鑳借鍕垮湪姝よ拷鍔犵鐐广 + /// + public class ApiController : NancyModule + { + private static readonly HttpClient SharedHttpClient = new HttpClient(); + private static readonly string ApiToken = Environment.GetEnvironmentVariable("STANDARDSCENE_API_TOKEN"); + private static List _taskRequests = new List(); + public static MyDict QrMap = + new MyDict(); + public static string QrMapJson; + + private string GetMethods(Type baseClassType) + { + var assembly = Assembly.GetExecutingAssembly(); + var derivedTypes = assembly.GetTypes() + .Where(t => t.IsSubclassOf(baseClassType)); + + var classMethodsDictionary = new Dictionary>(); + + foreach (var type in derivedTypes) + { + // old-school MethodMember + var methods = type.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance) + .Where(m => m.GetCustomAttribute() != null && m.GetCustomAttribute() == null) + .Select(m => + { + var attr = m.GetCustomAttribute(); + var ret = new MethodInfoDetails() { MethodName = m.Name }; + var nameField = attr.Name; + if (nameField != null) ret.ButtonName = nameField; + var descField = attr.Description; + if (descField != null) ret.ButtonDescription = descField; + return ret; + }) + .ToList(); + + // Methods with parameters + var paramMethods = type.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance) + .Where(m => m.GetCustomAttribute() != null) + .Select(m => + { + var attr = m.GetCustomAttribute(); + var ret = new MethodInfoDetails() { MethodName = m.Name }; + var nameField = attr.name; + if (nameField != null) ret.ButtonName = nameField; + var descField = attr.desc; + if (descField != null) ret.ButtonDescription = descField; + var hintField = attr.Hint; + if (hintField != null) ret.ParamsHint = hintField; + ret.ParamsList = m.GetParameters().Select(mp => new ParamInfo(mp.Name, mp.ParameterType)).ToList(); + return ret; + }) + .ToList(); + + classMethodsDictionary[type.Name] = methods.Concat(paramMethods).ToList(); + } + + return JsonConvert.SerializeObject( + new { Success = true, Code = 200, Data = classMethodsDictionary, Message = "Success" }, + Formatting.Indented); + } + + //鑾峰彇鎸囧畾class鎵鏈夋柟娉曠殑瀛楀吀 + private Dictionary> GetTypeMethods(Type type) + { + string[] shieldMethodButtonNames = new[] + { "瀵煎嚭灏忚溅鏃ュ織", "鍓嶅線绔欑偣", "閲嶅惎閫氫俊杩涚▼", "鎵嬪姩涓嬪彂鎸囦护", "璁″垝閲嶅惎", "鎶涘嚭涓柇", "鏀捐", "鏄剧ず璋冭瘯淇℃伅", "缁欓冮歌矾寰勭殑鍒濆鍖","杩斿巶妫淇" }; + var classMethodsDictionary = new Dictionary>(); + // old-school MethodMember + var methods = type.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance) + .Where(m => m.GetCustomAttribute() != null && m.GetCustomAttribute() == null) + .Where(m => !shieldMethodButtonNames.Any(n => m.GetCustomAttribute().Name.Contains(n))) + .Select(m => + { + var attr = m.GetCustomAttribute(); + var ret = new MethodInfoDetails() { MethodName = m.Name }; + var nameField = attr.Name; + if (nameField != null) ret.ButtonName = nameField; + var descField = attr.Description; + if (descField != null) ret.ButtonDescription = descField; + return ret; + }) + .ToList(); + + // Methods with parameters + var paramMethods = type.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance) + .Where(m => m.GetCustomAttribute() != null) + .Select(m => + { + var attr = m.GetCustomAttribute(); + var ret = new MethodInfoDetails() { MethodName = m.Name }; + var nameField = attr.name; + if (nameField != null) ret.ButtonName = nameField; + var descField = attr.desc; + if (descField != null) ret.ButtonDescription = descField; + var hintField = attr.Hint; + if (hintField != null) ret.ParamsHint = hintField; + ret.ParamsList = m.GetParameters().Select(mp => new ParamInfo(mp.Name, mp.ParameterType)).ToList(); + return ret; + }) + .ToList(); + + classMethodsDictionary[type.Name] = methods.Concat(paramMethods).ToList(); + + return classMethodsDictionary; + } + + /// + /// 鍙嶅皠璋冪敤鐧藉悕鍗曪細浠呭厑璁告樉寮忔爣娉ㄤ负鍙毚闇茬殑鏂规硶琚 HTTP 鍙嶅皠璋冪敤锛 + /// 涓 GetMethods 鐨勬毚闇插彛寰勪竴鑷达紝閬垮厤鈥滄寜鍚嶈皟鐢ㄤ换鎰 public 鏂规硶鈥濈殑杩滅▼鎵ц椋庨櫓銆 + /// + private static bool IsReflectionInvokable(MethodInfo methodInfo) + { + if (methodInfo == null) return false; + if (methodInfo.GetCustomAttribute() != null) return false; + return methodInfo.GetCustomAttribute() != null + || methodInfo.GetCustomAttribute() != null; + } + + private static string ReflectionForbidden(object id, string method) + { + return JsonConvert.SerializeObject(new + { + Success = false, + Code = 403, + Data = "null", + Message = $"method {method} of {id} is not exposed for reflection invocation" + }, Formatting.Indented); + } + + private bool IsAuthorized() + { + if (string.IsNullOrEmpty(ApiToken)) return true; + try + { + var q = ((DynamicDictionary)Request.Query).ToDictionary(); + return q.TryGetValue("token", out var t) && t != null && t.ToString() == ApiToken; + } + catch + { + return false; + } + } + + private static string Unauthorized() + { + return JsonConvert.SerializeObject(new + { + Success = false, + Code = 401, + Data = "null", + Message = "unauthorized: missing or invalid api token" + }, Formatting.Indented); + } + + private dynamic CarReflectionExecute(dynamic parameters) + { + try + { + if (!IsAuthorized()) return Unauthorized(); + var car = SimpleLib.GetCar((int)parameters.id); + if (car == null) + return JsonConvert.SerializeObject( + new { Success = false, Code = 500, Data = "null", Message = $"no car of id {parameters.id}" }, Formatting.Indented); + var queryParams = ((DynamicDictionary)Request.Query).ToDictionary(); + queryParams.Remove("token"); + MethodInfo methodInfo = null; + if (queryParams.Keys.Count == 0) + methodInfo = car.GetType().GetMethod((string)parameters.method, new Type[] { }); + else + methodInfo = car.GetType().GetMethod((string)parameters.method); + if (methodInfo == null) + return JsonConvert.SerializeObject( + new { Success = false, Code = 500, Data = "null", Message = $"car {parameters.id} has no method {parameters.method}" }, Formatting.Indented); + if (!IsReflectionInvokable(methodInfo)) + return ReflectionForbidden(parameters.id, (string)parameters.method); + return ExecuteMethod(methodInfo, car, queryParams); + } + catch (Exception ex) + { + return JsonConvert.SerializeObject( + new { Success = false, Code = 500, Data = "null", Message = ex.Message }, Formatting.Indented); + } + } + + private dynamic MissionReflectionExecute(dynamic parameters) + { + try + { + if (!IsAuthorized()) return Unauthorized(); + var mission = SimpleProject.proj.Missions.FirstOrDefault(mm => mm.id == (int)parameters.id); + if (mission == null) + return JsonConvert.SerializeObject( + new { Success = false, Code = 500, Data = "null", Message = $"no mission of id {parameters.id}" }, Formatting.Indented); + var methodInfo = mission.GetType().GetMethod((string)parameters.method); + if (methodInfo == null) + return JsonConvert.SerializeObject( + new { Success = false, Code = 500, Data = "null", Message = $"mission {parameters.id} has no method {parameters.method}" }, Formatting.Indented); + var queryParams = ((DynamicDictionary)Request.Query).ToDictionary(); + queryParams.Remove("token"); + if (!IsReflectionInvokable(methodInfo)) + return ReflectionForbidden(parameters.id, (string)parameters.method); + return ExecuteMethod(methodInfo, mission, queryParams); + } + catch (Exception ex) + { + return JsonConvert.SerializeObject( + new { Success = false, Code = 500, Data = "null", Message = ex.Message }, Formatting.Indented); + } + } + + private string ExecuteMethod(MethodInfo methodInfo, object instance, Dictionary inputs) + { + var methodParam = methodInfo.GetParameters(); + List paramList = new List(); + if (methodParam.Length == 0) + { + methodInfo.Invoke(instance, null); + } + else + { + if (inputs.Count != methodParam.Length) + { + return JsonConvert.SerializeObject( + new + { + Success = false, + Code = 500, + Data = "null", + Message = $"method need {methodParam.Length} params , but {inputs.Count}" + }, Formatting.Indented); + } + for (int i = 0; i < methodParam.Length; i++) + { + paramList.Add(Convert.ChangeType(inputs[methodParam[i].Name], methodParam[i].ParameterType)); + } + var actualParam = paramList.ToArray(); + methodInfo.Invoke(instance, actualParam); + } + + return JsonConvert.SerializeObject( + new { Success = true, Code = 200, Data = "null", Message = "Success" }, Formatting.Indented); + } + + + public ApiController() + { + After.AddItemToEndOfPipeline(ctx => + { + ctx.Response.WithHeader("Access-Control-Allow-Origin", "*") + .WithHeader("Access-Control-Allow-Methods", "POST,GET") + .WithHeader("Access-Control-Allow-Headers", "Accept, Origin, Content-Type"); + }); + + // 鍙嶅皠鎺ュ彛鍏辫瘑锛 + // 鎵鏈塻imple鐨勮皟搴﹀璞★紙鍖呮嫭杞︺佽繘绋嬶級鍙湁id鏄叏灞鍞竴鐨勩 + // 鎵鏈塻imple鐨勮皟搴﹀璞″彲鑳芥湁涓嶅悓鐨刢lass锛宑lass鐩稿悓鐨勫彲鑳芥湁涓嶅悓鐨刵ame锛坣ame骞堕潪鍏ㄥ眬鍞竴锛夈 + // 鎵鏈塻imple鐨勮皟搴﹀璞¢兘鏈夊睘鎬э紙field锛夈佺姸鎬侊紙status锛夈佸姩浣滐紙methodMember锛夈 + // 灞炴ф槸鍙鍙啓鐨勫瓧鍏革紙鐢ㄦ埛璁惧畾灞炴э紝骞朵繚瀛樺睘鎬т綔涓鸿繍琛岄厤缃級銆傜姸鎬佹槸鍙鐨勫瓧鍏搞傚姩浣滄槸鍑芥暟鎴愬憳銆 + // 鍙嶅皠鎺ュ彛鍏ㄩ儴杩斿洖json瀛楃涓诧紝鍖呮嫭success鍜宺esult涓や釜瀛楁銆 + // success涓簍rue鎴栬協alse锛屽垎鍒〃绀鸿姹傛垚鍔熸垨澶辫触銆 + // result涓鸿繑鍥炲唴瀹广俿uccess涓篺alse鏃讹紝result鏄姤閿欏唴瀹广 + + // 杩斿洖className + Get("/car_reflection/get_type/{id}", parameters => + { + try + { + var car = SimpleLib.GetCar((int)parameters.id); + if (car == null) + return JsonConvert.SerializeObject( + new + { + Success = false, + Code = 500, + Data = "null", + Message = $"no car of id {parameters.id}" + }, Formatting.Indented); + return JsonConvert.SerializeObject( + new { Success = true, Code = 200, Data = car.GetType().Name, Message = "Success" }, + Formatting.Indented); + } + catch (Exception ex) + { + return JsonConvert.SerializeObject( + new { success = false, Code = 500, Data = "null", Message = ex.Message }, Formatting.Indented); + } + }); + Get("/car_reflection/get_methods/{id}", parameters => + { + try + { + var car = SimpleLib.GetCar((int)parameters.id); + if (car == null) + return JsonConvert.SerializeObject( + new + { + Success = false, + Code = 500, + Data = "null", + Message = $"no car of id {parameters.id}" + }, Formatting.Indented); + + var classMethodsDictionary = GetTypeMethods(car.GetType()); + if (classMethodsDictionary == null) + { + return JsonConvert.SerializeObject(new + { + Success = false, + Code = 500, + Data = "null", + Message = $"鏈尮閰嶅埌id锛歔{parameters.id}] 鐨勮溅杈" + }, + Formatting.Indented); + } + return JsonConvert.SerializeObject(new + { + Success = true, + Code = 200, + Data = classMethodsDictionary.Values.FirstOrDefault(), + Message = "Success" + }, + Formatting.Indented); + + } + catch (Exception e) + { + return JsonConvert.SerializeObject(new + { + Success = false, + Code = 500, + Data = "null", + Message = e.Message + }, + Formatting.Indented); + } + }); + + // 杩斿洖姣忕className瀵瑰簲鏈夊摢浜涘姩浣 + Get("/car_reflection/get_type_methods", _ => GetMethods(typeof(GhostCar))); + + // 鎵ц鍔ㄤ綔锛堟敮鎸佷换鎰忔暟閲忓弬鏁帮級锛涙墽琛岀被鎿嶄綔鍚屾椂鎻愪緵 POST 璇箟鍖栧叆鍙o紝淇濈暀 GET 鍏煎鏃у墠绔 + Get("/car_reflection/execute/{id}/{method}", parameters => CarReflectionExecute(parameters)); + Post("/car_reflection/execute/{id}/{method}", parameters => CarReflectionExecute(parameters)); + + // 杩斿洖(id, name, className)鐨勫垪琛 + Get("/mission_reflection/get_mission_list", _ => + JsonConvert.SerializeObject(new + { + Success = true, + Code = 200, + Data = SimpleProject.proj.Missions.Select(mm => + { + var mType = (MissionType)mm.GetType().GetCustomAttribute(typeof(MissionType)); + return new { id = mm.id, name = mm.name, typeName = mType.Name, state = mm.status.status }; + }), + Message = "Success" + }, Formatting.Indented)); + + Get("/mission_reflection/get_type_methods", _ => GetMethods(typeof(Mission))); + + Get("/mission_reflection/execute/{id}/{method}", parameters => MissionReflectionExecute(parameters)); + Post("/mission_reflection/execute/{id}/{method}", parameters => MissionReflectionExecute(parameters)); + + Get("/mission_reflection/get_status/{id}", parameters => + { + try + { + var mission = SimpleProject.proj.Missions.FirstOrDefault(mm => mm.id == (int)parameters.id); + if (mission == null) + return JsonConvert.SerializeObject( + new + { + Success = false, + Code = 500, + Data = "null", + Message = $"no mission of id {parameters.id}" + }, Formatting.Indented); + + return JsonConvert.SerializeObject( + new { success = true, Code = 200, Data = mission.status, Message = "Success" }, + Formatting.Indented); + } + catch (Exception ex) + { + return JsonConvert.SerializeObject( + new { Success = false, Code = 500, Data = "null", result = ex.Message }, Formatting.Indented); + } + }); + + Get("/mission_reflection/get_fields/{id}", parameters => + { + try + { + var mission = SimpleProject.proj.Missions.FirstOrDefault(mm => mm.id == (int)parameters.id); + if (mission == null) + return JsonConvert.SerializeObject( + new + { + Success = false, + Code = 500, + Data = "null", + Message = $"no mission of id {parameters.id}" + }, Formatting.Indented); + + return JsonConvert.SerializeObject( + new + { + Success = true, + Code = 200, + Data = mission.fields.Select(x => { return new { Key = x.Key, Vaule = x.Value }; }) + .ToList(), + Message = "Success" + }, + Formatting.Indented); + } + catch (Exception ex) + { + return JsonConvert.SerializeObject( + new { Success = false, Code = 500, Data = "null", result = ex.Message }, Formatting.Indented); + } + }); + + Get("/mission_reflection/set_field/{id}/{field}/{value}", parameters => + { + try + { + var mission = SimpleProject.proj.Missions.FirstOrDefault(mm => mm.id == (int)parameters.id); + if (mission == null) + return JsonConvert.SerializeObject( + new { success = false, Data = "null", Message = $"no mission of id {parameters.id}" }, + Formatting.Indented); + + var fieldName = (string)parameters.field; + var fieldValue = (string)parameters.value; + mission.fields[fieldName] = fieldValue; + + return JsonConvert.SerializeObject( + new { Success = true, Code = 200, Data = "null", Message = "Success" }, Formatting.Indented); + } + catch (Exception ex) + { + return JsonConvert.SerializeObject( + new { Success = false, Code = 500, Data = "null", Message = ex.Message }, Formatting.Indented); + } + }); + + Get("/mission_reflection/delete_field/{id}/{field}", parameters => + { + try + { + var mission = SimpleProject.proj.Missions.FirstOrDefault(mm => mm.id == (int)parameters.id); + if (mission == null) + return JsonConvert.SerializeObject(new + { + Success = false, + Code = 500, + Data = "null", + Message = $"no mission of id {parameters.id}" + }, Formatting.Indented); + var fieldName = (string)parameters.field; + if (!mission.fields.ContainsKey(fieldName)) + { + return JsonConvert.SerializeObject(new + { + Success = false, + Code = 500, + Data = "null", + Message = $"mission not found field {parameters.field}" + }, Formatting.Indented); + } + mission.fields.Remove(fieldName); + return JsonConvert.SerializeObject(new + { + Success = true, + Code = 200, + Data = "null", + Message = "Success" + }, Formatting.Indented); + } + catch (Exception ex) + { + return JsonConvert.SerializeObject(new + { + Success = false, + Code = 500, + Data = "null", + Message = ex.Message + }, Formatting.Indented); + } + }); + Get("/mission_reflection/get_mission/{id}", parameters => + { + try + { + var classMethodsDictionary = new Dictionary>(); + var mission = SimpleProject.proj.Missions.FirstOrDefault(m => m.id == (int)parameters.id); + if (mission == null) + { + return JsonConvert.SerializeObject( + new + { + Success = false, + Code = 500, + Data = "null", + Message = $"no mission of id {parameters.id}" + }, + Formatting.Indented); + } + + var method = mission.GetType() + .GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance) + .Where(m => m.GetCustomAttribute() != null).Select(m => + { + var attr = m.GetCustomAttribute(); + var param = m.GetParameters(); + var ret = new MethodInfoDetails() { MethodName = m.Name }; + var name = attr.Name; + if (name != null) ret.ButtonName = name; + var desc = attr.Description; + if (desc != null) ret.ButtonDescription = desc; + return ret; + }).ToList(); + classMethodsDictionary[mission.name] = method; + return JsonConvert.SerializeObject( + new { Success = true, Code = 200, Data = classMethodsDictionary.Values.FirstOrDefault(), Message = "Success" }); + + } + catch (Exception e) + { + return JsonConvert.SerializeObject(new + { Success = false, Code = 500, Data = "null", Message = e.Message }); + } + }); + + // 闇璁ㄨ濡備綍瀹炵幇 + // Get["/save_parameters"] = _ => { }; + + #region 鍒涘缓浠诲姟[Fass鍙戝竷浠诲姟] + Post("/car/createTask", _ => + { + Response response; + var responseResult = new BaseRespose + { + Success = true, + Code = (int)HttpStatusCode.OK, + Message = "success" + }; + try + { + var jReq = new System.IO.StreamReader(Request.Body).ReadToEnd(); + var req = jReq.JsonTo(); + switch (req.MissionType) + { + case "Transport": + CreateTransportTask: + ( + responseResult.Success, + responseResult.Code, + responseResult.Message, + var _ + ) = CreateTransportTask(req, true); + break; + default: + goto CreateTransportTask; + } + response = Response.AsText( + responseResult.ToJson(), + "application/json;charset=UTF-8" + ); + return response; + } + catch (Exception ex) + { + Diagnosis.Log( + $"璧峰绔欑偣鍖归厤閿欒锛岃妫鏌" + + ExceptionFormatter.FormatEx(ex) + + $"--time:{DateTime.Now.ToString()}", + "error", + true + ); + responseResult.Success = false; + responseResult.Code = (int)HttpStatusCode.InternalServerError; + responseResult.Message = ex.Message; + response = Response.AsText( + responseResult.ToJson(), + "application/json;charset=UTF-8" + ); + return response; + } + }); + #endregion + + #region 浠诲姟涓嬪彂[Fass鎸佺画涓嬪彂] + Post("/car/carTask", _ => + { + Response response; + var responseResult = new BaseRespose + { + Success = true, + Code = (int)HttpStatusCode.OK, + Message = "success" + }; + try + { + Stopwatch sw = new Stopwatch(); + sw.Start(); + string jReq = new System.IO.StreamReader(Request.Body).ReadToEnd(); + TaskRequest req = jReq.JsonTo(); + switch (req.MissionType) + { + case "Transport": + CheckTransportTask: + ( + responseResult.Success, + responseResult.Code, + responseResult.Message, + responseResult.Data + ) = CreateTransportTask(req, false); + break; + default: + goto CheckTransportTask; + } + response = Response.AsText( + responseResult.ToJson(), + "application/json;charset=UTF-8" + ); + //Diagnosis.Log( + // $"carTask_Done:::consumeTime:{sw.ElapsedMilliseconds}", + // "Interface", + // true + //); + return response; + } + catch (Exception ex) + { + Diagnosis.Log( + $"璧峰绔欑偣鍖归厤閿欒锛岃妫鏌" + + ExceptionFormatter.FormatEx(ex) + + $"--time:{DateTime.Now.ToString()}", + "error", + true + ); + responseResult.Success = false; + responseResult.Code = (int)HttpStatusCode.InternalServerError; + responseResult.Message = ex.Message; + responseResult.Data = new CarStateInfo(); + response = Response.AsText( + responseResult.ToJson(), + "application/json;charset=UTF-8" + ); + return response; + } + }); + + #endregion + + #region 鐘舵佹煡璇 + Post("/car/carState", _ => + { + Response response; + try + { + Stopwatch sw = new Stopwatch(); + sw.Start(); + var jReq = new System.IO.StreamReader(Request.Body).ReadToEnd(); + var req = jReq.JsonTo(); + if (req.CarCode != "0") + { + var responseResult = new BaseRespose + { + Success = true, + Code = (int)HttpStatusCode.OK, + Message = "success" + }; + var singleCar = SimpleLib + .GetAllCars() + .OfType() + .FirstOrDefault(e => + e.id.ToString() == req.CarCode && e.GetLastSite() != -1 + ); + if (singleCar != null) + { + responseResult.Data = GetCarStateInfo(req.CarCode); + } + else + { + responseResult.Success = false; + responseResult.Code = (int)HttpStatusCode.InternalServerError; + responseResult.Message = $"杞﹁締carCode:{req.CarCode}涓嶅瓨鍦ㄦ垨鏈垵濮嬪寲"; + responseResult.Data = new CarStateInfo(); + } + response = Response.AsText( + responseResult.ToJson(), + "application/json;charset=UTF-8" + ); + //Diagnosis.Log( + // $"carState_Done:::consumeTime:{sw.ElapsedMilliseconds}", + // "Interface", + // true + //); + return response; + } + else + { + var responseResult = new BaseRespose> + { + Success = true, + Code = (int)HttpStatusCode.OK, + Message = "success" + }; + var carStateList = new List(); + var cars = SimpleLib.GetAllCars().OfType().ToList(); + if (cars.Count > 0) + { + foreach (var car in cars) + { + var carStateInfo = GetCarStateInfo(car.name); + carStateList.Add(carStateInfo); + } + responseResult.Data = carStateList; + } + else + { + responseResult.Success = false; + responseResult.Code = (int)HttpStatusCode.InternalServerError; + responseResult.Message = "褰撳墠绯荤粺涓棤鍚敤璋冨害杞﹁締"; + responseResult.Data = carStateList; + } + response = Response.AsText( + responseResult.ToJson(), + "application/json;charset=UTF-8" + ); + //Diagnosis.Log( + // $"carState_Done:::consumeTime:{sw.ElapsedMilliseconds}", + // "Interface", + // true + //); + return response; + } + } + catch (Exception ex) + { + Console.WriteLine($@"agv/getCar => ex: {ex.Message}"); + var responseResult = new BaseRespose + { + Success = true, + Code = (int)HttpStatusCode.OK, + Message = "success" + }; + responseResult.Success = false; + responseResult.Code = (int)HttpStatusCode.InternalServerError; + responseResult.Message = ex.Message; + responseResult.Data = new CarStateInfo(); + response = Response.AsText( + responseResult.ToJson(), + "application/json;charset=UTF-8" + ); + return response; + } + }); + #endregion + + #region 鍒濆鍖栧皬杞 + Get("/car/reset/{cartId}", _ => + { + var resp = new BaseRespose(); + try + { + int cartId = _.cartId; + Car car = (Car)SimpleLib.GetCar(cartId); + //濡傛灉鏈変换鍔′笉鍏佽鍒濆鍖 + if (!car.tags.Contains("occupied")) + { + car.Reset(); + car.tags.Add("idle", DateTime.Now.ToString()); + } + } + catch (Exception e) + { + resp.Success = false; + resp.Code = 500; + resp.Message = e.Message; + } + var response = Response.AsText(resp.ToJson(), "application/json;charset=UTF-8"); + return response; + }); + #endregion + + #region 鐜板満缁翠慨 + Get("/car/repair/{cartId}", _ => + { + var resp = new BaseRespose(); + try + { + int cartId = _.cartId; + Car car = (Car)SimpleLib.GetCar(cartId); + car.Repair(); + } + catch (Exception e) + { + resp.Success = false; + resp.Code = 500; + resp.Message = e.Message; + } + var response = Response.AsText(resp.ToJson(), "application/json;charset=UTF-8"); + return response; + }); + #endregion + + #region 杩斿巶缁翠慨 + Get("/car/blown/{cartId}", _ => + { + var resp = new BaseRespose(); + try + { + int cartId = _.cartId; + Car car = (Car)SimpleLib.GetCar(cartId); + var cdm = SimpleProject.proj.Missions.OfType().First(); + var deliver = cdm.GetDeliveries() + .Find(p => ((TransportDelivery)p).UsingCar.id == car.id); + if (deliver != null) + { + resp.Success = false; + resp.Code = 500; + resp.Message = "褰撳墠灏忚溅鏈変换鍔¢渶绛変换鍔$粨鏉熷悗杩斿巶"; + } + else + { + car.AppendDebug("ui-blown"); + Diagnosis.Post($"Car {car.name}({car.id}) blown"); + car.NoSchedule(); + car.siteID = -1; + car.tags.Clear(); + car.status.usage.AddUsage( + "base", + new CarUsage.CarUsageInfo { scheduling = false, refreshing = false } + ); + car.lstatus = "杩斿巶妫淇"; + } + } + catch (Exception e) + { + resp.Success = false; + resp.Code = 500; + resp.Message = e.Message; + } + var response = Response.AsText(resp.ToJson(), "application/json;charset=UTF-8"); + return response; + }); + #endregion + + #region 绔欑偣鍚敤 + Get("/site/enable/{siteId}", _ => + { + var resp = new BaseRespose(); + try + { + int siteId = _.siteId; + Site site = SimpleLib.GetSite(siteId); + if (site.tags.Contains("unavailable")) + site.tags.Remove("unavailable"); + } + catch (Exception e) + { + resp.Success = false; + resp.Code = 500; + resp.Message = e.Message; + } + var response = Response.AsText(resp.ToJson(), "application/json;charset=UTF-8"); + return response; + }); + #endregion + + #region 绔欑偣绂佺敤 + Get("/site/disable/{siteId}", _ => + { + var resp = new BaseRespose(); + try + { + int siteId = _.siteId; + Site site = SimpleLib.GetSite(siteId); + if (!site.tags.Contains("unavailable")) + site.tags.Add("unavailable"); + } + catch (Exception e) + { + resp.Success = false; + resp.Code = 500; + resp.Message = e.Message; + } + var response = Response.AsText(resp.ToJson(), "application/json;charset=UTF-8"); + return response; + }); + #endregion + + #region 鍙栨秷浠诲姟 + Post("/car/cancelTask", param => + { + Response response; + var responseResult = new BaseRespose + { + Success = true, + Code = (int)HttpStatusCode.OK, + Message = "success" + }; + try + { + var jReq = new System.IO.StreamReader(Request.Body).ReadToEnd(); + var req = jReq.JsonTo(); + switch (req.MissionType) + { + case "Transport": + TransportCancel: + var cdm = SimpleProject + .proj.Missions.OfType() + .First(); + var deliver = cdm.GetDeliveries() + .Find(p => ((TransportDelivery)p).TaskId == req.TaskCode); + if (deliver != null) + { + deliver.Canceled = true; + if (!deliver.Cancel()) + { + responseResult.Success = false; + responseResult.Code = 500; + responseResult.Message = "浠诲姟鍙栨秷澶辫触"; + return responseResult; + } + else + { + if (deliver.UsingCar != null) + { + var car = deliver.UsingCar; + new Thread(() => + { + try + { + car.AppendDebug("Clumsy Restarted"); + car.NoSchedule(true); + car.siteID = -1; + Commons.DeleteTag(car.tags, "occupied"); + if (car.address != null && car.address != "") + { + SharedHttpClient.GetAsync( + $"http://{car.address}:8008/reset" + ); + } + car.AppendDebug("restarting clumsy"); + //car.Get("reset"); + + car.AppendDebug( + "wait for any pending task to flush." + ); + + car.status.programs.task.Wait(); + //car.Reset(); + } + catch (Exception) + { + responseResult.Message = "閲嶅惎C澶辫触"; + } + }).Start(); + } + } + } + else + { + responseResult.Success = false; + responseResult.Code = 500; + responseResult.Message = $"CDM涓笉瀛樺湪浠诲姟id[{req.TaskCode}]"; + } + break; + default: + goto TransportCancel; + break; + } + response = Response.AsText(responseResult.ToJson(), "application/json"); + return response; + } + catch (Exception e) + { + responseResult.Code = (int)HttpStatusCode.InternalServerError; + responseResult.Message = $"浠诲姟鍙栨秷澶辫触锛 ex:{e.Message}"; + response = Response.AsText(responseResult.ToJson(), "application/json"); + return response; + } + }); + #endregion + + #region 鑾峰彇Simple鍦板浘 + Get("/map/getMap", _ => + { + var resp = new BaseRespose() + { + Success = true, + Code = (int)HttpStatusCode.OK, + Message = "success" + }; + + try + { + resp.Data = Map.GetMap(); + } + catch (Exception e) + { + resp.Success = false; + resp.Code = 500; + resp.Message = e.Message; + } + var response = Response.AsText(resp.ToJson(), "application/json;charset=UTF-8"); + return response; + }); + #endregion + + #region 鑾峰彇闆疯揪鍦板浘 + + Get("/map/getLidarMap", _ => + { + var resp = new BaseRespose() + { + Success = true, + Code = (int)HttpStatusCode.OK, + Message = "success" + }; + + try + { + resp.Data = LidarMap.GetLidarMap().Result; + } + catch (Exception e) + { + resp.Success = false; + resp.Code = 500; + resp.Message = e.Message; + } + var response = Response.AsText(resp.ToJson(), "application/json;charset=UTF-8"); + return response; + }); + + #endregion + + #region 鑾峰彇鎵鏈夊皬杞︿俊鎭 + Get("/car/getAllCars", _ => + { + var resp = new BaseRespose>(); + try + { + resp.Data = new List(); + foreach (var car in SimpleLib.GetAllCars()) + { + resp.Data.Add(CarBaseInfo.FromCar((Car)car)); + } + } + catch (Exception e) + { + Console.WriteLine(e.StackTrace); + resp.Code = 500; + resp.Message = e.Message; + } + var response = Response.AsText(resp.ToJson(), "application/json;charset=UTF-8"); + return response; + }); + #endregion + + #region 鍘绘煇鍦 + Get("/car/goSite", param => + { + var carCode = Request.Query["carCode"]; + var id = Request.Query["id"]; + var responseResult = new BaseRespose + { + Success = true, + Code = (int)HttpStatusCode.OK, + Message = "success" + }; + try + { + if (carCode != 0 && id != 0) + { + Car car = (Car)SimpleLib.GetCar(carCode); + Site site = SimpleLib.GetSite(id); + Commons.GoSite(car, site, 1); + } + var response = Response.AsText(responseResult.ToJson(), "application/json"); + return response; + } + catch (Exception) + { + return Response.AsText("璋冪敤灏忚溅鍘绘煇鍦板け璐".ToJson(), "application/json"); + } + }); + #endregion + + #region 鑾峰彇浠诲姟鍒楄〃 + Get("/task/getTask", _ => + { + Response response; + var resp = new BaseRespose>() + { + Success = true, + Code = (int)HttpStatusCode.OK, + Message = "success" + }; + + try + { + var missions = SimpleProject + .proj.Missions.OfType() + .ToList(); + List deliveryList = new List(); + if (missions.Count == 0) + return Response.AsText(resp.ToJson(), "application/json;charset=UTF-8"); + foreach (var cdm in missions) + { + var finishTaskList = cdm.GetDeliveries(true, true) + .Where(e => e.FinishTime > DateTime.Now.AddMinutes(-30)) + .ToList(); //30min鍐呭凡瀹屾垚瀹屾垚浠诲姟 + var executingTaskList = cdm.GetDeliveries(); //鏈畬鎴愪换鍔 + foreach (var delivery in finishTaskList) + { + var temp = (TransportDelivery)delivery; + deliveryList.Add( + new TaskRecord + { + TaskId = temp.TaskId, + Name = $"{temp.Src} =>{temp.Dst}", + CarId = temp.UsingCar.id.ToString(), + CarName = temp.UsingCar.name, + State = GetDeliveryStatus(temp), + SrcSiteId = temp.Src.ToString(), + DestSiteId = temp.Dst.ToString(), + Priority = temp.Priority, + StartTime = temp.StartTime, + EndTime = temp.FinishTime, + Created = temp.CreateTime + } + ); + } + + foreach (var delivery in executingTaskList) + { + var temp = (TransportDelivery)delivery; + deliveryList.Add( + new TaskRecord + { + TaskId = temp.TaskId, + Name = $"{temp.Src} =>{temp.Dst}", + CarId = temp.UsingCar.id.ToString(), + CarName = temp.UsingCar.name, + State = GetDeliveryStatus(temp), + SrcSiteId = temp.Src.ToString(), + DestSiteId = temp.Dst.ToString(), + Priority = temp.Priority, + StartTime = temp.StartTime, + EndTime = temp.FinishTime, + Created = temp.CreateTime + } + ); + } + + resp.Data = deliveryList; + } + } + catch (Exception e) + { + resp.Success = false; + resp.Code = 500; + resp.Message = e.Message; + } + response = Response.AsText(resp.ToJson(), "application/json;charset=UTF-8"); + return response; + }); + #endregion + + #region 寮哄埗缁撴潫 + Get("/car/ForceStop/{cartId}", _ => + { + #region + /* try + { + int cartId = _.cartId; + var car = (Car)SimpleLib.GetCar(cartId); + + try + { + if (car == null) + { + resp.Success = false; + resp.Code = 500; + resp.Message = "灏忚溅鍦ㄧ郴缁熶腑娌℃壘鍒"; + } + else + { + car.AppendDebug("ui-repair"); + car.tags.Clear(); + car.NoSchedule(makeUnavailable: false); + car.siteID = -1; + car.lstatus = "缁撴潫浠诲姟"; + var cdm = SimpleProject + .proj.Missions.OfType() + .First(); + var deliver = cdm.GetDeliveries() + .Find(p => ((TransportDelivery)p).usingCar.id == car.id); + if (deliver != null) + { + deliver.canceled = true; + if (!deliver.Cancel()) + { + deliver.usingCar.UIIntercept(); + resp.Success = false; + resp.Code = 500; + resp.Message = "浠诲姟鍙栨秷澶辫触"; + } + } + new Thread(() => + { + try + { + //car.status.programs.task.Wait(); + HttpClient client = new HttpClient(); + client.GetAsync($"http://{car.address}:8008/reset"); + } + catch + { + resp.Success = false; + resp.Code = 500; + resp.Message = "閲嶅惎C澶辫触"; + } + }).Start(); + + resp.Success = true; + resp.Code = 200; + resp.Message = "寮哄埗缁撴潫浠诲姟鎴愬姛"; + } + } + catch (Exception e) + { + resp.Success = false; + resp.Code = 500; + resp.Message = e.Message; + } + } + catch (Exception e) + { + resp.Success = false; + resp.Code = 500; + resp.Message = e.Message; + }*/ + #endregion + var resp = new BaseRespose() { Message = "浠诲姟鍙栨秷鎴愬姛" }; + try + { + int cartId = _.cartId; + var car = (Car)SimpleLib.GetCar(cartId); + car.NoSchedule(true); + car.siteID = -1; + + Commons.ClearTags(car.tags); + car.tags.Clear(); + var cdm = SimpleProject.proj.Missions.OfType().First(); + var deliver = cdm.GetDeliveries() + .Find(p => ((TransportDelivery)p).UsingCar.id == car.id); + + int count = 0; + + if (deliver != null) + { + deliver.Canceled = true; + if (!deliver.Cancel()) + { + resp.Success = false; + resp.Code = 400; + resp.Message = "浠诲姟鍙栨秷澶辫触"; + var res = Response.AsText( + resp.ToJson(), + "application/json;charset=UTF-8" + ); + return res; + } + } + + new Thread(() => + { + try + { + SharedHttpClient.GetAsync($"http://{car.address}:8008/reset"); + car.AppendDebug("restarting clumsy"); + //car.Get("reset"); + + car.AppendDebug("wait for any pending task to flush."); + + car.status.programs.task.Wait(); + car.AppendDebug("Clumsy Restarted"); + car.NoSchedule(); + car.siteID = -1; + Commons.DeleteTag(car.tags, "occupied"); + // car.Reset(); + } + catch (Exception) + { + resp.Message = "閲嶅惎C澶辫触"; + } + }) + { + Name = $"ForceStop:{car.name}({car.id})" + }.Start(); + } + catch (Exception e) + { + resp.Success = false; + resp.Code = 500; + resp.Message = e.Message; + } + var response = Response.AsText(resp.ToJson(), "application/json;charset=UTF-8"); + return response; + }); + #endregion + + #region 閲嶅惎鐢佃剳 + Get("/car/ReStart/{cartId}", _ => + { + var resp = new BaseRespose(); + int cartId = _.cartId; + var car = (Car)SimpleLib.GetCar(cartId); + var task = Task.Run(() => + { + try + { + resp.Success = false; + resp.Code = 200; + resp.Message = "閲嶅惎涓"; + + // 鍒涘缓涓涓狿rocess瀵硅薄 + Process process = new Process(); + + // 璁剧疆鍚姩淇℃伅 + process.StartInfo.FileName = "shutdown"; + process.StartInfo.Arguments = "/r /t 0"; // 閲嶅惎鐢佃剳锛岀珛鍗虫墽琛 + process.StartInfo.CreateNoWindow = true; // 涓嶆樉绀虹獥鍙 + process.StartInfo.UseShellExecute = false; // 涓嶄娇鐢ㄦ搷浣滅郴缁熷澹崇▼搴忓惎鍔ㄨ繘绋 + + // 鍚姩杩涚▼ + process.Start(); + } + catch (Exception e) + { + resp.Success = false; + resp.Code = 400; + resp.Message = e.Message; + } + }); + task.Wait(); + var response = Response.AsText(resp.ToJson(), "application/json;charset=UTF-8"); + return response; + }); + #endregion + + #region 鍋滄帴浠诲姟 + Get("/car/stopReceiveTask/{cartId}", _ => + { + var resp = new BaseRespose(); + int cartId = _.cartId; + try + { + var car = (Car)SimpleLib.GetCar(cartId); + if (car != null) + { + if (!car.fields.ContainsKey("StopAccept")) + { + Commons.AddOrUpdateCarField(car, "StopAccept", "1"); + } + else + { + Commons.DeleteCarField(car, "StopAccept"); + } + } + else + { + resp.Success = false; + resp.Code = 500; + resp.Message = "鎵句笉鍒板搴斿皬杞"; + } + } + catch (Exception e) + { + resp.Success = false; + resp.Code = 500; + resp.Message = e.Message; + } + + var response = Response.AsText(resp.ToJson(), "application/json;charset=UTF-8"); + return response; + }); + #endregion + + #region 鑾峰彇灏忚溅淇℃伅 + Get("/car/carInfo/{cartId}", _ => + { + var resp = new BaseRespose(); + int cartId = _.cartId; + try + { + var car = (Car)SimpleLib.GetCar(cartId); + if (car != null) + { + resp.Data = new CarInfo() + { + Name = car.name, + Address = car.address, + Speed = car.status.enums.TryGetValue( + "ActualLeftWheelVelocity", + out var statusEnum + ) + ? statusEnum + : car.speed.ToString(), + Voltage = car.status.enums.TryGetValue("soc", out var soc) + ? float.Parse(soc) + : -1, + Code = car.id.ToString(), + siteId = car.GetLastSite() + }; + } + } + catch (Exception e) + { + resp.Success = false; + resp.Code = 500; + resp.Message = e.Message; + } + + var response = Response.AsText(resp.ToJson(), "application/json;charset=UTF-8"); + return response; + }); + #endregion + + #region 鍘诲緟鍛界偣 + Get("/car/goToStandby", param => + { + var CarCode = Request.Query["carCode"]; + //var id = Request.Query["id"]; + Response response; + var responseResult = new BaseRespose + { + Success = true, + Code = (int)HttpStatusCode.OK, + Message = "success" + }; + try + { + var car = (Car)SimpleLib.GetCar(CarCode); + if (car != null) + { + var carGroup = car.fields.TryGetValue("group", out var value) + ? value + : "all"; + var targetPlan = Commons.GetNearestPlan( + (Car)car, + site => + site.fields.ContainsKey("standby") + && carGroup.Equals( + site.fields.TryGetValue("group", out var value) ? value : "all" + ) + && site.fields["standby"] == "true" + ); + if (targetPlan != null) + { + Commons.GoSite(car, targetPlan.Destination, 1); + } + } + else + { + responseResult.Success = false; + responseResult.Code = 500; + responseResult.Message = "娌℃湁鎵惧埌浠诲姟灏忚溅"; + } + response = Response.AsText(responseResult.ToJson(), "application/json"); + return response; + } + catch (Exception) + { + return Response.AsText("璋冪敤灏忚溅鍘诲緟鍛界偣澶辫触".ToJson(), "application/json"); + } + }); + #endregion + + #region 鍘诲厖鐢电偣 + Get("/car/goToCharge", param => + { + var CarCode = Request.Query["carCode"]; + //var id = Request.Query["id"]; + Response response; + var responseResult = new BaseRespose + { + Success = true, + Code = (int)HttpStatusCode.OK, + Message = "success" + }; + try + { + var car = (Car)SimpleLib.GetCar(CarCode); + if (car != null) + { + /* var carGroup = car.fields.TryGetValue("group", out var value) ? value : "all"; + var targetPlan = Commons.GetNearestPlan((Car)car, site => + site.fields.ContainsKey("charge") + && site.fields["charge"] == "true" && carGroup.Equals(site.fields.TryGetValue("group", out var value) ? value : "all") + ); + if (targetPlan != null) + { + Diagnosis.Post($"灏忚溅璺緞瑙勫垝鎴愬姛鐩爣鍏呯數浣峽targetPlan.Destination}", "鍘诲厖鐢",true); + Commons.GoSite(car, targetPlan.Destination, 1); + } + else + { + + Diagnosis.Post("灏忚溅璺緞瑙勫垝澶辫触", "鍘诲厖鐢", true); + + }*/ + + Commons.AddOrUpdateTag(car.tags, "shouldCharge", "true"); + } + else + { + responseResult.Success = false; + responseResult.Code = 500; + responseResult.Message = "娌℃湁鎵惧埌浠诲姟灏忚溅"; + } + response = Response.AsText(responseResult.ToJson(), "application/json"); + return response; + } + catch (Exception) + { + return Response.AsText("璋冪敤灏忚溅鍘诲厖鐢电數鐐瑰け璐".ToJson(), "application/json"); + } + }); + #endregion + + #region 鏆傚仠鎭㈠ + Get("/car/startOrPause", param => + { + var carCode = Request.Query["CarCode"]; + var responseResult = new BaseRespose + { + Success = true, + Code = (int)HttpStatusCode.OK, + Message = "success" + }; + + try + { + if (carCode == 0) + { + responseResult.Success = false; + responseResult.Code = (int)HttpStatusCode.BadRequest; + responseResult.Message = "娌℃湁鎵惧埌浠诲姟灏忚溅"; + return Response.AsJson(responseResult); + } + + ClumsyCar car = SimpleLib.GetCar(carCode) as ClumsyCar; + if (car == null) + { + responseResult.Success = false; + responseResult.Code = (int)HttpStatusCode.NotFound; + responseResult.Message = "鏈壘鍒板搴旂殑灏忚溅"; + return Response.AsJson(responseResult); + } + + string releaseStatus = Commons.GetCarStatus(car, "ISRelease"); + + try + { + ToggleCarReleaseStatus(car, releaseStatus); + } + catch (Exception ex) + { + responseResult.Success = false; + responseResult.Code = (int)HttpStatusCode.InternalServerError; + responseResult.Message = $"鏆傚仠鎭㈠澶辫触 => ex:{ExceptionFormatter.FormatEx(ex)}"; + Diagnosis.Log($"鏆傚仠鎭㈠澶辫触 => ex:{ExceptionFormatter.FormatEx(ex)}"); + } + + return Response.AsJson(responseResult); + } + catch (Exception ex) + { + responseResult.Success = false; + responseResult.Code = (int)HttpStatusCode.InternalServerError; + responseResult.Message = $"鏆傚仠鎭㈠澶辫触 => ex:{ExceptionFormatter.FormatEx(ex)}"; + return Response.AsJson(responseResult); + } + }); + #endregion + + #region 鏆傚仠浠诲姟/鎭㈠浠诲姟 + Post("/car/stopOrPauseTask", param => + { + Response response; + var responseResult = new BaseRespose + { + Success = true, + Code = (int)HttpStatusCode.OK, + Message = "success" + }; + try + { + var jReq = new System.IO.StreamReader(Request.Body).ReadToEnd(); + var result = jReq.JsonTo(); + var taskCode = result.TaskCode; + var cdms = SimpleProject + .proj.Missions.OfType() + .ToList(); + + if (cdms.Count == 0) + { + responseResult.Success = false; + responseResult.Code = 500; + responseResult.Message = "鏈壘鍒癈DM杩涚▼"; + return Response.AsText(responseResult.ToJson(), "application/json"); + } + + Delivery deliver = null; + foreach (var cdm in cdms) + { + deliver = cdm.GetDeliveries(includeAborted: true) + .FirstOrDefault(p => p.TaskId == taskCode); + if (deliver != null) + break; + } + + if (deliver == null) + { + responseResult.Success = false; + responseResult.Code = 500; + responseResult.Message = $"CDM涓笉瀛樺湪浠诲姟id[{taskCode}]"; + return Response.AsText(responseResult.ToJson(), "application/json"); + } + + deliver.Terminated = result.TaskStatus == "1"; + var status = 0; + + if (deliver.UsingCar != null) + { + status = (deliver.OnTerminated?.Invoke(deliver, result.TaskStatus)).Result; + } + + if (status != 1) + { + responseResult.Success = false; + responseResult.Code = 500; + responseResult.Message = deliver.Terminated ? "鏆傚仠浠诲姟鍋滄灏忚溅澶辫触" : "鎭㈠浠诲姟澶辫触"; + } + + response = Response.AsText(responseResult.ToJson(), "application/json"); + return response; + } + catch (JsonSerializationException jsonEx) + { + responseResult.Code = (int)HttpStatusCode.BadRequest; + responseResult.Message = $"JSON瑙f瀽澶辫触锛 ex:{jsonEx.Message}"; + response = Response.AsText(responseResult.ToJson(), "application/json"); + return response; + } + catch (Exception e) + { + responseResult.Code = (int)HttpStatusCode.InternalServerError; + responseResult.Message = $"浠诲姟鍙栨秷澶辫触锛 ex:{e.Message}"; + response = Response.AsText(responseResult.ToJson(), "application/json"); + return response; + } + }); + + #endregion + + #region 璁剧疆鍏呯數鍙傛暟 + Post("/car/setChargingSettings", param => + { + Response response; + var responseResult = new BaseRespose + { + Success = true, + Code = (int)HttpStatusCode.OK, + Message = "success" + }; + try + { + var jReq = new System.IO.StreamReader(Request.Body).ReadToEnd(); + var chargingSetting = jReq.JsonTo(); + + // 杩欓噷鍙互鏍规嵁 ChargingSetting 鐨勫睘鎬ц繘琛屽叿浣撶殑涓氬姟閫昏緫澶勭悊 + // 渚嬪锛 + if (chargingSetting.CarMinBattery < 0 || chargingSetting.CarMaxBattery > 100) + { + responseResult.Success = false; + responseResult.Code = 400; + responseResult.Message = "鍏呯數鐢甸噺鑼冨洿涓嶅悎娉"; + } + else + { + // 鍋囪鎴戜滑鏈変竴涓柟娉曟潵搴旂敤杩欎簺鍏呯數璁剧疆 + ApplyChargingSettings(chargingSetting); + } + + response = Response.AsText(responseResult.ToJson(), "application/json"); + return response; + } + catch (Exception e) + { + responseResult.Code = (int)HttpStatusCode.InternalServerError; + responseResult.Message = $"璁剧疆鍏呯數鍙傛暟澶辫触锛 ex:{e.Message}"; + response = Response.AsText(responseResult.ToJson(), "application/json"); + return response; + } + }); + + #endregion + #region 璁剧疆鍖呯粶鍙傛暟 + Post("/car/setEnvelopeSetting", param => + { + Response response; + var responseResult = new BaseRespose + { + Success = true, + Code = (int)HttpStatusCode.OK, + Message = "success" + }; + try + { + var jreq = new System.IO.StreamReader(Request.Body).ReadToEnd(); + var envelopeSetting = jreq.JsonTo(); + // 璇诲彇鏈湴 JSON 鏂囦欢 + var simpleFilePath = + $"{System.AppDomain.CurrentDomain.BaseDirectory}/simple.json"; + var simpleJson = File.ReadAllText(simpleFilePath); + var simpleConfig = simpleJson.JsonTo(); + var jsonFilePath = ""; + if (simpleConfig != null && simpleConfig.Autoload != null) + { + jsonFilePath = + $"{System.AppDomain.CurrentDomain.BaseDirectory}/{simpleConfig.Autoload}"; + } + else + { + responseResult.Code = (int)HttpStatusCode.BadRequest; + responseResult.Message = "淇敼鏂囦欢璺緞涓嶅"; + response = Response.AsText(responseResult.ToJson(), "application/json"); + return response; + } + //var jsonFilePath = $"{System.AppDomain.CurrentDomain.BaseDirectory}/0123.json"; + var jsonData = File.ReadAllText(jsonFilePath); + var jsonObject = JObject.Parse(jsonData); + + // 鏇存柊鎴栨坊鍔犲睘鎬 + // 鑾峰彇鎴栧垱寤 "conf" 鑺傜偣 + var confNode = jsonObject["conf"]; + if (confNode == null || confNode.Type != JTokenType.Object) + { + confNode = new JObject(); + jsonObject["conf"] = confNode; + } + + // 鏇存柊 JSON 鏁版嵁 + confNode[envelopeSetting.Name] = envelopeSetting.Value; + ; + + // 灏嗕慨鏀瑰悗鐨勬暟鎹啓鍥 JSON 鏂囦欢 + File.WriteAllText(jsonFilePath, jsonObject.ToString()); + + response = Response.AsText(responseResult.ToJson(), "application/json"); + return response; + } + catch (Exception e) + { + responseResult.Code = (int)HttpStatusCode.InternalServerError; + responseResult.Message = $"璁剧疆鍖呯粶鍙傛暟澶辫触锛 ex:{e.Message}"; + response = Response.AsText(responseResult.ToJson(), "application/json"); + return response; + } + }); + #endregion + + #region 璁剧疆浜ょ鍙傛暟 + Post("/car/setTrafficControlSetting", param => + { + var responseResult = new BaseRespose + { + Success = true, + Code = (int)HttpStatusCode.OK, + Message = "success" + }; + try + { + var jreq = new System.IO.StreamReader(Request.Body).ReadToEnd(); + var trafficControlSetting = jreq.JsonTo(); + // 璇诲彇鏈湴 JSON 鏂囦欢 + var simpleFilePath = + $"{System.AppDomain.CurrentDomain.BaseDirectory}/simple.json"; + var simpleJson = File.ReadAllText(simpleFilePath); + var simpleConfig = simpleJson.JsonTo(); + var jsonFilePath = ""; + if (simpleConfig != null && simpleConfig.Autoload != null) + { + jsonFilePath = + $"{System.AppDomain.CurrentDomain.BaseDirectory}/{simpleConfig.Autoload}"; + } + else + { + responseResult.Code = (int)HttpStatusCode.BadRequest; + responseResult.Message = "淇敼鏂囦欢璺緞涓嶅"; + } + // 璇诲彇鏈湴 JSON 鏂囦欢璺緞 + // jsonFilePath = $"{System.AppDomain.CurrentDomain.BaseDirectory}/0123.json"; + if (!File.Exists(jsonFilePath)) + { + throw new FileNotFoundException( + $"JSON file not found at path: {jsonFilePath}" + ); + } + + // 璇诲彇骞惰В鏋 JSON 鏂囦欢 + var jsonData = File.ReadAllText(jsonFilePath); + var jsonObject = JObject.Parse(jsonData); + + // 鑾峰彇鎴栧垱寤 "conf" 鑺傜偣 + var confNode = jsonObject["conf"]; + if (confNode == null || confNode.Type != JTokenType.Object) + { + confNode = new JObject(); + jsonObject["conf"] = confNode; + } + + // 鏇存柊 JSON 鏁版嵁 + confNode[trafficControlSetting.Name] = trafficControlSetting.Value; + + // 灏嗕慨鏀瑰悗鐨勬暟鎹啓鍥 JSON 鏂囦欢 + File.WriteAllText(jsonFilePath, jsonObject.ToString()); + + return Response.AsJson(responseResult); + } + catch (JsonSerializationException jsonEx) + { + responseResult.Code = (int)HttpStatusCode.BadRequest; + responseResult.Message = $"JSON瑙f瀽澶辫触锛 ex:{jsonEx.Message}"; + } + catch (Exception e) + { + responseResult.Code = (int)HttpStatusCode.InternalServerError; + responseResult.Message = $"璁剧疆浜ょ鎺у埗鍙傛暟澶辫触锛 ex:{e.Message}"; + } + + return Response.AsJson(responseResult); + }); + #endregion + + #region 鍒犻櫎閰嶇疆鍙傛暟 + Post("/car/delControlSetting", param => + { + Response response; + var responseResult = new BaseRespose + { + Success = true, + Code = (int)HttpStatusCode.OK, + Message = "success" + }; + try + { + var jreq = new System.IO.StreamReader(Request.Body).ReadToEnd(); + var keysToRemove = jreq.JsonTo>(); + // 璇诲彇鏈湴 JSON 鏂囦欢 + var simpleFilePath = + $"{System.AppDomain.CurrentDomain.BaseDirectory}/simple.json"; + var simpleJson = File.ReadAllText(simpleFilePath); + var simpleConfig = simpleJson.JsonTo(); + var jsonFilePath = ""; + if (simpleConfig != null && simpleConfig.Autoload != null) + { + jsonFilePath = + $"{System.AppDomain.CurrentDomain.BaseDirectory}/{simpleConfig.Autoload}"; + } + else + { + responseResult.Code = (int)HttpStatusCode.BadRequest; + responseResult.Message = "淇敼鏂囦欢璺緞涓嶅"; + } + + if (!File.Exists(jsonFilePath)) + { + throw new FileNotFoundException( + $"JSON file not found at path: {jsonFilePath}" + ); + } + var jsonData = File.ReadAllText(jsonFilePath); + var jsonObject = JObject.Parse(jsonData); + var confNode = jsonObject["conf"] as JObject; + + if (confNode == null) + { + confNode = new JObject(); + jsonObject["conf"] = confNode; + } + if (confNode != null) + { + foreach (var key in keysToRemove) + { + if (confNode.ContainsKey(key)) + { + confNode.Remove(key); + } + } + + // 灏嗕慨鏀瑰悗鐨勬暟鎹啓鍥 JSON 鏂囦欢 + File.WriteAllText(jsonFilePath, jsonObject.ToString()); + } + + response = Response.AsText(responseResult.ToJson(), "application/json"); + return response; + } + catch (JsonSerializationException jsonEx) + { + responseResult.Code = (int)HttpStatusCode.BadRequest; + responseResult.Message = $"JSON瑙f瀽澶辫触锛 ex:{jsonEx.Message}"; + response = Response.AsText(responseResult.ToJson(), "application/json"); + return response; + } + catch (FileNotFoundException fnfEx) + { + responseResult.Code = (int)HttpStatusCode.NotFound; + responseResult.Message = $"鏂囦欢鏈壘鍒帮紒 ex:{fnfEx.Message}"; + response = Response.AsText(responseResult.ToJson(), "application/json"); + return response; + } + catch (Exception e) + { + responseResult.Code = (int)HttpStatusCode.InternalServerError; + responseResult.Message = $"璁剧疆鎺у埗鍙傛暟澶辫触锛 ex:{e.Message}"; + response = Response.AsText(responseResult.ToJson(), "application/json"); + return response; + } + }); + #endregion + + #region 鍒犻櫎璺緞瑙勫垝鍙傛暟璁剧疆 + Post("/car/delPlanRulesControlSetting", param => + { + var responseResult = new BaseRespose + { + Success = true, + Code = (int)HttpStatusCode.OK, + Message = "success" + }; + try + { + var jreq = new System.IO.StreamReader(Request.Body).ReadToEnd(); + var planRulesSettings = jreq.JsonTo>(); + if (planRulesSettings.Count() < 0) + { + responseResult.Code = (int)HttpStatusCode.BadRequest; + responseResult.Message = "娌℃湁闇瑕佸垹闄ょ殑璺緞鍙傛暟"; + return Response.AsJson(responseResult); + } + foreach (var planRules in planRulesSettings) + { + //鏍规嵁鍙傛暟锛岀珯鐐笽D 鎵惧埌璇ョ珯鐐癸紝濡傛灉绔欑偣鏈夎繖涓瓧娈靛垯鍒犻櫎锛屾病鏈変笉鐢ㄧ + Site site = SimpleLib.GetSite(int.Parse(planRules.NodeId)); + if (site == null) + { + responseResult.Code = (int)HttpStatusCode.BadRequest; + responseResult.Message = "闇瑕佷慨鏀瑰睘鎬х殑绔欑偣涓嶅瓨鍦"; + return Response.AsJson(responseResult); + } + if (site.fields.TryGetValue(planRules.Name, out var value)) + { + site.fields.Remove(planRules.Name); + } + } + + return Response.AsJson(responseResult); + } + catch (JsonSerializationException jsonEx) + { + responseResult.Code = (int)HttpStatusCode.BadRequest; + responseResult.Message = $"JSON瑙f瀽澶辫触锛 ex:{jsonEx.Message}"; + } + catch (Exception e) + { + responseResult.Code = (int)HttpStatusCode.InternalServerError; + responseResult.Message = $"鍒犻櫎璺緞瑙勫垝鍙傛暟澶辫触锛 ex:{e.Message}"; + } + + return Response.AsJson(responseResult); + }); + #endregion + + #region 璁剧疆璺緞瑙勫垝鍙傛暟 + Post("/car/setPlanRulesControlSetting", param => + { + var responseResult = new BaseRespose + { + Success = true, + Code = (int)HttpStatusCode.OK, + Message = "success" + }; + try + { + var jreq = new System.IO.StreamReader(Request.Body).ReadToEnd(); + var planRulesSetting = jreq.JsonTo(); + if (planRulesSetting == null) + { + responseResult.Code = (int)HttpStatusCode.BadRequest; + responseResult.Message = "闇瑕佷慨鏀瑰睘鎬х殑绔欑偣涓嶅瓨鍦"; + return Response.AsJson(responseResult); + } + Site site = SimpleLib.GetSite(int.Parse(planRulesSetting.NodeId)); + if (site == null) + { + responseResult.Code = (int)HttpStatusCode.BadRequest; + responseResult.Message = "闇瑕佷慨鏀瑰睘鎬х殑绔欑偣涓嶅瓨鍦"; + return Response.AsJson(responseResult); + } + //鍒ゆ柇绔欑偣灞炴ф槸鍚﹀瓨鍦紝瀛樺湪鍒欎慨鏀癸紝涓嶅瓨鍦ㄥ垯娣诲姞 + if (site.fields.TryGetValue(planRulesSetting.Name, out var value)) + { + site.fields[planRulesSetting.Name] = planRulesSetting.Value; + } + else + { + site.fields.Add(planRulesSetting.Name, planRulesSetting.Value); + } + return Response.AsJson(responseResult); + } + catch (JsonSerializationException jsonEx) + { + responseResult.Code = (int)HttpStatusCode.BadRequest; + responseResult.Message = $"JSON瑙f瀽澶辫触锛 ex:{jsonEx.Message}"; + } + catch (Exception e) + { + responseResult.Code = (int)HttpStatusCode.InternalServerError; + responseResult.Message = $"璁剧疆璺緞瑙勫垝鍙傛暟澶辫触锛 ex:{e.Message}"; + } + + return Response.AsJson(responseResult); + }); + #endregion + + Get("/api/QrMap", _ => + { + return QrMapJson; + }); + + Post("/api/CreateSite", o => + { + try + { + string jreq = new System.IO.StreamReader(Request.Body).ReadToEnd(); + QrSite site = JsonConvert.DeserializeObject(jreq); + var newSite = new UISite() { x = site.X, y = site.Y }; + Console.WriteLine($"x:{site.X},y:{site.Y}"); + var d = SimpleLib + .GetAllSites() + .Select(p => LessMath.dist(site.X, site.Y, p.x, p.y)); + if (d.Any() && d.Min() < 3) + { + Console.WriteLine($"閲嶅璁剧疆绔欑偣锛"); + return JsonConvert.SerializeObject( + new { data = new { success = false, message = "璁剧疆绔欑偣澶辫触,閲嶅璁剧疆绔欑偣锛" } } + ); + } + + SimpleLib.SetSite(newSite); + var ss = SimpleLib + .GetAllSites() + .OrderBy(p => LessMath.dist(site.X, site.Y, p.x, p.y)) + .First(); + ss.fields["th"] = (site.Th).ToString(); + ss.fields["tag"] = (site.Tag).ToString(); + return JsonConvert.SerializeObject( + new { data = new { success = true, message = "璁剧疆绔欑偣鎴愬姛" } } + ); + } + catch (Exception e) + { + Console.WriteLine(e); + return JsonConvert.SerializeObject( + new { data = new { success = false, message = "璁剧疆绔欑偣澶辫触" + e } } + ); + } + }); + + #region OTA鑾峰彇杞﹁締鍒楄〃 + Get("/api/agv/list", _ => + { + List cars = new List(); + foreach (var car in SimpleLib.GetAllCars().OfType()) + { + var station = SimpleLib + .GetAllSites() + .ToList() + .FirstOrDefault(site => + site.id == car.status.holdingLocks.FirstOrDefault() + && site.name.Contains("TaskStation") + ); + var statusV = car.status.enums.ContainsKey("actualV") + ? float.Parse(car.status.enums["actualV"]) + : 0; + car.status.enums.TryGetValue("electricCurrent", out var electricCurrent); + var agv_status = ""; + if (statusV == 0) + { + agv_status = "鍋滄"; + } + else if (statusV > 0) + { + agv_status = "杩愯涓"; + } + if ( + statusV == 0 + && electricCurrent != null + && electricCurrent != "" + && float.Parse(electricCurrent) * 0.01f > 0 + ) + { + agv_status = "鍏呯數涓"; + } + + cars.Add( + new + { + agv_id = car.id, + agv_name = car.name, + agv_ip = car.address, + //agv_status = car.tags["carStatu"], + //agv_status = car.tags.Contains("carStatu") ? car.tags["carStatu"] : "OFFLINE", + energy = car.status.enums.ContainsKey("soc") + ? float.Parse(car.status.enums["soc"]) + : -1, + /* x = tPos.Item1, + y = tPos.Item2, + th = tPos.Item3,*/ + x = car.x, + y = car.y, + th = car.th, + speed = Commons.CarValue(car, "sendSpeed"), + fault = car.status.enums.ContainsKey("mAlarm") + ? car.status.enums["mAlarm"] + : "姝e父", + siteId = car.GetLastSite(), + // materialinfo =new string[3] { "materialinfo1", "materialinfo2", "materialinfo3" } + stationInfo = station != null + ? new { stationID = station.id, staionName = station.name } + : null, + + agv_status = agv_status + } + ); + } + return JsonConvert.SerializeObject( + new + { + code = 200, + message = "ok", + data = cars + } + ); + }); + #endregion + + #region 绗笁鏂逛氦绠 + Post("api/agv/traffic", _ => + { + try + { + string jreq = new System.IO.StreamReader(Request.Body).ReadToEnd(); + + var Traffic = JsonConvert.DeserializeObject(jreq); + + var Area = TrafficInterlockMission.TrafficAreaList.Find(t => t.AreaName == Traffic.AreaName); + + if (Traffic != null && Area != null) + { + if (Traffic.IsOccupy && !Area.IsOccupy) + { + lock (TrafficInterlockMission.TrafficAreaList) + { + Area.IsOccupy = true; Area.ControllerName = Traffic.ControllerName; + } + + return JsonConvert.SerializeObject(new { data = new { success = true, message = "鐢宠绠″埗鍖烘垚鍔" } }); + + + } + else if (!Traffic.IsOccupy && Area.ControllerName == Traffic.ControllerName) + { + lock (TrafficInterlockMission.TrafficAreaList) + { + Area.IsOccupy = false; Area.ControllerName = string.Empty; + } + + return JsonConvert.SerializeObject(new { data = new { success = true, message = "閲婃斁绠″埗鍖烘垚鍔" } }); + + } + } + + return JsonConvert.SerializeObject(new { data = new { success = false, message = "澶辫触" } }); + } + catch (Exception e) + { + Console.WriteLine(e); + + return JsonConvert.SerializeObject(new { data = new { success = false, message = "璁剧疆浜ょ澶辫触" + e } }); + } + }); + #endregion + } + + private void ToggleCarReleaseStatus(ClumsyCar car, string currentStatus) + { + int targetStatus = (currentStatus == "1") ? 0 : 1; + string command = $"pilot.ISRelease={targetStatus};"; + + while (Commons.GetCarStatus(car, "ISRelease") == currentStatus) + { + car.ImmediateCommand(command); + Thread.Sleep(100); + } + } + + private void ApplyChargingSettings(ChargingSetting settings) + { + Console.WriteLine("寮濮嬪鐞嗗厖鐢甸厤缃弬鏁扮殑閫昏緫"); + // 杩欓噷鏄簲鐢ㄥ厖鐢佃缃殑鍏蜂綋閫昏緫 + // 渚嬪锛屾洿鏂拌澶囩殑鍏呯數鍙傛暟绛 + var chargeMission = SimpleProject + .proj.Missions.OfType() + .FirstOrDefault(); + if (chargeMission == null) + return; + Commons.AddOrUpdateMissionField( + chargeMission, + "mustChargeSoc", + settings.CarMinBattery.ToString() + ); + Commons.AddOrUpdateMissionField( + chargeMission, + "fullChargeSoc", + settings.CarMaxBattery.ToString() + ); + Commons.AddOrUpdateMissionField( + chargeMission, + "taskAvailableSoc", + settings.TaskAvailableBattery.ToString() + ); + Commons.AddOrUpdateMissionField( + chargeMission, + "mustChargeSeconds", + settings.CarIdleSecond.ToString() + ); + Commons.AddOrUpdateMissionField( + chargeMission, + "idleChargeSoc", + settings.CarIdleChargeBattery.ToString() + ); + } + + private (bool, int, string, CarStateInfo) CreateTransportTask( + TaskRequest req, + bool isCreate + ) + { + var transportMission = SimpleProject.proj.Missions.OfType().First(); + //鏍¢獙浠诲姟閲嶅 + if ( + transportMission + .GetDeliveries(true) + .FindAll(p => ((TransportDelivery)p).TaskId == req.TaskCode) + .Count > 0 + ) + { + return ( + false, + (int)HttpStatusCode.InternalServerError, + $"taskCode:{req.TaskCode}宸茬粡瀛樺湪", + GetCarStateInfo(req.CarCode) + ); + } + //鍒ゅ畾鎸囧畾杞﹁締鏄惁瀛樺湪 + Car car = null; + if (!string.IsNullOrWhiteSpace(req.CarCode)) + { + car = SimpleLib + .GetAllCars() + .OfType() + .FirstOrDefault(e => e.id.ToString() == req.CarCode); + if (car == null || car.status.holdingLocks.FirstOrDefault() == -1) + { + return ( + false, + (int)HttpStatusCode.InternalServerError, + $"浼犲叆杞﹁締code{req.CarCode}涓嶅瓨鍦ㄦ垨娌℃湁鍒濆鍖", + new CarStateInfo() + ); + } + } + //鍒ゆ柇node鐨勪釜鏁 + switch (req.Nodes.Count) + { + case 2: + Diagnosis.Log( + $"receiveTask:[TaskId ={req.TaskCode},carId = {req.CarCode},src = {req.Nodes[0].Code},dst = {req.Nodes[1].Code}", + "task", + true + ); + break; + case 1: + if (car == null) + return ( + false, + (int)HttpStatusCode.InternalServerError, + $"鍒涘缓鍗曠偣浠诲姟鏃讹紝鏈寚瀹氳溅杈", + new CarStateInfo() + ); + Diagnosis.Log( + $"receiveTask:[TaskId ={req.TaskCode},carId = {req.CarCode},dst = {req.Nodes[0].Code}", + "task", + true + ); + break; + default: + return ( + false, + (int)HttpStatusCode.InternalServerError, + $"鍒涘缓浠诲姟鏃讹紝浼犲叆绔欑偣涓暟涓嶅", + new CarStateInfo() + ); + } + + //绔欑偣瀛樺湪鍒ゆ柇,榛樿閮芥槸鍙岀偣浠诲姟 + var siteCodes = req.Nodes.Select(e => e.Code).ToArray(); + if (!Commons.IsSceneSiteByCode(siteCodes)) + { + return ( + false, + (int)HttpStatusCode.InternalServerError, + $"涓嶅瓨鍦ㄨ瀹氱珯鐐 => taskCode:{req.TaskCode}", + new CarStateInfo() + ); + } + + var src = + req.Nodes.Count == 2 + ? SimpleLib.GetSite(req.Nodes[0].Code) + : SimpleLib.GetSite(car.status.holdingLocks.First()); //濡傛灉鏄崟鐐逛换鍔★紝榛樿褰撳墠鏈煡涓鸿捣鐐 + var dst = + req.Nodes.Count == 2 + ? SimpleLib.GetSite(req.Nodes[1].Code) + : SimpleLib.GetSite(req.Nodes[0].Code); + + var d = new TransportDelivery + { + Src = src.id, + Dst = dst.id, + CarType = req.CarType, + TaskId = req.TaskCode, + TaskType = req.TaskType, + Material = req.Material, + Priority = req.priority, + MaterialLength = req.ContainerSize != null ? (float)req.ContainerSize.Length : -1, + MaterialWidth = req.ContainerSize != null ? (float)req.ContainerSize.Width : -1, + }; + if (car != null) + { + d.UsingCar = car; + } + + d.FetchPlanInfo["MaterialLength"] = d.MaterialLength + ""; + d.FetchPlanInfo["MaterialWidth"] = d.MaterialWidth + ""; + if (d.Src == d.Dst) + { + d.FetchPlanInfo["action"] = "/"; + d.PutPlanInfo["action"] = "/"; + } + // 鍒濆鍖栧洖璋冮厤缃紝纭繚浠诲姟鍦ㄥ悇闃舵瑙﹀彂缁熶竴鍥炶皟 + d.ReportOnStarted = true; + d.ReportOnFetched = true; + d.ReportOnPut = true; + d.ReportOnFinished = true; + d.ReportOnFailed = true; + d.ReportOnTerminated = true; + + // 鏍规嵁 ReportOn* 鍐欏叆 key 鍒楄〃 + if (d.ReportOnStarted) + d.OnStartCallbackKeys.Add(TransportDeliveryCallbacks.KeyOnStarted); + if (d.ReportOnFetched) + d.DoneFetchCallbackKeys.Add(TransportDeliveryCallbacks.KeyOnFetched); + if (d.ReportOnPut) + d.DonePutCallbackKeys.Add(TransportDeliveryCallbacks.KeyOnPut); + if (d.ReportOnFinished) + d.DoneMissionCallbackKeys.Add(TransportDeliveryCallbacks.KeyOnFinished); + if (d.ReportOnFailed) + d.FailedCallbackKeys.Add(TransportDeliveryCallbacks.KeyOnFailed); + if (d.ReportOnTerminated) + d.OnTerminatedCallbackKeys.Add(TransportDeliveryCallbacks.KeyOnTerminated); + + // 缁熶竴閫氳繃 Attacher 鎸傝浇鍥炶皟锛屼究浜庢寔涔呭寲涓庢仮澶 + DeliveryCallbackAttacher.AttachAll(d); + + transportMission.Enqueue(d); + Diagnosis.Log($"Add task =>{d.ToJson()}锛宺eq:{req.ToJson()}"); + return (true, (int)HttpStatusCode.OK, "success", GetCarStateInfo(req.CarCode)); + } + + private class GetCar + { + public string CarCode; + } + + public CarStateInfo GetCarStateInfo(string carCode) + { + try + { + var transportMission = SimpleProject + .proj.Missions.OfType() + .FirstOrDefault(); + if (string.IsNullOrEmpty(carCode)) + return new CarStateInfo(); + + var singleCar = SimpleLib + .GetAllCars() + .OfType() + .FirstOrDefault(e => e.id.ToString() == carCode && e.GetLastSite() != -1); + if (singleCar == null) + return new CarStateInfo(); + + var runningTask = (TransportDelivery) + transportMission + .GetDeliveries() + .FindAll(p => + ( + p.GetStatus() == DeliveryStatus.Fetching + || p.GetStatus() == DeliveryStatus.Putting + ) + && p.UsingCar.id == singleCar.id + ) + .FirstOrDefault(); //鑾峰彇褰撳墠杞﹁締姝e湪杩愯鐨勪换鍔 + var finishedTask = (TransportDelivery) + transportMission + .GetDeliveries(true) + .FindAll(p => + p.GetStatus() == DeliveryStatus.Finished + && p.UsingCar.id == singleCar.id + ) + .OrderByDescending(e => e.FinishTime) + .FirstOrDefault(); //鑾峰彇鏈杩戝畬鎴愮殑浠诲姟 + var waitingTask = (TransportDelivery) + transportMission + .GetDeliveries() + .FindAll(p => + p.GetStatus() == DeliveryStatus.Waiting + && p.UsingCar != null + && p.UsingCar.id == singleCar.id + ) + .OrderBy(e => e.CreateTime) + .FirstOrDefault(); + var taskList = new List(); + var actions = new List(); + //string carState = SwitchCarState(singleCar); + var isOnline = false; + string carState = SwitchCarState(singleCar, ref isOnline); + //var resNodes = new List(); + if (finishedTask != null) + { + taskList.Add( + new TaskInfo() + { + Code = finishedTask.TaskId, + State = "Completed", + Nodes = new List() + { + new() + { + //Code = Commons.GetSiteCodeById(finishedTask.src) + Code = finishedTask.Src.ToString() + }, + new() + { + //Code = Commons.GetSiteCodeById(finishedTask.dst) + Code = finishedTask.Dst.ToString() + } + } + } + ); + } + + if (runningTask != null) + { + taskList.Add( + new TaskInfo() + { + Code = runningTask.TaskId, + State = "Running", + Nodes = new List() + { + new() + { + //Code = Commons.GetSiteCodeById(runningTask.src) + Code = runningTask.Src.ToString() + }, + new() + { + //Code = Commons.GetSiteCodeById(runningTask.dst) + Code = runningTask.Dst.ToString() + } + } + } + ); + } + + if (waitingTask != null) + { + taskList.Add( + new TaskInfo() + { + Code = waitingTask.TaskId, + State = "Distributed", + Nodes = new List() + { + new() + { + //Code = Commons.GetSiteCodeById(waittingTask.src) + Code = waitingTask.Src.ToString() + }, + new() + { + //Code = Commons.GetSiteCodeById(waittingTask.dst) + Code = waitingTask.Dst.ToString() + } + } + } + ); + } + + var carId = singleCar.id; + var alarmsList = new List(); + if ( + singleCar.status.enums.ContainsKey("AlarmInfo") + && !string.IsNullOrWhiteSpace(singleCar.status.enums["AlarmInfo"]) + ) + { + string alarmMessage = singleCar.status.enums["AlarmInfo"].TrimEnd(','); + string[] alarms = alarmMessage.Split(','); + foreach (string alarm in alarms) + { + string[] info = alarm.Split('.'); + if (info.Length == 2) + { + alarmsList.Add(new CarAlarm() { Code = info[0], Name = info[1] }); + } + } + } + + var currentSite = SimpleLib.GetSite(singleCar.status.holdingLocks.FirstOrDefault()); + // HashSet> blockedBy = singleCar.status.blockedBy; + var blockedBy = singleCar.status.blockedBy.ToHashSet(); + List blockedByItems = new List(); + //var BlockedBy = string.Empty; + for (int i = 0; i < blockedBy.Count; i++) + { + //BlockedBy += $"({blockedBy.ElementAt(i).Item1 + "," + blockedBy.ElementAt(i).Item2});"; + blockedByItems.Add( + new BlockedByItem + { + CarCode = blockedBy.ElementAt(i).Item1.ToString(), + Type = blockedBy.ElementAt(i).Item2 + } + ); + } + //BlockedBy = BlockedBy.TrimEnd(';'); + return new CarStateInfo() + { + Code = carCode, + Name = singleCar.name, + CurrState = carState, + IsOnline = isOnline, + Battery = singleCar.status.enums.ContainsKey("Soc") + ? int.Parse(singleCar.status.enums["Soc"]) + : 100, + Voltage = singleCar.status.enums.ContainsKey("Voltage") + ? double.Parse(singleCar.status.enums["Voltage"]) + : 0, + ElectricCurrent = singleCar.status.enums.ContainsKey("ElectricCurrent") + ? double.Parse(singleCar.status.enums["ElectricCurrent"]) + : 0, + X = singleCar.x, + Y = singleCar.y, + Theta = singleCar.th, + Speed = singleCar.status.enums.ContainsKey("ActualLeftWheelVelocity") + ? double.Parse( + SimpleLib.GetCar(carId).status.enums["ActualLeftWheelVelocity"] + ) + : singleCar.speed, + //CurrNodeCode = currentSite.fields.ContainsKey("code") ? currentSite.fields["code"] : currentSite.id.ToString(), + Load = singleCar.status.enums.TryGetValue("loadStatus", out var statusEnum) + ? int.Parse(statusEnum) + : 0, + CurrNodeCode = currentSite.id.ToString(), + StartNodeCode = "0", + EndNodeCode = "0", + StartEdgeCode = "0", + EndEdgeCode = "0", + HoldingLocks = singleCar.status.holdingLocks.ToList(), + PendingLocks = singleCar.status.pendingLocks.ToList(), + BlockedBy = blockedByItems, + // BlockingTime = singleCar.status.blockingTime.ToString("yyyy-MM-dd HH:mm:ss"),//娌℃湁浜 + TrafficMessage = singleCar.status.TCStat.ToString(), + AquiringLock = singleCar.status.aquiringLock, + Tasks = taskList, + Alarms = alarmsList, + StopAccept = singleCar.fields.ContainsKey("StopAccept"), + Actions = new List() + }; + } + catch (Exception ex) + { + Console.WriteLine($"getCarStateInfo => ex: {ex}"); + return new CarStateInfo(); + } + } + + public string SwitchCarState(Car car, ref bool isOnline) + { + string state = "Stopping"; + if (car.GetLastSite() == -1) + { + isOnline = false; + return state; + } + if (Commons.GetVehicleStatus(car)!= VehicleStatus.Offline) + { + if (Commons.GetVehicleStatus(car) is VehicleStatus.Normal or VehicleStatus.NeedInit) + state = "Stopping"; //Idle + var lp = car.status.programs.latest; + if (lp != null) + { + if ( + lp.status.state >= SimpleCore.Compiler.CarProgram.StatusEnum.Programming + && lp.status.state <= SimpleCore.Compiler.CarProgram.StatusEnum.Running + ) + { + state = "Running"; //Executing + } + } + //瀛樺湪鎶ヨ淇℃伅 + if (car.status.enums.ContainsKey("AlarmInfo")) + { + if (!string.IsNullOrEmpty(car.status.enums["AlarmInfo"])) + { + state = "Faulting"; //Malfunction + } + } + if (car.tags.Contains("charging")) + state = "Charging"; + isOnline = true; + } + else + { + isOnline = false; + } + + return state; + } + + public string GetDeliveryStatus(TransportDelivery delivery) + { + ChainedDeliveryMission.DeliveryStatus status = + (ChainedDeliveryMission.DeliveryStatus)delivery.GetStatus(); + + if (status == ChainedDeliveryMission.DeliveryStatus.Error) + { + return "Faulted"; + } + else if (status == ChainedDeliveryMission.DeliveryStatus.Canceled) + { + return "Canceled"; + } + else if (status == ChainedDeliveryMission.DeliveryStatus.Terminated) + { + return "Terminated"; + } + else if (status == ChainedDeliveryMission.DeliveryStatus.Finished) + { + return "Completed"; + } + else if (status == ChainedDeliveryMission.DeliveryStatus.Putting) + { + return "Putting"; + } + else if (status == ChainedDeliveryMission.DeliveryStatus.Fetching) + { + return "Fetching"; + } + else if (status == ChainedDeliveryMission.DeliveryStatus.Waiting) + { + return "Created"; + } + else + { + return "UnKnown"; + } + } + } + + internal class MethodInfoDetails + { + public string MethodName; + + public string ButtonName; + + public string ButtonDescription; + + public string ParamsHint; + + public List ParamsList = new(); + } + + internal class ParamInfo + { + public ParamInfo(string name, Type type) + { + Name = name; + Type = type; + } + + public string Name; + public Type Type; + } + + public class HttpPostData + { + private HttpClient hc = new HttpClient() { Timeout = TimeSpan.FromSeconds(3) }; + + public void UploadSignal(string content) + { + try + { + //var json = JsonConvert.SerializeObject(new { }); + //var data = new StringContent(json, Encoding.UTF8, "application/json"); + StringContent stringContent = new StringContent( + JsonConvert.SerializeObject(content) + ); + //ApiController._logger.Info($"DeviceNotifyPost:[{stringContent.ToString()}]"); + + stringContent.Headers.ContentType = new MediaTypeHeaderValue("application/json"); + //hc.Timeout = TimeSpan.FromSeconds(30); + var task = hc.PostAsync( + "http://localhost:20101/api/v1/SecuritySignal/GetSecuritySignal", + stringContent + ); + var responseStr = task.Result.Content.ReadAsStringAsync().Result; + ResultMsg response = JsonConvert.DeserializeObject(responseStr); + } + catch (Exception e) + { + Diagnosis.Log("DeviceNotifyPost" + ExceptionFormatter.FormatEx(e), $"error", true); + } + } + + public void UploadListNode(HashSet contentList) + { + try + { + /* if (contentList == null || contentList.Count == 0) + { + throw new ArgumentException("Content list cannot be null or empty."); + }*/ + // 搴忓垪鍖朙ist涓篔SON瀛楃涓 + var jsonContent = JsonConvert.SerializeObject(contentList); + StringContent stringContent = new StringContent( + jsonContent, + Encoding.UTF8, + "application/json" + ); + + // 璁剧疆鍐呭绫诲瀷涓篴pplication/json + stringContent.Headers.ContentType = new MediaTypeHeaderValue("application/json"); + // 鍙戦丳OST璇锋眰 + var task = hc.PostAsync( + "http://localhost:20101/api/v1/Node/NodeListDisable", + stringContent + ); + // 璇诲彇鍝嶅簲鍐呭 + var responseStr = task.Result.Content.ReadAsStringAsync().Result; + // 鍙嶅簭鍒楀寲鍝嶅簲 + ResultMsg response = JsonConvert.DeserializeObject(responseStr); + } + catch (Exception e) + { + // 璁板綍寮傚父 + Diagnosis.Log("DeviceNotifyPost" + ExceptionFormatter.FormatEx(e), "error", true); + } + } + } + + public class QrSite + { + public float X; + public float Y; + public float Th; + public int Tag; + } + + class ResultMsg + { + public string code { get; set; } + + public string reqCode { get; set; } + public string msg { get; set; } + public int statu { get; set; } + } + + public class TrafficRequestModel + { + /// + /// 鍖哄煙鍚嶇О + /// + public string AreaName { get; set; } + + /// + /// 鎺у埗鍗曚綅 + /// + public string ControllerName { get; set; } + + /// + /// 鐢宠鍗犵敤/閲婃斁鍖哄煙锛宼rue涓哄崰鐢紝false涓洪噴鏀 + /// + public bool IsOccupy { get; set; } + } +} diff --git a/StandardScene.Core/refpath.json b/StandardScene.Core/refpath.json new file mode 100644 index 0000000..3f67a26 --- /dev/null +++ b/StandardScene.Core/refpath.json @@ -0,0 +1,6973 @@ +锘縶 + "Items": { + "ReferencePath": [ + { + "Identity": "C:\\Users\\admin\\.nuget\\packages\\microsoft.windowsdesktop.app.ref\\8.0.27\\ref\\net8.0\\Accessibility.dll", + "FileVersion": "8.0.2726.23001", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "AssemblyName": "Accessibility", + "NuGetPackageVersion": "8.0.27", + "Private": "false", + "PublicKeyToken": "31bf3856ad364e35", + "Version": "", + "FrameworkReferenceVersion": "8.0.27", + "ExternallyResolved": "true", + "NuGetPackageId": "Microsoft.WindowsDesktop.App.Ref", + "ResolvedFrom": "{RawFileName}", + "FrameworkReferenceName": "Microsoft.WindowsDesktop.App.WindowsForms", + "ImageRuntime": "v4.0.30319", + "AssemblyVersion": "4.0.0.0", + "CopyLocal": "false", + "OriginalItemSpec": "C:\\Users\\admin\\.nuget\\packages\\microsoft.windowsdesktop.app.ref\\8.0.27\\ref/net8.0/Accessibility.dll", + "FusionName": "Accessibility, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "FullPath": "C:\\Users\\admin\\.nuget\\packages\\microsoft.windowsdesktop.app.ref\\8.0.27\\ref\\net8.0\\Accessibility.dll", + "RootDir": "C:\\", + "Filename": "Accessibility", + "Extension": ".dll", + "RelativeDir": "C:\\Users\\admin\\.nuget\\packages\\microsoft.windowsdesktop.app.ref\\8.0.27\\ref\\net8.0\\", + "Directory": "Users\\admin\\.nuget\\packages\\microsoft.windowsdesktop.app.ref\\8.0.27\\ref\\net8.0\\", + "RecursiveDir": "", + "ModifiedTime": "2026-04-30 15:24:22.0000000", + "CreatedTime": "2026-05-29 17:24:43.9702275", + "AccessedTime": "2026-06-09 15:08:48.6723289", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "C:\\Users\\admin\\.nuget\\packages\\acornima\\1.3.0\\lib\\net8.0\\Acornima.dll", + "HintPath": "C:\\Users\\admin\\.nuget\\packages\\acornima\\1.3.0\\lib\\net8.0\\Acornima.dll", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "NuGetPackageVersion": "1.3.0", + "Private": "false", + "Version": "", + "ExternallyResolved": "true", + "NuGetPackageId": "Acornima", + "ResolvedFrom": "{HintPathFromItem}", + "ImageRuntime": "v4.0.30319", + "CopyLocal": "false", + "OriginalItemSpec": "C:\\Users\\admin\\.nuget\\packages\\acornima\\1.3.0\\lib\\net8.0\\Acornima.dll", + "FusionName": "Acornima, Version=1.3.0.0, Culture=neutral, PublicKeyToken=496b38436bb9edeb", + "PathInPackage": "lib/net8.0/Acornima.dll", + "NuGetSourceType": "Package", + "FullPath": "C:\\Users\\admin\\.nuget\\packages\\acornima\\1.3.0\\lib\\net8.0\\Acornima.dll", + "RootDir": "C:\\", + "Filename": "Acornima", + "Extension": ".dll", + "RelativeDir": "C:\\Users\\admin\\.nuget\\packages\\acornima\\1.3.0\\lib\\net8.0\\", + "Directory": "Users\\admin\\.nuget\\packages\\acornima\\1.3.0\\lib\\net8.0\\", + "RecursiveDir": "", + "ModifiedTime": "2026-03-11 05:55:06.0000000", + "CreatedTime": "2026-03-25 17:54:20.9056381", + "AccessedTime": "2026-06-09 15:08:48.6683281", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "E:\\Work\\Core\\Simple-FR\\StandardSence\\build\\CommonUsage.dll", + "HintPath": "D:\\MDCS\\Dependencies\\Commons\\CommonUsage.dll", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "Version": "", + "ResolvedFrom": "{CandidateAssemblyFiles}", + "ImageRuntime": "v4.0.30319", + "CopyLocal": "true", + "OriginalItemSpec": "CommonUsage", + "FusionName": "CommonUsage, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null", + "FullPath": "E:\\Work\\Core\\Simple-FR\\StandardSence\\build\\CommonUsage.dll", + "RootDir": "E:\\", + "Filename": "CommonUsage", + "Extension": ".dll", + "RelativeDir": "E:\\Work\\Core\\Simple-FR\\StandardSence\\build\\", + "Directory": "Work\\Core\\Simple-FR\\StandardSence\\build\\", + "RecursiveDir": "", + "ModifiedTime": "2026-05-11 14:42:31.7071990", + "CreatedTime": "2026-06-09 12:57:36.5577076", + "AccessedTime": "2026-06-09 15:36:39.7316366", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "C:\\Users\\admin\\.nuget\\packages\\documentformat.openxml\\3.4.1\\lib\\net8.0\\DocumentFormat.OpenXml.dll", + "HintPath": "C:\\Users\\admin\\.nuget\\packages\\documentformat.openxml\\3.4.1\\lib\\net8.0\\DocumentFormat.OpenXml.dll", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "NuGetPackageVersion": "3.4.1", + "Private": "false", + "Version": "", + "ExternallyResolved": "true", + "NuGetPackageId": "DocumentFormat.OpenXml", + "ResolvedFrom": "{HintPathFromItem}", + "ImageRuntime": "v4.0.30319", + "CopyLocal": "false", + "OriginalItemSpec": "C:\\Users\\admin\\.nuget\\packages\\documentformat.openxml\\3.4.1\\lib\\net8.0\\DocumentFormat.OpenXml.dll", + "FusionName": "DocumentFormat.OpenXml, Version=3.4.1.0, Culture=neutral, PublicKeyToken=8fb06cb64d019a17", + "PathInPackage": "lib/net8.0/DocumentFormat.OpenXml.dll", + "NuGetSourceType": "Package", + "FullPath": "C:\\Users\\admin\\.nuget\\packages\\documentformat.openxml\\3.4.1\\lib\\net8.0\\DocumentFormat.OpenXml.dll", + "RootDir": "C:\\", + "Filename": "DocumentFormat.OpenXml", + "Extension": ".dll", + "RelativeDir": "C:\\Users\\admin\\.nuget\\packages\\documentformat.openxml\\3.4.1\\lib\\net8.0\\", + "Directory": "Users\\admin\\.nuget\\packages\\documentformat.openxml\\3.4.1\\lib\\net8.0\\", + "RecursiveDir": "", + "ModifiedTime": "2026-01-07 03:41:52.0000000", + "CreatedTime": "2026-04-02 15:34:44.7014389", + "AccessedTime": "2026-06-09 15:08:48.6633339", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "C:\\Users\\admin\\.nuget\\packages\\documentformat.openxml.framework\\3.4.1\\lib\\net8.0\\DocumentFormat.OpenXml.Framework.dll", + "HintPath": "C:\\Users\\admin\\.nuget\\packages\\documentformat.openxml.framework\\3.4.1\\lib\\net8.0\\DocumentFormat.OpenXml.Framework.dll", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "NuGetPackageVersion": "3.4.1", + "Private": "false", + "Version": "", + "ExternallyResolved": "true", + "NuGetPackageId": "DocumentFormat.OpenXml.Framework", + "ResolvedFrom": "{HintPathFromItem}", + "ImageRuntime": "v4.0.30319", + "CopyLocal": "false", + "OriginalItemSpec": "C:\\Users\\admin\\.nuget\\packages\\documentformat.openxml.framework\\3.4.1\\lib\\net8.0\\DocumentFormat.OpenXml.Framework.dll", + "FusionName": "DocumentFormat.OpenXml.Framework, Version=3.4.1.0, Culture=neutral, PublicKeyToken=8fb06cb64d019a17", + "PathInPackage": "lib/net8.0/DocumentFormat.OpenXml.Framework.dll", + "NuGetSourceType": "Package", + "FullPath": "C:\\Users\\admin\\.nuget\\packages\\documentformat.openxml.framework\\3.4.1\\lib\\net8.0\\DocumentFormat.OpenXml.Framework.dll", + "RootDir": "C:\\", + "Filename": "DocumentFormat.OpenXml.Framework", + "Extension": ".dll", + "RelativeDir": "C:\\Users\\admin\\.nuget\\packages\\documentformat.openxml.framework\\3.4.1\\lib\\net8.0\\", + "Directory": "Users\\admin\\.nuget\\packages\\documentformat.openxml.framework\\3.4.1\\lib\\net8.0\\", + "RecursiveDir": "", + "ModifiedTime": "2026-01-07 03:41:52.0000000", + "CreatedTime": "2026-04-02 15:34:44.5489739", + "AccessedTime": "2026-06-09 15:08:48.6561443", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "C:\\Users\\admin\\.nuget\\packages\\easymodbustcp\\5.6.0\\lib\\net40\\EasyModbus.dll", + "HintPath": "C:\\Users\\admin\\.nuget\\packages\\easymodbustcp\\5.6.0\\lib\\net40\\EasyModbus.dll", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "NuGetPackageVersion": "5.6.0", + "Private": "false", + "Version": "", + "ExternallyResolved": "true", + "NuGetPackageId": "EasyModbusTCP", + "ResolvedFrom": "{HintPathFromItem}", + "ImageRuntime": "v4.0.30319", + "CopyLocal": "false", + "OriginalItemSpec": "C:\\Users\\admin\\.nuget\\packages\\easymodbustcp\\5.6.0\\lib\\net40\\EasyModbus.dll", + "FusionName": "EasyModbus, Version=5.6.0.0, Culture=neutral, PublicKeyToken=null", + "PathInPackage": "lib/net40/EasyModbus.dll", + "NuGetSourceType": "Package", + "FullPath": "C:\\Users\\admin\\.nuget\\packages\\easymodbustcp\\5.6.0\\lib\\net40\\EasyModbus.dll", + "RootDir": "C:\\", + "Filename": "EasyModbus", + "Extension": ".dll", + "RelativeDir": "C:\\Users\\admin\\.nuget\\packages\\easymodbustcp\\5.6.0\\lib\\net40\\", + "Directory": "Users\\admin\\.nuget\\packages\\easymodbustcp\\5.6.0\\lib\\net40\\", + "RecursiveDir": "", + "ModifiedTime": "2020-12-31 21:44:38.0000000", + "CreatedTime": "2026-03-24 15:22:34.1314263", + "AccessedTime": "2026-06-09 15:08:48.6531437", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "C:\\Users\\admin\\.nuget\\packages\\iotclient\\1.0.40\\lib\\netstandard2.0\\IoTClient.dll", + "HintPath": "C:\\Users\\admin\\.nuget\\packages\\iotclient\\1.0.40\\lib\\netstandard2.0\\IoTClient.dll", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "NuGetPackageVersion": "1.0.40", + "Private": "false", + "Version": "", + "ExternallyResolved": "true", + "NuGetPackageId": "IoTClient", + "ResolvedFrom": "{HintPathFromItem}", + "ImageRuntime": "v4.0.30319", + "CopyLocal": "false", + "OriginalItemSpec": "C:\\Users\\admin\\.nuget\\packages\\iotclient\\1.0.40\\lib\\netstandard2.0\\IoTClient.dll", + "FusionName": "IoTClient, Version=1.0.40.0, Culture=neutral, PublicKeyToken=null", + "PathInPackage": "lib/netstandard2.0/IoTClient.dll", + "NuGetSourceType": "Package", + "FullPath": "C:\\Users\\admin\\.nuget\\packages\\iotclient\\1.0.40\\lib\\netstandard2.0\\IoTClient.dll", + "RootDir": "C:\\", + "Filename": "IoTClient", + "Extension": ".dll", + "RelativeDir": "C:\\Users\\admin\\.nuget\\packages\\iotclient\\1.0.40\\lib\\netstandard2.0\\", + "Directory": "Users\\admin\\.nuget\\packages\\iotclient\\1.0.40\\lib\\netstandard2.0\\", + "RecursiveDir": "", + "ModifiedTime": "2022-10-25 16:40:42.0000000", + "CreatedTime": "2026-03-24 15:22:34.1314263", + "AccessedTime": "2026-06-09 15:08:48.6501429", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "C:\\Users\\admin\\.nuget\\packages\\jint\\4.6.3\\lib\\net8.0\\Jint.dll", + "HintPath": "C:\\Users\\admin\\.nuget\\packages\\jint\\4.6.3\\lib\\net8.0\\Jint.dll", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "NuGetPackageVersion": "4.6.3", + "Private": "false", + "Version": "", + "ExternallyResolved": "true", + "NuGetPackageId": "Jint", + "ResolvedFrom": "{HintPathFromItem}", + "ImageRuntime": "v4.0.30319", + "CopyLocal": "false", + "OriginalItemSpec": "C:\\Users\\admin\\.nuget\\packages\\jint\\4.6.3\\lib\\net8.0\\Jint.dll", + "FusionName": "Jint, Version=4.6.3.0, Culture=neutral, PublicKeyToken=2e92ba9c8d81157f", + "PathInPackage": "lib/net8.0/Jint.dll", + "NuGetSourceType": "Package", + "FullPath": "C:\\Users\\admin\\.nuget\\packages\\jint\\4.6.3\\lib\\net8.0\\Jint.dll", + "RootDir": "C:\\", + "Filename": "Jint", + "Extension": ".dll", + "RelativeDir": "C:\\Users\\admin\\.nuget\\packages\\jint\\4.6.3\\lib\\net8.0\\", + "Directory": "Users\\admin\\.nuget\\packages\\jint\\4.6.3\\lib\\net8.0\\", + "RecursiveDir": "", + "ModifiedTime": "2026-03-12 04:36:24.0000000", + "CreatedTime": "2026-03-25 17:54:20.9619089", + "AccessedTime": "2026-06-09 15:08:48.6461438", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "E:\\Work\\Core\\Simple-FR\\StandardSence\\Ref\\leegKeys-sdk.dll", + "HintPath": "Ref\\leegKeys-sdk.dll", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "Version": "", + "ResolvedFrom": "{CandidateAssemblyFiles}", + "ImageRuntime": "v4.0.30319", + "CopyLocal": "true", + "OriginalItemSpec": "leegKeys-sdk", + "FusionName": "leegKeys-sdk, Version=2.1.1.33176, Culture=neutral, PublicKeyToken=null", + "FullPath": "E:\\Work\\Core\\Simple-FR\\StandardSence\\Ref\\leegKeys-sdk.dll", + "RootDir": "E:\\", + "Filename": "leegKeys-sdk", + "Extension": ".dll", + "RelativeDir": "E:\\Work\\Core\\Simple-FR\\StandardSence\\Ref\\", + "Directory": "Work\\Core\\Simple-FR\\StandardSence\\Ref\\", + "RecursiveDir": "", + "ModifiedTime": "2026-05-30 09:15:26.8949314", + "CreatedTime": "2026-06-09 13:02:25.2575075", + "AccessedTime": "2026-06-09 15:33:01.5388188", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "E:\\Work\\Core\\Simple-FR\\Simple\\SimpleLite\\bin\\Debug\\LessokajiWeaverUtilities.dll", + "HintPath": "E:\\Work\\Core\\Simple-FR\\Simple\\SimpleLite\\bin\\Debug\\LessokajiWeaverUtilities.dll", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "Version": "", + "ResolvedFrom": "{HintPathFromItem}", + "ImageRuntime": "v4.0.30319", + "CopyLocal": "true", + "OriginalItemSpec": "LessokajiWeaverUtilities", + "FusionName": "LessokajiWeaverUtilities, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null", + "FullPath": "E:\\Work\\Core\\Simple-FR\\Simple\\SimpleLite\\bin\\Debug\\LessokajiWeaverUtilities.dll", + "RootDir": "E:\\", + "Filename": "LessokajiWeaverUtilities", + "Extension": ".dll", + "RelativeDir": "E:\\Work\\Core\\Simple-FR\\Simple\\SimpleLite\\bin\\Debug\\", + "Directory": "Work\\Core\\Simple-FR\\Simple\\SimpleLite\\bin\\Debug\\", + "RecursiveDir": "", + "ModifiedTime": "2026-05-29 15:37:09.7318341", + "CreatedTime": "2026-06-09 14:32:25.7817234", + "AccessedTime": "2026-06-09 15:08:48.6393164", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "E:\\Work\\Core\\Simple-FR\\StandardSence\\build\\MDCSToolBox.dll", + "HintPath": "D:\\MDCS\\Dependencies\\Commons\\MDCSToolBox.dll", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "Version": "", + "ResolvedFrom": "{CandidateAssemblyFiles}", + "ImageRuntime": "v4.0.30319", + "CopyLocal": "true", + "OriginalItemSpec": "MDCSToolBox", + "FusionName": "MDCSToolBox, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null", + "FullPath": "E:\\Work\\Core\\Simple-FR\\StandardSence\\build\\MDCSToolBox.dll", + "RootDir": "E:\\", + "Filename": "MDCSToolBox", + "Extension": ".dll", + "RelativeDir": "E:\\Work\\Core\\Simple-FR\\StandardSence\\build\\", + "Directory": "Work\\Core\\Simple-FR\\StandardSence\\build\\", + "RecursiveDir": "", + "ModifiedTime": "2025-07-23 10:35:20.0000000", + "CreatedTime": "2026-06-09 12:57:36.6246719", + "AccessedTime": "2026-06-09 15:08:48.6383141", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\Microsoft.CSharp.dll", + "FileVersion": "8.0.2726.22922", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "AssemblyName": "Microsoft.CSharp", + "NuGetPackageVersion": "8.0.27", + "Private": "false", + "PublicKeyToken": "b03f5f7f11d50a3a", + "Version": "", + "FrameworkReferenceVersion": "8.0.27", + "ExternallyResolved": "true", + "NuGetPackageId": "Microsoft.NETCore.App.Ref", + "ResolvedFrom": "{RawFileName}", + "FrameworkReferenceName": "Microsoft.NETCore.App", + "ImageRuntime": "v4.0.30319", + "AssemblyVersion": "8.0.0.0", + "CopyLocal": "false", + "OriginalItemSpec": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref/net8.0/Microsoft.CSharp.dll", + "FusionName": "Microsoft.CSharp, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", + "FullPath": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\Microsoft.CSharp.dll", + "RootDir": "C:\\", + "Filename": "Microsoft.CSharp", + "Extension": ".dll", + "RelativeDir": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "Directory": "Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "RecursiveDir": "", + "ModifiedTime": "2026-04-30 07:39:00.0000000", + "CreatedTime": "2026-05-29 17:24:44.9322380", + "AccessedTime": "2026-06-09 15:16:46.9250516", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "C:\\Users\\admin\\.nuget\\packages\\microsoft.dotnet.platformabstractions\\2.0.4\\lib\\netstandard1.3\\Microsoft.DotNet.PlatformAbstractions.dll", + "HintPath": "C:\\Users\\admin\\.nuget\\packages\\microsoft.dotnet.platformabstractions\\2.0.4\\lib\\netstandard1.3\\Microsoft.DotNet.PlatformAbstractions.dll", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "NuGetPackageVersion": "2.0.4", + "Private": "false", + "Version": "", + "ExternallyResolved": "true", + "NuGetPackageId": "Microsoft.DotNet.PlatformAbstractions", + "ResolvedFrom": "{HintPathFromItem}", + "ImageRuntime": "v4.0.30319", + "CopyLocal": "false", + "OriginalItemSpec": "C:\\Users\\admin\\.nuget\\packages\\microsoft.dotnet.platformabstractions\\2.0.4\\lib\\netstandard1.3\\Microsoft.DotNet.PlatformAbstractions.dll", + "FusionName": "Microsoft.DotNet.PlatformAbstractions, Version=2.0.4.0, Culture=neutral, PublicKeyToken=adb9793829ddae60", + "PathInPackage": "lib/netstandard1.3/Microsoft.DotNet.PlatformAbstractions.dll", + "NuGetSourceType": "Package", + "FullPath": "C:\\Users\\admin\\.nuget\\packages\\microsoft.dotnet.platformabstractions\\2.0.4\\lib\\netstandard1.3\\Microsoft.DotNet.PlatformAbstractions.dll", + "RootDir": "C:\\", + "Filename": "Microsoft.DotNet.PlatformAbstractions", + "Extension": ".dll", + "RelativeDir": "C:\\Users\\admin\\.nuget\\packages\\microsoft.dotnet.platformabstractions\\2.0.4\\lib\\netstandard1.3\\", + "Directory": "Users\\admin\\.nuget\\packages\\microsoft.dotnet.platformabstractions\\2.0.4\\lib\\netstandard1.3\\", + "RecursiveDir": "", + "ModifiedTime": "2017-11-22 18:48:10.0000000", + "CreatedTime": "2026-03-27 13:02:04.1551171", + "AccessedTime": "2026-06-09 15:08:48.6333171", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "C:\\Users\\admin\\.nuget\\packages\\microsoft.extensions.dependencymodel\\2.0.4\\lib\\netstandard1.6\\Microsoft.Extensions.DependencyModel.dll", + "HintPath": "C:\\Users\\admin\\.nuget\\packages\\microsoft.extensions.dependencymodel\\2.0.4\\lib\\netstandard1.6\\Microsoft.Extensions.DependencyModel.dll", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "NuGetPackageVersion": "2.0.4", + "Private": "false", + "Version": "", + "ExternallyResolved": "true", + "NuGetPackageId": "Microsoft.Extensions.DependencyModel", + "ResolvedFrom": "{HintPathFromItem}", + "ImageRuntime": "v4.0.30319", + "CopyLocal": "false", + "OriginalItemSpec": "C:\\Users\\admin\\.nuget\\packages\\microsoft.extensions.dependencymodel\\2.0.4\\lib\\netstandard1.6\\Microsoft.Extensions.DependencyModel.dll", + "FusionName": "Microsoft.Extensions.DependencyModel, Version=2.0.4.0, Culture=neutral, PublicKeyToken=adb9793829ddae60", + "PathInPackage": "lib/netstandard1.6/Microsoft.Extensions.DependencyModel.dll", + "NuGetSourceType": "Package", + "FullPath": "C:\\Users\\admin\\.nuget\\packages\\microsoft.extensions.dependencymodel\\2.0.4\\lib\\netstandard1.6\\Microsoft.Extensions.DependencyModel.dll", + "RootDir": "C:\\", + "Filename": "Microsoft.Extensions.DependencyModel", + "Extension": ".dll", + "RelativeDir": "C:\\Users\\admin\\.nuget\\packages\\microsoft.extensions.dependencymodel\\2.0.4\\lib\\netstandard1.6\\", + "Directory": "Users\\admin\\.nuget\\packages\\microsoft.extensions.dependencymodel\\2.0.4\\lib\\netstandard1.6\\", + "RecursiveDir": "", + "ModifiedTime": "2017-11-22 18:48:14.0000000", + "CreatedTime": "2026-03-27 13:01:57.0997239", + "AccessedTime": "2026-06-09 15:08:48.6303161", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "C:\\Users\\admin\\.nuget\\packages\\microsoft.extensions.platformabstractions\\1.1.0\\lib\\netstandard1.3\\Microsoft.Extensions.PlatformAbstractions.dll", + "HintPath": "C:\\Users\\admin\\.nuget\\packages\\microsoft.extensions.platformabstractions\\1.1.0\\lib\\netstandard1.3\\Microsoft.Extensions.PlatformAbstractions.dll", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "NuGetPackageVersion": "1.1.0", + "Private": "false", + "Version": "", + "ExternallyResolved": "true", + "NuGetPackageId": "Microsoft.Extensions.PlatformAbstractions", + "ResolvedFrom": "{HintPathFromItem}", + "ImageRuntime": "v4.0.30319", + "CopyLocal": "false", + "OriginalItemSpec": "C:\\Users\\admin\\.nuget\\packages\\microsoft.extensions.platformabstractions\\1.1.0\\lib\\netstandard1.3\\Microsoft.Extensions.PlatformAbstractions.dll", + "FusionName": "Microsoft.Extensions.PlatformAbstractions, Version=1.1.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60", + "PathInPackage": "lib/netstandard1.3/Microsoft.Extensions.PlatformAbstractions.dll", + "NuGetSourceType": "Package", + "FullPath": "C:\\Users\\admin\\.nuget\\packages\\microsoft.extensions.platformabstractions\\1.1.0\\lib\\netstandard1.3\\Microsoft.Extensions.PlatformAbstractions.dll", + "RootDir": "C:\\", + "Filename": "Microsoft.Extensions.PlatformAbstractions", + "Extension": ".dll", + "RelativeDir": "C:\\Users\\admin\\.nuget\\packages\\microsoft.extensions.platformabstractions\\1.1.0\\lib\\netstandard1.3\\", + "Directory": "Users\\admin\\.nuget\\packages\\microsoft.extensions.platformabstractions\\1.1.0\\lib\\netstandard1.3\\", + "RecursiveDir": "", + "ModifiedTime": "2016-11-15 04:41:30.0000000", + "CreatedTime": "2026-03-27 13:02:12.1186357", + "AccessedTime": "2026-06-09 15:17:14.7784324", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\Microsoft.VisualBasic.Core.dll", + "FileVersion": "13.0.2726.22922", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "AssemblyName": "Microsoft.VisualBasic.Core", + "NuGetPackageVersion": "8.0.27", + "Private": "false", + "PublicKeyToken": "b03f5f7f11d50a3a", + "Version": "", + "FrameworkReferenceVersion": "8.0.27", + "ExternallyResolved": "true", + "NuGetPackageId": "Microsoft.NETCore.App.Ref", + "ResolvedFrom": "{RawFileName}", + "FrameworkReferenceName": "Microsoft.NETCore.App", + "ImageRuntime": "v4.0.30319", + "AssemblyVersion": "13.0.0.0", + "CopyLocal": "false", + "OriginalItemSpec": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref/net8.0/Microsoft.VisualBasic.Core.dll", + "FusionName": "Microsoft.VisualBasic.Core, Version=13.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", + "FullPath": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\Microsoft.VisualBasic.Core.dll", + "RootDir": "C:\\", + "Filename": "Microsoft.VisualBasic.Core", + "Extension": ".dll", + "RelativeDir": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "Directory": "Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "RecursiveDir": "", + "ModifiedTime": "2026-04-30 07:38:58.0000000", + "CreatedTime": "2026-05-29 17:24:44.9403607", + "AccessedTime": "2026-06-09 15:16:47.0139693", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "C:\\Users\\admin\\.nuget\\packages\\microsoft.windowsdesktop.app.ref\\8.0.27\\ref\\net8.0\\Microsoft.VisualBasic.dll", + "FileVersion": "8.0.2726.23001", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "AssemblyName": "Microsoft.VisualBasic", + "NuGetPackageVersion": "8.0.27", + "Private": "false", + "PublicKeyToken": "b03f5f7f11d50a3a", + "Version": "", + "FrameworkReferenceVersion": "8.0.27", + "ExternallyResolved": "true", + "NuGetPackageId": "Microsoft.WindowsDesktop.App.Ref", + "ResolvedFrom": "{RawFileName}", + "FrameworkReferenceName": "Microsoft.WindowsDesktop.App.WindowsForms", + "ImageRuntime": "v4.0.30319", + "AssemblyVersion": "10.1.0.0", + "CopyLocal": "false", + "OriginalItemSpec": "C:\\Users\\admin\\.nuget\\packages\\microsoft.windowsdesktop.app.ref\\8.0.27\\ref/net8.0/Microsoft.VisualBasic.dll", + "FusionName": "Microsoft.VisualBasic, Version=10.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", + "FullPath": "C:\\Users\\admin\\.nuget\\packages\\microsoft.windowsdesktop.app.ref\\8.0.27\\ref\\net8.0\\Microsoft.VisualBasic.dll", + "RootDir": "C:\\", + "Filename": "Microsoft.VisualBasic", + "Extension": ".dll", + "RelativeDir": "C:\\Users\\admin\\.nuget\\packages\\microsoft.windowsdesktop.app.ref\\8.0.27\\ref\\net8.0\\", + "Directory": "Users\\admin\\.nuget\\packages\\microsoft.windowsdesktop.app.ref\\8.0.27\\ref\\net8.0\\", + "RecursiveDir": "", + "ModifiedTime": "2026-04-30 15:26:16.0000000", + "CreatedTime": "2026-05-29 17:24:43.9922035", + "AccessedTime": "2026-06-09 15:08:48.6115805", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "C:\\Users\\admin\\.nuget\\packages\\microsoft.windowsdesktop.app.ref\\8.0.27\\ref\\net8.0\\Microsoft.VisualBasic.Forms.dll", + "FileVersion": "8.0.2726.23001", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "AssemblyName": "Microsoft.VisualBasic.Forms", + "NuGetPackageVersion": "8.0.27", + "Private": "false", + "PublicKeyToken": "b03f5f7f11d50a3a", + "Version": "", + "FrameworkReferenceVersion": "8.0.27", + "ExternallyResolved": "true", + "NuGetPackageId": "Microsoft.WindowsDesktop.App.Ref", + "ResolvedFrom": "{RawFileName}", + "FrameworkReferenceName": "Microsoft.WindowsDesktop.App.WindowsForms", + "ImageRuntime": "v4.0.30319", + "AssemblyVersion": "8.0.0.0", + "CopyLocal": "false", + "OriginalItemSpec": "C:\\Users\\admin\\.nuget\\packages\\microsoft.windowsdesktop.app.ref\\8.0.27\\ref/net8.0/Microsoft.VisualBasic.Forms.dll", + "FusionName": "Microsoft.VisualBasic.Forms, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", + "FullPath": "C:\\Users\\admin\\.nuget\\packages\\microsoft.windowsdesktop.app.ref\\8.0.27\\ref\\net8.0\\Microsoft.VisualBasic.Forms.dll", + "RootDir": "C:\\", + "Filename": "Microsoft.VisualBasic.Forms", + "Extension": ".dll", + "RelativeDir": "C:\\Users\\admin\\.nuget\\packages\\microsoft.windowsdesktop.app.ref\\8.0.27\\ref\\net8.0\\", + "Directory": "Users\\admin\\.nuget\\packages\\microsoft.windowsdesktop.app.ref\\8.0.27\\ref\\net8.0\\", + "RecursiveDir": "", + "ModifiedTime": "2026-04-30 15:26:10.0000000", + "CreatedTime": "2026-05-29 17:24:44.0159203", + "AccessedTime": "2026-06-09 15:08:48.6054642", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\Microsoft.Win32.Primitives.dll", + "FileVersion": "8.0.2726.22922", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "AssemblyName": "Microsoft.Win32.Primitives", + "NuGetPackageVersion": "8.0.27", + "Private": "false", + "PublicKeyToken": "b03f5f7f11d50a3a", + "Version": "", + "FrameworkReferenceVersion": "8.0.27", + "ExternallyResolved": "true", + "NuGetPackageId": "Microsoft.NETCore.App.Ref", + "ResolvedFrom": "{RawFileName}", + "FrameworkReferenceName": "Microsoft.NETCore.App", + "ImageRuntime": "v4.0.30319", + "AssemblyVersion": "8.0.0.0", + "CopyLocal": "false", + "OriginalItemSpec": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref/net8.0/Microsoft.Win32.Primitives.dll", + "FusionName": "Microsoft.Win32.Primitives, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", + "FullPath": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\Microsoft.Win32.Primitives.dll", + "RootDir": "C:\\", + "Filename": "Microsoft.Win32.Primitives", + "Extension": ".dll", + "RelativeDir": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "Directory": "Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "RecursiveDir": "", + "ModifiedTime": "2026-04-30 07:38:56.0000000", + "CreatedTime": "2026-05-29 17:24:44.9593216", + "AccessedTime": "2026-06-09 15:16:47.0219734", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "C:\\Users\\admin\\.nuget\\packages\\microsoft.windowsdesktop.app.ref\\8.0.27\\ref\\net8.0\\Microsoft.Win32.Registry.AccessControl.dll", + "FileVersion": "8.0.2726.22922", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "AssemblyName": "Microsoft.Win32.Registry.AccessControl", + "NuGetPackageVersion": "8.0.27", + "Private": "false", + "PublicKeyToken": "b03f5f7f11d50a3a", + "Version": "", + "FrameworkReferenceVersion": "8.0.27", + "ExternallyResolved": "true", + "NuGetPackageId": "Microsoft.WindowsDesktop.App.Ref", + "ResolvedFrom": "{RawFileName}", + "FrameworkReferenceName": "Microsoft.WindowsDesktop.App.WindowsForms", + "ImageRuntime": "v4.0.30319", + "AssemblyVersion": "8.0.0.0", + "CopyLocal": "false", + "OriginalItemSpec": "C:\\Users\\admin\\.nuget\\packages\\microsoft.windowsdesktop.app.ref\\8.0.27\\ref/net8.0/Microsoft.Win32.Registry.AccessControl.dll", + "FusionName": "Microsoft.Win32.Registry.AccessControl, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", + "FullPath": "C:\\Users\\admin\\.nuget\\packages\\microsoft.windowsdesktop.app.ref\\8.0.27\\ref\\net8.0\\Microsoft.Win32.Registry.AccessControl.dll", + "RootDir": "C:\\", + "Filename": "Microsoft.Win32.Registry.AccessControl", + "Extension": ".dll", + "RelativeDir": "C:\\Users\\admin\\.nuget\\packages\\microsoft.windowsdesktop.app.ref\\8.0.27\\ref\\net8.0\\", + "Directory": "Users\\admin\\.nuget\\packages\\microsoft.windowsdesktop.app.ref\\8.0.27\\ref\\net8.0\\", + "RecursiveDir": "", + "ModifiedTime": "2026-04-30 07:27:28.0000000", + "CreatedTime": "2026-05-29 17:24:44.0333752", + "AccessedTime": "2026-06-09 15:08:48.5898654", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\Microsoft.Win32.Registry.dll", + "FileVersion": "8.0.2726.22922", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "AssemblyName": "Microsoft.Win32.Registry", + "NuGetPackageVersion": "8.0.27", + "Private": "false", + "PublicKeyToken": "b03f5f7f11d50a3a", + "Version": "", + "FrameworkReferenceVersion": "8.0.27", + "ExternallyResolved": "true", + "NuGetPackageId": "Microsoft.NETCore.App.Ref", + "ResolvedFrom": "{RawFileName}", + "FrameworkReferenceName": "Microsoft.NETCore.App", + "ImageRuntime": "v4.0.30319", + "AssemblyVersion": "8.0.0.0", + "CopyLocal": "false", + "OriginalItemSpec": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref/net8.0/Microsoft.Win32.Registry.dll", + "FusionName": "Microsoft.Win32.Registry, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", + "FullPath": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\Microsoft.Win32.Registry.dll", + "RootDir": "C:\\", + "Filename": "Microsoft.Win32.Registry", + "Extension": ".dll", + "RelativeDir": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "Directory": "Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "RecursiveDir": "", + "ModifiedTime": "2026-04-30 07:39:02.0000000", + "CreatedTime": "2026-05-29 17:24:44.9663236", + "AccessedTime": "2026-06-09 15:16:47.0259711", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "C:\\Users\\admin\\.nuget\\packages\\microsoft.windowsdesktop.app.ref\\8.0.27\\ref\\net8.0\\Microsoft.Win32.SystemEvents.dll", + "FileVersion": "8.0.2726.22922", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "AssemblyName": "Microsoft.Win32.SystemEvents", + "NuGetPackageVersion": "8.0.27", + "Private": "false", + "PublicKeyToken": "cc7b13ffcd2ddd51", + "Version": "", + "FrameworkReferenceVersion": "8.0.27", + "ExternallyResolved": "true", + "NuGetPackageId": "Microsoft.WindowsDesktop.App.Ref", + "ResolvedFrom": "{RawFileName}", + "FrameworkReferenceName": "Microsoft.WindowsDesktop.App.WindowsForms", + "ImageRuntime": "v4.0.30319", + "AssemblyVersion": "8.0.0.0", + "CopyLocal": "false", + "OriginalItemSpec": "C:\\Users\\admin\\.nuget\\packages\\microsoft.windowsdesktop.app.ref\\8.0.27\\ref/net8.0/Microsoft.Win32.SystemEvents.dll", + "FusionName": "Microsoft.Win32.SystemEvents, Version=8.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51", + "FullPath": "C:\\Users\\admin\\.nuget\\packages\\microsoft.windowsdesktop.app.ref\\8.0.27\\ref\\net8.0\\Microsoft.Win32.SystemEvents.dll", + "RootDir": "C:\\", + "Filename": "Microsoft.Win32.SystemEvents", + "Extension": ".dll", + "RelativeDir": "C:\\Users\\admin\\.nuget\\packages\\microsoft.windowsdesktop.app.ref\\8.0.27\\ref\\net8.0\\", + "Directory": "Users\\admin\\.nuget\\packages\\microsoft.windowsdesktop.app.ref\\8.0.27\\ref\\net8.0\\", + "RecursiveDir": "", + "ModifiedTime": "2026-04-30 07:27:28.0000000", + "CreatedTime": "2026-05-29 17:24:44.0496287", + "AccessedTime": "2026-06-09 15:08:48.5736331", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "C:\\Users\\admin\\.nuget\\packages\\mqttnet\\4.3.6.1152\\lib\\net7.0\\MQTTnet.dll", + "HintPath": "C:\\Users\\admin\\.nuget\\packages\\mqttnet\\4.3.6.1152\\lib\\net7.0\\MQTTnet.dll", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "NuGetPackageVersion": "4.3.6.1152", + "Private": "false", + "Version": "", + "ExternallyResolved": "true", + "NuGetPackageId": "MQTTnet", + "ResolvedFrom": "{HintPathFromItem}", + "ImageRuntime": "v4.0.30319", + "CopyLocal": "false", + "OriginalItemSpec": "C:\\Users\\admin\\.nuget\\packages\\mqttnet\\4.3.6.1152\\lib\\net7.0\\MQTTnet.dll", + "FusionName": "MQTTnet, Version=4.3.6.1152, Culture=neutral, PublicKeyToken=fdb7629f2e364a63", + "PathInPackage": "lib/net7.0/MQTTnet.dll", + "NuGetSourceType": "Package", + "FullPath": "C:\\Users\\admin\\.nuget\\packages\\mqttnet\\4.3.6.1152\\lib\\net7.0\\MQTTnet.dll", + "RootDir": "C:\\", + "Filename": "MQTTnet", + "Extension": ".dll", + "RelativeDir": "C:\\Users\\admin\\.nuget\\packages\\mqttnet\\4.3.6.1152\\lib\\net7.0\\", + "Directory": "Users\\admin\\.nuget\\packages\\mqttnet\\4.3.6.1152\\lib\\net7.0\\", + "RecursiveDir": "", + "ModifiedTime": "2024-05-24 01:39:56.0000000", + "CreatedTime": "2026-03-24 15:22:34.2626409", + "AccessedTime": "2026-06-09 15:08:48.5638462", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "C:\\Users\\admin\\.nuget\\packages\\mqttnet.extensions.managedclient\\4.3.6.1152\\lib\\net7.0\\MQTTnet.Extensions.ManagedClient.dll", + "HintPath": "C:\\Users\\admin\\.nuget\\packages\\mqttnet.extensions.managedclient\\4.3.6.1152\\lib\\net7.0\\MQTTnet.Extensions.ManagedClient.dll", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "NuGetPackageVersion": "4.3.6.1152", + "Private": "false", + "Version": "", + "ExternallyResolved": "true", + "NuGetPackageId": "MQTTnet.Extensions.ManagedClient", + "ResolvedFrom": "{HintPathFromItem}", + "ImageRuntime": "v4.0.30319", + "CopyLocal": "false", + "OriginalItemSpec": "C:\\Users\\admin\\.nuget\\packages\\mqttnet.extensions.managedclient\\4.3.6.1152\\lib\\net7.0\\MQTTnet.Extensions.ManagedClient.dll", + "FusionName": "MQTTnet.Extensions.ManagedClient, Version=4.3.6.1152, Culture=neutral, PublicKeyToken=fdb7629f2e364a63", + "PathInPackage": "lib/net7.0/MQTTnet.Extensions.ManagedClient.dll", + "NuGetSourceType": "Package", + "FullPath": "C:\\Users\\admin\\.nuget\\packages\\mqttnet.extensions.managedclient\\4.3.6.1152\\lib\\net7.0\\MQTTnet.Extensions.ManagedClient.dll", + "RootDir": "C:\\", + "Filename": "MQTTnet.Extensions.ManagedClient", + "Extension": ".dll", + "RelativeDir": "C:\\Users\\admin\\.nuget\\packages\\mqttnet.extensions.managedclient\\4.3.6.1152\\lib\\net7.0\\", + "Directory": "Users\\admin\\.nuget\\packages\\mqttnet.extensions.managedclient\\4.3.6.1152\\lib\\net7.0\\", + "RecursiveDir": "", + "ModifiedTime": "2024-05-24 01:40:16.0000000", + "CreatedTime": "2026-03-24 15:22:34.3941931", + "AccessedTime": "2026-06-09 15:08:48.5598426", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\mscorlib.dll", + "FileVersion": "8.0.2726.22922", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "AssemblyName": "mscorlib", + "NuGetPackageVersion": "8.0.27", + "Private": "false", + "PublicKeyToken": "b77a5c561934e089", + "Version": "", + "FrameworkReferenceVersion": "8.0.27", + "ExternallyResolved": "true", + "NuGetPackageId": "Microsoft.NETCore.App.Ref", + "ResolvedFrom": "{RawFileName}", + "FrameworkReferenceName": "Microsoft.NETCore.App", + "ImageRuntime": "v4.0.30319", + "AssemblyVersion": "4.0.0.0", + "CopyLocal": "false", + "OriginalItemSpec": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref/net8.0/mscorlib.dll", + "FusionName": "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", + "FullPath": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\mscorlib.dll", + "RootDir": "C:\\", + "Filename": "mscorlib", + "Extension": ".dll", + "RelativeDir": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "Directory": "Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "RecursiveDir": "", + "ModifiedTime": "2026-04-30 07:43:18.0000000", + "CreatedTime": "2026-05-29 17:24:44.9757454", + "AccessedTime": "2026-06-09 15:16:47.0390744", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "C:\\Users\\admin\\.nuget\\packages\\nancy\\2.0.0\\lib\\netstandard2.0\\Nancy.dll", + "HintPath": "C:\\Users\\admin\\.nuget\\packages\\nancy\\2.0.0\\lib\\netstandard2.0\\Nancy.dll", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "NuGetPackageVersion": "2.0.0", + "Private": "false", + "Version": "", + "ExternallyResolved": "true", + "NuGetPackageId": "Nancy", + "ResolvedFrom": "{HintPathFromItem}", + "ImageRuntime": "v4.0.30319", + "CopyLocal": "false", + "OriginalItemSpec": "C:\\Users\\admin\\.nuget\\packages\\nancy\\2.0.0\\lib\\netstandard2.0\\Nancy.dll", + "FusionName": "Nancy, Version=2.0.0.0, Culture=neutral, PublicKeyToken=null", + "PathInPackage": "lib/netstandard2.0/Nancy.dll", + "NuGetSourceType": "Package", + "FullPath": "C:\\Users\\admin\\.nuget\\packages\\nancy\\2.0.0\\lib\\netstandard2.0\\Nancy.dll", + "RootDir": "C:\\", + "Filename": "Nancy", + "Extension": ".dll", + "RelativeDir": "C:\\Users\\admin\\.nuget\\packages\\nancy\\2.0.0\\lib\\netstandard2.0\\", + "Directory": "Users\\admin\\.nuget\\packages\\nancy\\2.0.0\\lib\\netstandard2.0\\", + "RecursiveDir": "", + "ModifiedTime": "2019-04-27 18:54:46.0000000", + "CreatedTime": "2026-03-27 13:01:57.7029131", + "AccessedTime": "2026-06-09 15:17:14.9088276", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\netstandard.dll", + "FileVersion": "8.0.2726.22922", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "AssemblyName": "netstandard", + "NuGetPackageVersion": "8.0.27", + "Private": "false", + "PublicKeyToken": "cc7b13ffcd2ddd51", + "Version": "", + "FrameworkReferenceVersion": "8.0.27", + "ExternallyResolved": "true", + "NuGetPackageId": "Microsoft.NETCore.App.Ref", + "ResolvedFrom": "{RawFileName}", + "FrameworkReferenceName": "Microsoft.NETCore.App", + "ImageRuntime": "v4.0.30319", + "AssemblyVersion": "2.1.0.0", + "CopyLocal": "false", + "OriginalItemSpec": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref/net8.0/netstandard.dll", + "FusionName": "netstandard, Version=2.1.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51", + "FullPath": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\netstandard.dll", + "RootDir": "C:\\", + "Filename": "netstandard", + "Extension": ".dll", + "RelativeDir": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "Directory": "Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "RecursiveDir": "", + "ModifiedTime": "2026-04-30 07:43:18.0000000", + "CreatedTime": "2026-05-29 17:24:44.9827448", + "AccessedTime": "2026-06-09 15:16:47.0514710", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "C:\\Users\\admin\\.nuget\\packages\\newtonsoft.json\\13.0.4\\lib\\net6.0\\Newtonsoft.Json.dll", + "HintPath": "C:\\Users\\admin\\.nuget\\packages\\newtonsoft.json\\13.0.4\\lib\\net6.0\\Newtonsoft.Json.dll", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "NuGetPackageVersion": "13.0.4", + "Private": "false", + "Version": "", + "ExternallyResolved": "true", + "NuGetPackageId": "Newtonsoft.Json", + "ResolvedFrom": "{HintPathFromItem}", + "ImageRuntime": "v4.0.30319", + "CopyLocal": "false", + "OriginalItemSpec": "C:\\Users\\admin\\.nuget\\packages\\newtonsoft.json\\13.0.4\\lib\\net6.0\\Newtonsoft.Json.dll", + "FusionName": "Newtonsoft.Json, Version=13.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed", + "PathInPackage": "lib/net6.0/Newtonsoft.Json.dll", + "NuGetSourceType": "Package", + "FullPath": "C:\\Users\\admin\\.nuget\\packages\\newtonsoft.json\\13.0.4\\lib\\net6.0\\Newtonsoft.Json.dll", + "RootDir": "C:\\", + "Filename": "Newtonsoft.Json", + "Extension": ".dll", + "RelativeDir": "C:\\Users\\admin\\.nuget\\packages\\newtonsoft.json\\13.0.4\\lib\\net6.0\\", + "Directory": "Users\\admin\\.nuget\\packages\\newtonsoft.json\\13.0.4\\lib\\net6.0\\", + "RecursiveDir": "", + "ModifiedTime": "2025-09-16 16:04:24.0000000", + "CreatedTime": "2026-04-02 15:34:44.8258942", + "AccessedTime": "2026-06-09 15:08:48.5298457", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "E:\\Work\\Core\\Simple-FR\\Simple\\SimpleCore\\bin\\Debug\\netstandard2.0\\SimpleCore.dll", + "HintPath": "E:\\Work\\Core\\Simple-FR\\Simple\\SimpleCore\\bin\\Debug\\netstandard2.0\\SimpleCore.dll", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "Version": "", + "ResolvedFrom": "{HintPathFromItem}", + "ImageRuntime": "v4.0.30319", + "CopyLocal": "true", + "OriginalItemSpec": "SimpleCore", + "FusionName": "SimpleCore, Version=0.3.65.5811, Culture=neutral, PublicKeyToken=null", + "FullPath": "E:\\Work\\Core\\Simple-FR\\Simple\\SimpleCore\\bin\\Debug\\netstandard2.0\\SimpleCore.dll", + "RootDir": "E:\\", + "Filename": "SimpleCore", + "Extension": ".dll", + "RelativeDir": "E:\\Work\\Core\\Simple-FR\\Simple\\SimpleCore\\bin\\Debug\\netstandard2.0\\", + "Directory": "Work\\Core\\Simple-FR\\Simple\\SimpleCore\\bin\\Debug\\netstandard2.0\\", + "RecursiveDir": "", + "ModifiedTime": "2026-06-09 14:31:41.9497899", + "CreatedTime": "2026-06-09 14:31:44.1469646", + "AccessedTime": "2026-06-09 15:37:16.2649185", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "E:\\Work\\Core\\Simple-FR\\Simple\\SimpleLite\\bin\\Debug\\SimpleLite.dll", + "HintPath": "E:\\Work\\Core\\Simple-FR\\Simple\\SimpleLite\\bin\\Debug\\SimpleLite.dll", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "Version": "", + "ResolvedFrom": "{HintPathFromItem}", + "ImageRuntime": "v4.0.30319", + "CopyLocal": "true", + "OriginalItemSpec": "SimpleLite", + "FusionName": "SimpleLite, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null", + "FullPath": "E:\\Work\\Core\\Simple-FR\\Simple\\SimpleLite\\bin\\Debug\\SimpleLite.dll", + "RootDir": "E:\\", + "Filename": "SimpleLite", + "Extension": ".dll", + "RelativeDir": "E:\\Work\\Core\\Simple-FR\\Simple\\SimpleLite\\bin\\Debug\\", + "Directory": "Work\\Core\\Simple-FR\\Simple\\SimpleLite\\bin\\Debug\\", + "RecursiveDir": "", + "ModifiedTime": "2026-06-09 15:17:15.9896902", + "CreatedTime": "2026-06-09 14:32:28.3499878", + "AccessedTime": "2026-06-09 15:35:36.5902161", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.AppContext.dll", + "FileVersion": "8.0.2726.22922", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "AssemblyName": "System.AppContext", + "NuGetPackageVersion": "8.0.27", + "Private": "false", + "PublicKeyToken": "b03f5f7f11d50a3a", + "Version": "", + "FrameworkReferenceVersion": "8.0.27", + "ExternallyResolved": "true", + "NuGetPackageId": "Microsoft.NETCore.App.Ref", + "ResolvedFrom": "{RawFileName}", + "FrameworkReferenceName": "Microsoft.NETCore.App", + "ImageRuntime": "v4.0.30319", + "AssemblyVersion": "8.0.0.0", + "CopyLocal": "false", + "OriginalItemSpec": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref/net8.0/System.AppContext.dll", + "FusionName": "System.AppContext, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", + "FullPath": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.AppContext.dll", + "RootDir": "C:\\", + "Filename": "System.AppContext", + "Extension": ".dll", + "RelativeDir": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "Directory": "Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "RecursiveDir": "", + "ModifiedTime": "2026-04-30 07:42:24.0000000", + "CreatedTime": "2026-05-29 17:24:44.9920863", + "AccessedTime": "2026-06-09 15:16:47.1079268", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Buffers.dll", + "FileVersion": "8.0.2726.22922", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "AssemblyName": "System.Buffers", + "NuGetPackageVersion": "8.0.27", + "Private": "false", + "PublicKeyToken": "cc7b13ffcd2ddd51", + "Version": "", + "FrameworkReferenceVersion": "8.0.27", + "ExternallyResolved": "true", + "NuGetPackageId": "Microsoft.NETCore.App.Ref", + "ResolvedFrom": "{RawFileName}", + "FrameworkReferenceName": "Microsoft.NETCore.App", + "ImageRuntime": "v4.0.30319", + "AssemblyVersion": "8.0.0.0", + "CopyLocal": "false", + "OriginalItemSpec": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref/net8.0/System.Buffers.dll", + "FusionName": "System.Buffers, Version=8.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51", + "FullPath": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Buffers.dll", + "RootDir": "C:\\", + "Filename": "System.Buffers", + "Extension": ".dll", + "RelativeDir": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "Directory": "Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "RecursiveDir": "", + "ModifiedTime": "2026-04-30 07:42:24.0000000", + "CreatedTime": "2026-05-29 17:24:45.0023134", + "AccessedTime": "2026-06-09 15:16:47.1106528", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "C:\\Users\\admin\\.nuget\\packages\\microsoft.windowsdesktop.app.ref\\8.0.27\\ref\\net8.0\\System.CodeDom.dll", + "FileVersion": "8.0.2726.22922", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "AssemblyName": "System.CodeDom", + "NuGetPackageVersion": "8.0.27", + "Private": "false", + "PublicKeyToken": "cc7b13ffcd2ddd51", + "Version": "", + "FrameworkReferenceVersion": "8.0.27", + "ExternallyResolved": "true", + "NuGetPackageId": "Microsoft.WindowsDesktop.App.Ref", + "ResolvedFrom": "{RawFileName}", + "FrameworkReferenceName": "Microsoft.WindowsDesktop.App.WindowsForms", + "ImageRuntime": "v4.0.30319", + "AssemblyVersion": "8.0.0.0", + "CopyLocal": "false", + "OriginalItemSpec": "C:\\Users\\admin\\.nuget\\packages\\microsoft.windowsdesktop.app.ref\\8.0.27\\ref/net8.0/System.CodeDom.dll", + "FusionName": "System.CodeDom, Version=8.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51", + "FullPath": "C:\\Users\\admin\\.nuget\\packages\\microsoft.windowsdesktop.app.ref\\8.0.27\\ref\\net8.0\\System.CodeDom.dll", + "RootDir": "C:\\", + "Filename": "System.CodeDom", + "Extension": ".dll", + "RelativeDir": "C:\\Users\\admin\\.nuget\\packages\\microsoft.windowsdesktop.app.ref\\8.0.27\\ref\\net8.0\\", + "Directory": "Users\\admin\\.nuget\\packages\\microsoft.windowsdesktop.app.ref\\8.0.27\\ref\\net8.0\\", + "RecursiveDir": "", + "ModifiedTime": "2026-04-30 07:26:38.0000000", + "CreatedTime": "2026-05-29 17:24:44.3781064", + "AccessedTime": "2026-06-09 15:08:48.5098435", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Collections.Concurrent.dll", + "FileVersion": "8.0.2726.22922", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "AssemblyName": "System.Collections.Concurrent", + "NuGetPackageVersion": "8.0.27", + "Private": "false", + "PublicKeyToken": "b03f5f7f11d50a3a", + "Version": "", + "FrameworkReferenceVersion": "8.0.27", + "ExternallyResolved": "true", + "NuGetPackageId": "Microsoft.NETCore.App.Ref", + "ResolvedFrom": "{RawFileName}", + "FrameworkReferenceName": "Microsoft.NETCore.App", + "ImageRuntime": "v4.0.30319", + "AssemblyVersion": "8.0.0.0", + "CopyLocal": "false", + "OriginalItemSpec": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref/net8.0/System.Collections.Concurrent.dll", + "FusionName": "System.Collections.Concurrent, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", + "FullPath": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Collections.Concurrent.dll", + "RootDir": "C:\\", + "Filename": "System.Collections.Concurrent", + "Extension": ".dll", + "RelativeDir": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "Directory": "Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "RecursiveDir": "", + "ModifiedTime": "2026-04-30 07:38:56.0000000", + "CreatedTime": "2026-05-29 17:24:45.0108641", + "AccessedTime": "2026-06-09 15:16:47.1136488", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Collections.dll", + "FileVersion": "8.0.2726.22922", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "AssemblyName": "System.Collections", + "NuGetPackageVersion": "8.0.27", + "Private": "false", + "PublicKeyToken": "b03f5f7f11d50a3a", + "Version": "", + "FrameworkReferenceVersion": "8.0.27", + "ExternallyResolved": "true", + "NuGetPackageId": "Microsoft.NETCore.App.Ref", + "ResolvedFrom": "{RawFileName}", + "FrameworkReferenceName": "Microsoft.NETCore.App", + "ImageRuntime": "v4.0.30319", + "AssemblyVersion": "8.0.0.0", + "CopyLocal": "false", + "OriginalItemSpec": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref/net8.0/System.Collections.dll", + "FusionName": "System.Collections, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", + "FullPath": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Collections.dll", + "RootDir": "C:\\", + "Filename": "System.Collections", + "Extension": ".dll", + "RelativeDir": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "Directory": "Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "RecursiveDir": "", + "ModifiedTime": "2026-04-30 07:38:56.0000000", + "CreatedTime": "2026-05-29 17:24:45.0204939", + "AccessedTime": "2026-06-09 15:16:47.1176519", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Collections.Immutable.dll", + "FileVersion": "8.0.2726.22922", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "AssemblyName": "System.Collections.Immutable", + "NuGetPackageVersion": "8.0.27", + "Private": "false", + "PublicKeyToken": "b03f5f7f11d50a3a", + "Version": "", + "FrameworkReferenceVersion": "8.0.27", + "ExternallyResolved": "true", + "NuGetPackageId": "Microsoft.NETCore.App.Ref", + "ResolvedFrom": "{RawFileName}", + "FrameworkReferenceName": "Microsoft.NETCore.App", + "ImageRuntime": "v4.0.30319", + "AssemblyVersion": "8.0.0.0", + "CopyLocal": "false", + "OriginalItemSpec": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref/net8.0/System.Collections.Immutable.dll", + "FusionName": "System.Collections.Immutable, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", + "FullPath": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Collections.Immutable.dll", + "RootDir": "C:\\", + "Filename": "System.Collections.Immutable", + "Extension": ".dll", + "RelativeDir": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "Directory": "Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "RecursiveDir": "", + "ModifiedTime": "2026-04-30 07:39:04.0000000", + "CreatedTime": "2026-05-29 17:24:45.0295074", + "AccessedTime": "2026-06-09 15:16:47.1231838", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Collections.NonGeneric.dll", + "FileVersion": "8.0.2726.22922", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "AssemblyName": "System.Collections.NonGeneric", + "NuGetPackageVersion": "8.0.27", + "Private": "false", + "PublicKeyToken": "b03f5f7f11d50a3a", + "Version": "", + "FrameworkReferenceVersion": "8.0.27", + "ExternallyResolved": "true", + "NuGetPackageId": "Microsoft.NETCore.App.Ref", + "ResolvedFrom": "{RawFileName}", + "FrameworkReferenceName": "Microsoft.NETCore.App", + "ImageRuntime": "v4.0.30319", + "AssemblyVersion": "8.0.0.0", + "CopyLocal": "false", + "OriginalItemSpec": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref/net8.0/System.Collections.NonGeneric.dll", + "FusionName": "System.Collections.NonGeneric, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", + "FullPath": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Collections.NonGeneric.dll", + "RootDir": "C:\\", + "Filename": "System.Collections.NonGeneric", + "Extension": ".dll", + "RelativeDir": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "Directory": "Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "RecursiveDir": "", + "ModifiedTime": "2026-04-30 07:38:56.0000000", + "CreatedTime": "2026-05-29 17:24:45.0374936", + "AccessedTime": "2026-06-09 15:16:47.1281860", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Collections.Specialized.dll", + "FileVersion": "8.0.2726.22922", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "AssemblyName": "System.Collections.Specialized", + "NuGetPackageVersion": "8.0.27", + "Private": "false", + "PublicKeyToken": "b03f5f7f11d50a3a", + "Version": "", + "FrameworkReferenceVersion": "8.0.27", + "ExternallyResolved": "true", + "NuGetPackageId": "Microsoft.NETCore.App.Ref", + "ResolvedFrom": "{RawFileName}", + "FrameworkReferenceName": "Microsoft.NETCore.App", + "ImageRuntime": "v4.0.30319", + "AssemblyVersion": "8.0.0.0", + "CopyLocal": "false", + "OriginalItemSpec": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref/net8.0/System.Collections.Specialized.dll", + "FusionName": "System.Collections.Specialized, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", + "FullPath": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Collections.Specialized.dll", + "RootDir": "C:\\", + "Filename": "System.Collections.Specialized", + "Extension": ".dll", + "RelativeDir": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "Directory": "Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "RecursiveDir": "", + "ModifiedTime": "2026-04-30 07:39:00.0000000", + "CreatedTime": "2026-05-29 17:24:45.0494977", + "AccessedTime": "2026-06-09 15:16:47.1311858", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.ComponentModel.Annotations.dll", + "FileVersion": "8.0.2726.22922", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "AssemblyName": "System.ComponentModel.Annotations", + "NuGetPackageVersion": "8.0.27", + "Private": "false", + "PublicKeyToken": "b03f5f7f11d50a3a", + "Version": "", + "FrameworkReferenceVersion": "8.0.27", + "ExternallyResolved": "true", + "NuGetPackageId": "Microsoft.NETCore.App.Ref", + "ResolvedFrom": "{RawFileName}", + "FrameworkReferenceName": "Microsoft.NETCore.App", + "ImageRuntime": "v4.0.30319", + "AssemblyVersion": "8.0.0.0", + "CopyLocal": "false", + "OriginalItemSpec": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref/net8.0/System.ComponentModel.Annotations.dll", + "FusionName": "System.ComponentModel.Annotations, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", + "FullPath": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.ComponentModel.Annotations.dll", + "RootDir": "C:\\", + "Filename": "System.ComponentModel.Annotations", + "Extension": ".dll", + "RelativeDir": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "Directory": "Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "RecursiveDir": "", + "ModifiedTime": "2026-04-30 07:39:02.0000000", + "CreatedTime": "2026-05-29 17:24:45.0568415", + "AccessedTime": "2026-06-09 15:16:47.1354693", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.ComponentModel.DataAnnotations.dll", + "FileVersion": "8.0.2726.22922", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "AssemblyName": "System.ComponentModel.DataAnnotations", + "NuGetPackageVersion": "8.0.27", + "Private": "false", + "PublicKeyToken": "31bf3856ad364e35", + "Version": "", + "FrameworkReferenceVersion": "8.0.27", + "ExternallyResolved": "true", + "NuGetPackageId": "Microsoft.NETCore.App.Ref", + "ResolvedFrom": "{RawFileName}", + "FrameworkReferenceName": "Microsoft.NETCore.App", + "ImageRuntime": "v4.0.30319", + "AssemblyVersion": "4.0.0.0", + "CopyLocal": "false", + "OriginalItemSpec": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref/net8.0/System.ComponentModel.DataAnnotations.dll", + "FusionName": "System.ComponentModel.DataAnnotations, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "FullPath": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.ComponentModel.DataAnnotations.dll", + "RootDir": "C:\\", + "Filename": "System.ComponentModel.DataAnnotations", + "Extension": ".dll", + "RelativeDir": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "Directory": "Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "RecursiveDir": "", + "ModifiedTime": "2026-04-30 07:42:34.0000000", + "CreatedTime": "2026-05-29 17:24:45.0638434", + "AccessedTime": "2026-06-09 15:16:47.1394785", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.ComponentModel.dll", + "FileVersion": "8.0.2726.22922", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "AssemblyName": "System.ComponentModel", + "NuGetPackageVersion": "8.0.27", + "Private": "false", + "PublicKeyToken": "b03f5f7f11d50a3a", + "Version": "", + "FrameworkReferenceVersion": "8.0.27", + "ExternallyResolved": "true", + "NuGetPackageId": "Microsoft.NETCore.App.Ref", + "ResolvedFrom": "{RawFileName}", + "FrameworkReferenceName": "Microsoft.NETCore.App", + "ImageRuntime": "v4.0.30319", + "AssemblyVersion": "8.0.0.0", + "CopyLocal": "false", + "OriginalItemSpec": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref/net8.0/System.ComponentModel.dll", + "FusionName": "System.ComponentModel, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", + "FullPath": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.ComponentModel.dll", + "RootDir": "C:\\", + "Filename": "System.ComponentModel", + "Extension": ".dll", + "RelativeDir": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "Directory": "Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "RecursiveDir": "", + "ModifiedTime": "2026-04-30 07:38:58.0000000", + "CreatedTime": "2026-05-29 17:24:45.0738437", + "AccessedTime": "2026-06-09 15:16:47.1454790", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.ComponentModel.EventBasedAsync.dll", + "FileVersion": "8.0.2726.22922", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "AssemblyName": "System.ComponentModel.EventBasedAsync", + "NuGetPackageVersion": "8.0.27", + "Private": "false", + "PublicKeyToken": "b03f5f7f11d50a3a", + "Version": "", + "FrameworkReferenceVersion": "8.0.27", + "ExternallyResolved": "true", + "NuGetPackageId": "Microsoft.NETCore.App.Ref", + "ResolvedFrom": "{RawFileName}", + "FrameworkReferenceName": "Microsoft.NETCore.App", + "ImageRuntime": "v4.0.30319", + "AssemblyVersion": "8.0.0.0", + "CopyLocal": "false", + "OriginalItemSpec": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref/net8.0/System.ComponentModel.EventBasedAsync.dll", + "FusionName": "System.ComponentModel.EventBasedAsync, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", + "FullPath": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.ComponentModel.EventBasedAsync.dll", + "RootDir": "C:\\", + "Filename": "System.ComponentModel.EventBasedAsync", + "Extension": ".dll", + "RelativeDir": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "Directory": "Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "RecursiveDir": "", + "ModifiedTime": "2026-04-30 07:39:00.0000000", + "CreatedTime": "2026-05-29 17:24:45.0818424", + "AccessedTime": "2026-06-09 15:16:47.1494770", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.ComponentModel.Primitives.dll", + "FileVersion": "8.0.2726.22922", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "AssemblyName": "System.ComponentModel.Primitives", + "NuGetPackageVersion": "8.0.27", + "Private": "false", + "PublicKeyToken": "b03f5f7f11d50a3a", + "Version": "", + "FrameworkReferenceVersion": "8.0.27", + "ExternallyResolved": "true", + "NuGetPackageId": "Microsoft.NETCore.App.Ref", + "ResolvedFrom": "{RawFileName}", + "FrameworkReferenceName": "Microsoft.NETCore.App", + "ImageRuntime": "v4.0.30319", + "AssemblyVersion": "8.0.0.0", + "CopyLocal": "false", + "OriginalItemSpec": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref/net8.0/System.ComponentModel.Primitives.dll", + "FusionName": "System.ComponentModel.Primitives, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", + "FullPath": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.ComponentModel.Primitives.dll", + "RootDir": "C:\\", + "Filename": "System.ComponentModel.Primitives", + "Extension": ".dll", + "RelativeDir": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "Directory": "Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "RecursiveDir": "", + "ModifiedTime": "2026-04-30 07:38:58.0000000", + "CreatedTime": "2026-05-29 17:24:45.0898426", + "AccessedTime": "2026-06-09 15:16:47.1544779", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.ComponentModel.TypeConverter.dll", + "FileVersion": "8.0.2726.22922", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "AssemblyName": "System.ComponentModel.TypeConverter", + "NuGetPackageVersion": "8.0.27", + "Private": "false", + "PublicKeyToken": "b03f5f7f11d50a3a", + "Version": "", + "FrameworkReferenceVersion": "8.0.27", + "ExternallyResolved": "true", + "NuGetPackageId": "Microsoft.NETCore.App.Ref", + "ResolvedFrom": "{RawFileName}", + "FrameworkReferenceName": "Microsoft.NETCore.App", + "ImageRuntime": "v4.0.30319", + "AssemblyVersion": "8.0.0.0", + "CopyLocal": "false", + "OriginalItemSpec": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref/net8.0/System.ComponentModel.TypeConverter.dll", + "FusionName": "System.ComponentModel.TypeConverter, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", + "FullPath": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.ComponentModel.TypeConverter.dll", + "RootDir": "C:\\", + "Filename": "System.ComponentModel.TypeConverter", + "Extension": ".dll", + "RelativeDir": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "Directory": "Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "RecursiveDir": "", + "ModifiedTime": "2026-04-30 07:39:02.0000000", + "CreatedTime": "2026-05-29 17:24:45.0981988", + "AccessedTime": "2026-06-09 15:16:47.1590207", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "C:\\Users\\admin\\.nuget\\packages\\microsoft.windowsdesktop.app.ref\\8.0.27\\ref\\net8.0\\System.Configuration.ConfigurationManager.dll", + "FileVersion": "8.0.2726.22922", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "AssemblyName": "System.Configuration.ConfigurationManager", + "NuGetPackageVersion": "8.0.27", + "Private": "false", + "PublicKeyToken": "cc7b13ffcd2ddd51", + "Version": "", + "FrameworkReferenceVersion": "8.0.27", + "ExternallyResolved": "true", + "NuGetPackageId": "Microsoft.WindowsDesktop.App.Ref", + "ResolvedFrom": "{RawFileName}", + "FrameworkReferenceName": "Microsoft.WindowsDesktop.App.WindowsForms", + "ImageRuntime": "v4.0.30319", + "AssemblyVersion": "8.0.0.0", + "CopyLocal": "false", + "OriginalItemSpec": "C:\\Users\\admin\\.nuget\\packages\\microsoft.windowsdesktop.app.ref\\8.0.27\\ref/net8.0/System.Configuration.ConfigurationManager.dll", + "FusionName": "System.Configuration.ConfigurationManager, Version=8.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51", + "FullPath": "C:\\Users\\admin\\.nuget\\packages\\microsoft.windowsdesktop.app.ref\\8.0.27\\ref\\net8.0\\System.Configuration.ConfigurationManager.dll", + "RootDir": "C:\\", + "Filename": "System.Configuration.ConfigurationManager", + "Extension": ".dll", + "RelativeDir": "C:\\Users\\admin\\.nuget\\packages\\microsoft.windowsdesktop.app.ref\\8.0.27\\ref\\net8.0\\", + "Directory": "Users\\admin\\.nuget\\packages\\microsoft.windowsdesktop.app.ref\\8.0.27\\ref\\net8.0\\", + "RecursiveDir": "", + "ModifiedTime": "2026-04-30 07:26:56.0000000", + "CreatedTime": "2026-05-29 17:24:44.3861048", + "AccessedTime": "2026-06-09 15:08:48.4436203", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Configuration.dll", + "FileVersion": "8.0.2726.22922", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "AssemblyName": "System.Configuration", + "NuGetPackageVersion": "8.0.27", + "Private": "false", + "PublicKeyToken": "b03f5f7f11d50a3a", + "Version": "", + "FrameworkReferenceVersion": "8.0.27", + "ExternallyResolved": "true", + "NuGetPackageId": "Microsoft.NETCore.App.Ref", + "ResolvedFrom": "{RawFileName}", + "FrameworkReferenceName": "Microsoft.NETCore.App", + "ImageRuntime": "v4.0.30319", + "AssemblyVersion": "4.0.0.0", + "CopyLocal": "false", + "OriginalItemSpec": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref/net8.0/System.Configuration.dll", + "FusionName": "System.Configuration, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", + "FullPath": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Configuration.dll", + "RootDir": "C:\\", + "Filename": "System.Configuration", + "Extension": ".dll", + "RelativeDir": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "Directory": "Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "RecursiveDir": "", + "ModifiedTime": "2026-04-30 07:42:32.0000000", + "CreatedTime": "2026-05-29 17:24:45.1064472", + "AccessedTime": "2026-06-09 15:16:47.1610345", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Console.dll", + "FileVersion": "8.0.2726.22922", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "AssemblyName": "System.Console", + "NuGetPackageVersion": "8.0.27", + "Private": "false", + "PublicKeyToken": "b03f5f7f11d50a3a", + "Version": "", + "FrameworkReferenceVersion": "8.0.27", + "ExternallyResolved": "true", + "NuGetPackageId": "Microsoft.NETCore.App.Ref", + "ResolvedFrom": "{RawFileName}", + "FrameworkReferenceName": "Microsoft.NETCore.App", + "ImageRuntime": "v4.0.30319", + "AssemblyVersion": "8.0.0.0", + "CopyLocal": "false", + "OriginalItemSpec": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref/net8.0/System.Console.dll", + "FusionName": "System.Console, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", + "FullPath": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Console.dll", + "RootDir": "C:\\", + "Filename": "System.Console", + "Extension": ".dll", + "RelativeDir": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "Directory": "Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "RecursiveDir": "", + "ModifiedTime": "2026-04-30 07:38:58.0000000", + "CreatedTime": "2026-05-29 17:24:45.1177567", + "AccessedTime": "2026-06-09 15:16:47.1640337", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Core.dll", + "FileVersion": "8.0.2726.22922", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "AssemblyName": "System.Core", + "NuGetPackageVersion": "8.0.27", + "Private": "false", + "PublicKeyToken": "b77a5c561934e089", + "Version": "", + "FrameworkReferenceVersion": "8.0.27", + "ExternallyResolved": "true", + "NuGetPackageId": "Microsoft.NETCore.App.Ref", + "ResolvedFrom": "{RawFileName}", + "FrameworkReferenceName": "Microsoft.NETCore.App", + "ImageRuntime": "v4.0.30319", + "AssemblyVersion": "4.0.0.0", + "CopyLocal": "false", + "OriginalItemSpec": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref/net8.0/System.Core.dll", + "FusionName": "System.Core, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", + "FullPath": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Core.dll", + "RootDir": "C:\\", + "Filename": "System.Core", + "Extension": ".dll", + "RelativeDir": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "Directory": "Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "RecursiveDir": "", + "ModifiedTime": "2026-04-30 07:43:16.0000000", + "CreatedTime": "2026-05-29 17:24:45.1287577", + "AccessedTime": "2026-06-09 15:16:47.1677480", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Data.Common.dll", + "FileVersion": "8.0.2726.22922", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "AssemblyName": "System.Data.Common", + "NuGetPackageVersion": "8.0.27", + "Private": "false", + "PublicKeyToken": "b03f5f7f11d50a3a", + "Version": "", + "FrameworkReferenceVersion": "8.0.27", + "ExternallyResolved": "true", + "NuGetPackageId": "Microsoft.NETCore.App.Ref", + "ResolvedFrom": "{RawFileName}", + "FrameworkReferenceName": "Microsoft.NETCore.App", + "ImageRuntime": "v4.0.30319", + "AssemblyVersion": "8.0.0.0", + "CopyLocal": "false", + "OriginalItemSpec": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref/net8.0/System.Data.Common.dll", + "FusionName": "System.Data.Common, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", + "FullPath": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Data.Common.dll", + "RootDir": "C:\\", + "Filename": "System.Data.Common", + "Extension": ".dll", + "RelativeDir": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "Directory": "Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "RecursiveDir": "", + "ModifiedTime": "2026-04-30 07:39:02.0000000", + "CreatedTime": "2026-05-29 17:24:45.1357572", + "AccessedTime": "2026-06-09 15:16:47.1717696", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Data.DataSetExtensions.dll", + "FileVersion": "8.0.2726.22922", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "AssemblyName": "System.Data.DataSetExtensions", + "NuGetPackageVersion": "8.0.27", + "Private": "false", + "PublicKeyToken": "b77a5c561934e089", + "Version": "", + "FrameworkReferenceVersion": "8.0.27", + "ExternallyResolved": "true", + "NuGetPackageId": "Microsoft.NETCore.App.Ref", + "ResolvedFrom": "{RawFileName}", + "FrameworkReferenceName": "Microsoft.NETCore.App", + "ImageRuntime": "v4.0.30319", + "AssemblyVersion": "8.0.0.0", + "CopyLocal": "false", + "OriginalItemSpec": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref/net8.0/System.Data.DataSetExtensions.dll", + "FusionName": "System.Data.DataSetExtensions, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", + "FullPath": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Data.DataSetExtensions.dll", + "RootDir": "C:\\", + "Filename": "System.Data.DataSetExtensions", + "Extension": ".dll", + "RelativeDir": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "Directory": "Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "RecursiveDir": "", + "ModifiedTime": "2026-04-30 07:43:02.0000000", + "CreatedTime": "2026-05-29 17:24:45.1458076", + "AccessedTime": "2026-06-09 15:16:47.1727558", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Data.dll", + "FileVersion": "8.0.2726.22922", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "AssemblyName": "System.Data", + "NuGetPackageVersion": "8.0.27", + "Private": "false", + "PublicKeyToken": "b77a5c561934e089", + "Version": "", + "FrameworkReferenceVersion": "8.0.27", + "ExternallyResolved": "true", + "NuGetPackageId": "Microsoft.NETCore.App.Ref", + "ResolvedFrom": "{RawFileName}", + "FrameworkReferenceName": "Microsoft.NETCore.App", + "ImageRuntime": "v4.0.30319", + "AssemblyVersion": "4.0.0.0", + "CopyLocal": "false", + "OriginalItemSpec": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref/net8.0/System.Data.dll", + "FusionName": "System.Data, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", + "FullPath": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Data.dll", + "RootDir": "C:\\", + "Filename": "System.Data", + "Extension": ".dll", + "RelativeDir": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "Directory": "Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "RecursiveDir": "", + "ModifiedTime": "2026-04-30 07:43:14.0000000", + "CreatedTime": "2026-05-29 17:24:45.1558075", + "AccessedTime": "2026-06-09 15:16:47.1767608", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "C:\\Users\\admin\\.nuget\\packages\\microsoft.windowsdesktop.app.ref\\8.0.27\\ref\\net8.0\\System.Design.dll", + "FileVersion": "8.0.2726.23001", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "AssemblyName": "System.Design", + "NuGetPackageVersion": "8.0.27", + "Private": "false", + "PublicKeyToken": "b03f5f7f11d50a3a", + "Version": "", + "FrameworkReferenceVersion": "8.0.27", + "ExternallyResolved": "true", + "NuGetPackageId": "Microsoft.WindowsDesktop.App.Ref", + "ResolvedFrom": "{RawFileName}", + "FrameworkReferenceName": "Microsoft.WindowsDesktop.App.WindowsForms", + "ImageRuntime": "v4.0.30319", + "AssemblyVersion": "8.0.0.0", + "CopyLocal": "false", + "OriginalItemSpec": "C:\\Users\\admin\\.nuget\\packages\\microsoft.windowsdesktop.app.ref\\8.0.27\\ref/net8.0/System.Design.dll", + "FusionName": "System.Design, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", + "FullPath": "C:\\Users\\admin\\.nuget\\packages\\microsoft.windowsdesktop.app.ref\\8.0.27\\ref\\net8.0\\System.Design.dll", + "RootDir": "C:\\", + "Filename": "System.Design", + "Extension": ".dll", + "RelativeDir": "C:\\Users\\admin\\.nuget\\packages\\microsoft.windowsdesktop.app.ref\\8.0.27\\ref\\net8.0\\", + "Directory": "Users\\admin\\.nuget\\packages\\microsoft.windowsdesktop.app.ref\\8.0.27\\ref\\net8.0\\", + "RecursiveDir": "", + "ModifiedTime": "2026-04-30 15:26:12.0000000", + "CreatedTime": "2026-05-29 17:24:44.3951023", + "AccessedTime": "2026-06-09 15:08:48.4092977", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Diagnostics.Contracts.dll", + "FileVersion": "8.0.2726.22922", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "AssemblyName": "System.Diagnostics.Contracts", + "NuGetPackageVersion": "8.0.27", + "Private": "false", + "PublicKeyToken": "b03f5f7f11d50a3a", + "Version": "", + "FrameworkReferenceVersion": "8.0.27", + "ExternallyResolved": "true", + "NuGetPackageId": "Microsoft.NETCore.App.Ref", + "ResolvedFrom": "{RawFileName}", + "FrameworkReferenceName": "Microsoft.NETCore.App", + "ImageRuntime": "v4.0.30319", + "AssemblyVersion": "8.0.0.0", + "CopyLocal": "false", + "OriginalItemSpec": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref/net8.0/System.Diagnostics.Contracts.dll", + "FusionName": "System.Diagnostics.Contracts, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", + "FullPath": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Diagnostics.Contracts.dll", + "RootDir": "C:\\", + "Filename": "System.Diagnostics.Contracts", + "Extension": ".dll", + "RelativeDir": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "Directory": "Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "RecursiveDir": "", + "ModifiedTime": "2026-04-30 07:38:58.0000000", + "CreatedTime": "2026-05-29 17:24:45.1641285", + "AccessedTime": "2026-06-09 15:16:47.1797554", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Diagnostics.Debug.dll", + "FileVersion": "8.0.2726.22922", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "AssemblyName": "System.Diagnostics.Debug", + "NuGetPackageVersion": "8.0.27", + "Private": "false", + "PublicKeyToken": "b03f5f7f11d50a3a", + "Version": "", + "FrameworkReferenceVersion": "8.0.27", + "ExternallyResolved": "true", + "NuGetPackageId": "Microsoft.NETCore.App.Ref", + "ResolvedFrom": "{RawFileName}", + "FrameworkReferenceName": "Microsoft.NETCore.App", + "ImageRuntime": "v4.0.30319", + "AssemblyVersion": "8.0.0.0", + "CopyLocal": "false", + "OriginalItemSpec": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref/net8.0/System.Diagnostics.Debug.dll", + "FusionName": "System.Diagnostics.Debug, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", + "FullPath": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Diagnostics.Debug.dll", + "RootDir": "C:\\", + "Filename": "System.Diagnostics.Debug", + "Extension": ".dll", + "RelativeDir": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "Directory": "Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "RecursiveDir": "", + "ModifiedTime": "2026-04-30 07:42:26.0000000", + "CreatedTime": "2026-05-29 17:24:45.1731306", + "AccessedTime": "2026-06-09 15:16:47.1851089", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Diagnostics.DiagnosticSource.dll", + "FileVersion": "8.0.2726.22922", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "AssemblyName": "System.Diagnostics.DiagnosticSource", + "NuGetPackageVersion": "8.0.27", + "Private": "false", + "PublicKeyToken": "cc7b13ffcd2ddd51", + "Version": "", + "FrameworkReferenceVersion": "8.0.27", + "ExternallyResolved": "true", + "NuGetPackageId": "Microsoft.NETCore.App.Ref", + "ResolvedFrom": "{RawFileName}", + "FrameworkReferenceName": "Microsoft.NETCore.App", + "ImageRuntime": "v4.0.30319", + "AssemblyVersion": "8.0.0.0", + "CopyLocal": "false", + "OriginalItemSpec": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref/net8.0/System.Diagnostics.DiagnosticSource.dll", + "FusionName": "System.Diagnostics.DiagnosticSource, Version=8.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51", + "FullPath": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Diagnostics.DiagnosticSource.dll", + "RootDir": "C:\\", + "Filename": "System.Diagnostics.DiagnosticSource", + "Extension": ".dll", + "RelativeDir": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "Directory": "Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "RecursiveDir": "", + "ModifiedTime": "2026-04-30 07:39:04.0000000", + "CreatedTime": "2026-05-29 17:24:45.1811312", + "AccessedTime": "2026-06-09 15:16:47.1901109", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "C:\\Users\\admin\\.nuget\\packages\\microsoft.windowsdesktop.app.ref\\8.0.27\\ref\\net8.0\\System.Diagnostics.EventLog.dll", + "FileVersion": "8.0.2726.22922", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "AssemblyName": "System.Diagnostics.EventLog", + "NuGetPackageVersion": "8.0.27", + "Private": "false", + "PublicKeyToken": "cc7b13ffcd2ddd51", + "Version": "", + "FrameworkReferenceVersion": "8.0.27", + "ExternallyResolved": "true", + "NuGetPackageId": "Microsoft.WindowsDesktop.App.Ref", + "ResolvedFrom": "{RawFileName}", + "FrameworkReferenceName": "Microsoft.WindowsDesktop.App.WindowsForms", + "ImageRuntime": "v4.0.30319", + "AssemblyVersion": "8.0.0.0", + "CopyLocal": "false", + "OriginalItemSpec": "C:\\Users\\admin\\.nuget\\packages\\microsoft.windowsdesktop.app.ref\\8.0.27\\ref/net8.0/System.Diagnostics.EventLog.dll", + "FusionName": "System.Diagnostics.EventLog, Version=8.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51", + "FullPath": "C:\\Users\\admin\\.nuget\\packages\\microsoft.windowsdesktop.app.ref\\8.0.27\\ref\\net8.0\\System.Diagnostics.EventLog.dll", + "RootDir": "C:\\", + "Filename": "System.Diagnostics.EventLog", + "Extension": ".dll", + "RelativeDir": "C:\\Users\\admin\\.nuget\\packages\\microsoft.windowsdesktop.app.ref\\8.0.27\\ref\\net8.0\\", + "Directory": "Users\\admin\\.nuget\\packages\\microsoft.windowsdesktop.app.ref\\8.0.27\\ref\\net8.0\\", + "RecursiveDir": "", + "ModifiedTime": "2026-04-30 07:26:30.0000000", + "CreatedTime": "2026-05-29 17:24:44.4041203", + "AccessedTime": "2026-06-09 15:08:48.3929797", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Diagnostics.FileVersionInfo.dll", + "FileVersion": "8.0.2726.22922", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "AssemblyName": "System.Diagnostics.FileVersionInfo", + "NuGetPackageVersion": "8.0.27", + "Private": "false", + "PublicKeyToken": "b03f5f7f11d50a3a", + "Version": "", + "FrameworkReferenceVersion": "8.0.27", + "ExternallyResolved": "true", + "NuGetPackageId": "Microsoft.NETCore.App.Ref", + "ResolvedFrom": "{RawFileName}", + "FrameworkReferenceName": "Microsoft.NETCore.App", + "ImageRuntime": "v4.0.30319", + "AssemblyVersion": "8.0.0.0", + "CopyLocal": "false", + "OriginalItemSpec": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref/net8.0/System.Diagnostics.FileVersionInfo.dll", + "FusionName": "System.Diagnostics.FileVersionInfo, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", + "FullPath": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Diagnostics.FileVersionInfo.dll", + "RootDir": "C:\\", + "Filename": "System.Diagnostics.FileVersionInfo", + "Extension": ".dll", + "RelativeDir": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "Directory": "Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "RecursiveDir": "", + "ModifiedTime": "2026-04-30 07:38:58.0000000", + "CreatedTime": "2026-05-29 17:24:45.1901301", + "AccessedTime": "2026-06-09 15:16:47.1941125", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "C:\\Users\\admin\\.nuget\\packages\\microsoft.windowsdesktop.app.ref\\8.0.27\\ref\\net8.0\\System.Diagnostics.PerformanceCounter.dll", + "FileVersion": "8.0.2726.22922", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "AssemblyName": "System.Diagnostics.PerformanceCounter", + "NuGetPackageVersion": "8.0.27", + "Private": "false", + "PublicKeyToken": "cc7b13ffcd2ddd51", + "Version": "", + "FrameworkReferenceVersion": "8.0.27", + "ExternallyResolved": "true", + "NuGetPackageId": "Microsoft.WindowsDesktop.App.Ref", + "ResolvedFrom": "{RawFileName}", + "FrameworkReferenceName": "Microsoft.WindowsDesktop.App.WindowsForms", + "ImageRuntime": "v4.0.30319", + "AssemblyVersion": "8.0.0.0", + "CopyLocal": "false", + "OriginalItemSpec": "C:\\Users\\admin\\.nuget\\packages\\microsoft.windowsdesktop.app.ref\\8.0.27\\ref/net8.0/System.Diagnostics.PerformanceCounter.dll", + "FusionName": "System.Diagnostics.PerformanceCounter, Version=8.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51", + "FullPath": "C:\\Users\\admin\\.nuget\\packages\\microsoft.windowsdesktop.app.ref\\8.0.27\\ref\\net8.0\\System.Diagnostics.PerformanceCounter.dll", + "RootDir": "C:\\", + "Filename": "System.Diagnostics.PerformanceCounter", + "Extension": ".dll", + "RelativeDir": "C:\\Users\\admin\\.nuget\\packages\\microsoft.windowsdesktop.app.ref\\8.0.27\\ref\\net8.0\\", + "Directory": "Users\\admin\\.nuget\\packages\\microsoft.windowsdesktop.app.ref\\8.0.27\\ref\\net8.0\\", + "RecursiveDir": "", + "ModifiedTime": "2026-04-30 07:27:40.0000000", + "CreatedTime": "2026-05-29 17:24:44.4121049", + "AccessedTime": "2026-06-09 15:08:48.3839807", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Diagnostics.Process.dll", + "FileVersion": "8.0.2726.22922", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "AssemblyName": "System.Diagnostics.Process", + "NuGetPackageVersion": "8.0.27", + "Private": "false", + "PublicKeyToken": "b03f5f7f11d50a3a", + "Version": "", + "FrameworkReferenceVersion": "8.0.27", + "ExternallyResolved": "true", + "NuGetPackageId": "Microsoft.NETCore.App.Ref", + "ResolvedFrom": "{RawFileName}", + "FrameworkReferenceName": "Microsoft.NETCore.App", + "ImageRuntime": "v4.0.30319", + "AssemblyVersion": "8.0.0.0", + "CopyLocal": "false", + "OriginalItemSpec": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref/net8.0/System.Diagnostics.Process.dll", + "FusionName": "System.Diagnostics.Process, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", + "FullPath": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Diagnostics.Process.dll", + "RootDir": "C:\\", + "Filename": "System.Diagnostics.Process", + "Extension": ".dll", + "RelativeDir": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "Directory": "Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "RecursiveDir": "", + "ModifiedTime": "2026-04-30 07:39:00.0000000", + "CreatedTime": "2026-05-29 17:24:45.2002750", + "AccessedTime": "2026-06-09 15:16:47.1982498", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Diagnostics.StackTrace.dll", + "FileVersion": "8.0.2726.22922", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "AssemblyName": "System.Diagnostics.StackTrace", + "NuGetPackageVersion": "8.0.27", + "Private": "false", + "PublicKeyToken": "b03f5f7f11d50a3a", + "Version": "", + "FrameworkReferenceVersion": "8.0.27", + "ExternallyResolved": "true", + "NuGetPackageId": "Microsoft.NETCore.App.Ref", + "ResolvedFrom": "{RawFileName}", + "FrameworkReferenceName": "Microsoft.NETCore.App", + "ImageRuntime": "v4.0.30319", + "AssemblyVersion": "8.0.0.0", + "CopyLocal": "false", + "OriginalItemSpec": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref/net8.0/System.Diagnostics.StackTrace.dll", + "FusionName": "System.Diagnostics.StackTrace, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", + "FullPath": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Diagnostics.StackTrace.dll", + "RootDir": "C:\\", + "Filename": "System.Diagnostics.StackTrace", + "Extension": ".dll", + "RelativeDir": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "Directory": "Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "RecursiveDir": "", + "ModifiedTime": "2026-04-30 07:38:56.0000000", + "CreatedTime": "2026-05-29 17:24:45.2088193", + "AccessedTime": "2026-06-09 15:16:47.2062444", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Diagnostics.TextWriterTraceListener.dll", + "FileVersion": "8.0.2726.22922", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "AssemblyName": "System.Diagnostics.TextWriterTraceListener", + "NuGetPackageVersion": "8.0.27", + "Private": "false", + "PublicKeyToken": "b03f5f7f11d50a3a", + "Version": "", + "FrameworkReferenceVersion": "8.0.27", + "ExternallyResolved": "true", + "NuGetPackageId": "Microsoft.NETCore.App.Ref", + "ResolvedFrom": "{RawFileName}", + "FrameworkReferenceName": "Microsoft.NETCore.App", + "ImageRuntime": "v4.0.30319", + "AssemblyVersion": "8.0.0.0", + "CopyLocal": "false", + "OriginalItemSpec": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref/net8.0/System.Diagnostics.TextWriterTraceListener.dll", + "FusionName": "System.Diagnostics.TextWriterTraceListener, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", + "FullPath": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Diagnostics.TextWriterTraceListener.dll", + "RootDir": "C:\\", + "Filename": "System.Diagnostics.TextWriterTraceListener", + "Extension": ".dll", + "RelativeDir": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "Directory": "Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "RecursiveDir": "", + "ModifiedTime": "2026-04-30 07:39:00.0000000", + "CreatedTime": "2026-05-29 17:24:45.2198189", + "AccessedTime": "2026-06-09 15:16:47.2092455", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Diagnostics.Tools.dll", + "FileVersion": "8.0.2726.22922", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "AssemblyName": "System.Diagnostics.Tools", + "NuGetPackageVersion": "8.0.27", + "Private": "false", + "PublicKeyToken": "b03f5f7f11d50a3a", + "Version": "", + "FrameworkReferenceVersion": "8.0.27", + "ExternallyResolved": "true", + "NuGetPackageId": "Microsoft.NETCore.App.Ref", + "ResolvedFrom": "{RawFileName}", + "FrameworkReferenceName": "Microsoft.NETCore.App", + "ImageRuntime": "v4.0.30319", + "AssemblyVersion": "8.0.0.0", + "CopyLocal": "false", + "OriginalItemSpec": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref/net8.0/System.Diagnostics.Tools.dll", + "FusionName": "System.Diagnostics.Tools, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", + "FullPath": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Diagnostics.Tools.dll", + "RootDir": "C:\\", + "Filename": "System.Diagnostics.Tools", + "Extension": ".dll", + "RelativeDir": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "Directory": "Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "RecursiveDir": "", + "ModifiedTime": "2026-04-30 07:42:26.0000000", + "CreatedTime": "2026-05-29 17:24:45.2263399", + "AccessedTime": "2026-06-09 15:16:47.2135064", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Diagnostics.TraceSource.dll", + "FileVersion": "8.0.2726.22922", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "AssemblyName": "System.Diagnostics.TraceSource", + "NuGetPackageVersion": "8.0.27", + "Private": "false", + "PublicKeyToken": "b03f5f7f11d50a3a", + "Version": "", + "FrameworkReferenceVersion": "8.0.27", + "ExternallyResolved": "true", + "NuGetPackageId": "Microsoft.NETCore.App.Ref", + "ResolvedFrom": "{RawFileName}", + "FrameworkReferenceName": "Microsoft.NETCore.App", + "ImageRuntime": "v4.0.30319", + "AssemblyVersion": "8.0.0.0", + "CopyLocal": "false", + "OriginalItemSpec": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref/net8.0/System.Diagnostics.TraceSource.dll", + "FusionName": "System.Diagnostics.TraceSource, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", + "FullPath": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Diagnostics.TraceSource.dll", + "RootDir": "C:\\", + "Filename": "System.Diagnostics.TraceSource", + "Extension": ".dll", + "RelativeDir": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "Directory": "Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "RecursiveDir": "", + "ModifiedTime": "2026-04-30 07:39:00.0000000", + "CreatedTime": "2026-05-29 17:24:45.2346206", + "AccessedTime": "2026-06-09 15:16:47.2175252", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Diagnostics.Tracing.dll", + "FileVersion": "8.0.2726.22922", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "AssemblyName": "System.Diagnostics.Tracing", + "NuGetPackageVersion": "8.0.27", + "Private": "false", + "PublicKeyToken": "b03f5f7f11d50a3a", + "Version": "", + "FrameworkReferenceVersion": "8.0.27", + "ExternallyResolved": "true", + "NuGetPackageId": "Microsoft.NETCore.App.Ref", + "ResolvedFrom": "{RawFileName}", + "FrameworkReferenceName": "Microsoft.NETCore.App", + "ImageRuntime": "v4.0.30319", + "AssemblyVersion": "8.0.0.0", + "CopyLocal": "false", + "OriginalItemSpec": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref/net8.0/System.Diagnostics.Tracing.dll", + "FusionName": "System.Diagnostics.Tracing, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", + "FullPath": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Diagnostics.Tracing.dll", + "RootDir": "C:\\", + "Filename": "System.Diagnostics.Tracing", + "Extension": ".dll", + "RelativeDir": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "Directory": "Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "RecursiveDir": "", + "ModifiedTime": "2026-04-30 07:38:58.0000000", + "CreatedTime": "2026-05-29 17:24:45.2430405", + "AccessedTime": "2026-06-09 15:16:47.2195219", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "C:\\Users\\admin\\.nuget\\packages\\microsoft.windowsdesktop.app.ref\\8.0.27\\ref\\net8.0\\System.DirectoryServices.dll", + "FileVersion": "8.0.2726.22922", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "AssemblyName": "System.DirectoryServices", + "NuGetPackageVersion": "8.0.27", + "Private": "false", + "PublicKeyToken": "b03f5f7f11d50a3a", + "Version": "", + "FrameworkReferenceVersion": "8.0.27", + "ExternallyResolved": "true", + "NuGetPackageId": "Microsoft.WindowsDesktop.App.Ref", + "ResolvedFrom": "{RawFileName}", + "FrameworkReferenceName": "Microsoft.WindowsDesktop.App.WindowsForms", + "ImageRuntime": "v4.0.30319", + "AssemblyVersion": "8.0.0.0", + "CopyLocal": "false", + "OriginalItemSpec": "C:\\Users\\admin\\.nuget\\packages\\microsoft.windowsdesktop.app.ref\\8.0.27\\ref/net8.0/System.DirectoryServices.dll", + "FusionName": "System.DirectoryServices, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", + "FullPath": "C:\\Users\\admin\\.nuget\\packages\\microsoft.windowsdesktop.app.ref\\8.0.27\\ref\\net8.0\\System.DirectoryServices.dll", + "RootDir": "C:\\", + "Filename": "System.DirectoryServices", + "Extension": ".dll", + "RelativeDir": "C:\\Users\\admin\\.nuget\\packages\\microsoft.windowsdesktop.app.ref\\8.0.27\\ref\\net8.0\\", + "Directory": "Users\\admin\\.nuget\\packages\\microsoft.windowsdesktop.app.ref\\8.0.27\\ref\\net8.0\\", + "RecursiveDir": "", + "ModifiedTime": "2026-04-30 07:27:38.0000000", + "CreatedTime": "2026-05-29 17:24:44.4201027", + "AccessedTime": "2026-06-09 15:08:48.3316843", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.dll", + "FileVersion": "8.0.2726.22922", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "AssemblyName": "System", + "NuGetPackageVersion": "8.0.27", + "Private": "false", + "PublicKeyToken": "b77a5c561934e089", + "Version": "", + "FrameworkReferenceVersion": "8.0.27", + "ExternallyResolved": "true", + "NuGetPackageId": "Microsoft.NETCore.App.Ref", + "ResolvedFrom": "{RawFileName}", + "FrameworkReferenceName": "Microsoft.NETCore.App", + "ImageRuntime": "v4.0.30319", + "AssemblyVersion": "4.0.0.0", + "CopyLocal": "false", + "OriginalItemSpec": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref/net8.0/System.dll", + "FusionName": "System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", + "FullPath": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.dll", + "RootDir": "C:\\", + "Filename": "System", + "Extension": ".dll", + "RelativeDir": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "Directory": "Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "RecursiveDir": "", + "ModifiedTime": "2026-04-30 07:43:18.0000000", + "CreatedTime": "2026-05-29 17:24:45.2510374", + "AccessedTime": "2026-06-09 15:16:47.2235164", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "C:\\Users\\admin\\.nuget\\packages\\microsoft.windowsdesktop.app.ref\\8.0.27\\ref\\net8.0\\System.Drawing.Common.dll", + "FileVersion": "8.0.2726.23001", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "AssemblyName": "System.Drawing.Common", + "NuGetPackageVersion": "8.0.27", + "Private": "false", + "PublicKeyToken": "cc7b13ffcd2ddd51", + "Version": "", + "FrameworkReferenceVersion": "8.0.27", + "ExternallyResolved": "true", + "NuGetPackageId": "Microsoft.WindowsDesktop.App.Ref", + "ResolvedFrom": "{RawFileName}", + "FrameworkReferenceName": "Microsoft.WindowsDesktop.App.WindowsForms", + "ImageRuntime": "v4.0.30319", + "AssemblyVersion": "8.0.0.0", + "CopyLocal": "false", + "OriginalItemSpec": "C:\\Users\\admin\\.nuget\\packages\\microsoft.windowsdesktop.app.ref\\8.0.27\\ref/net8.0/System.Drawing.Common.dll", + "FusionName": "System.Drawing.Common, Version=8.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51", + "FullPath": "C:\\Users\\admin\\.nuget\\packages\\microsoft.windowsdesktop.app.ref\\8.0.27\\ref\\net8.0\\System.Drawing.Common.dll", + "RootDir": "C:\\", + "Filename": "System.Drawing.Common", + "Extension": ".dll", + "RelativeDir": "C:\\Users\\admin\\.nuget\\packages\\microsoft.windowsdesktop.app.ref\\8.0.27\\ref\\net8.0\\", + "Directory": "Users\\admin\\.nuget\\packages\\microsoft.windowsdesktop.app.ref\\8.0.27\\ref\\net8.0\\", + "RecursiveDir": "", + "ModifiedTime": "2026-04-30 15:24:38.0000000", + "CreatedTime": "2026-05-29 17:24:44.4281054", + "AccessedTime": "2026-06-09 15:08:48.3256827", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "C:\\Users\\admin\\.nuget\\packages\\microsoft.windowsdesktop.app.ref\\8.0.27\\ref\\net8.0\\System.Drawing.Design.dll", + "FileVersion": "8.0.2726.23001", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "AssemblyName": "System.Drawing.Design", + "NuGetPackageVersion": "8.0.27", + "Private": "false", + "PublicKeyToken": "b03f5f7f11d50a3a", + "Version": "", + "FrameworkReferenceVersion": "8.0.27", + "ExternallyResolved": "true", + "NuGetPackageId": "Microsoft.WindowsDesktop.App.Ref", + "ResolvedFrom": "{RawFileName}", + "FrameworkReferenceName": "Microsoft.WindowsDesktop.App.WindowsForms", + "ImageRuntime": "v4.0.30319", + "AssemblyVersion": "8.0.0.0", + "CopyLocal": "false", + "OriginalItemSpec": "C:\\Users\\admin\\.nuget\\packages\\microsoft.windowsdesktop.app.ref\\8.0.27\\ref/net8.0/System.Drawing.Design.dll", + "FusionName": "System.Drawing.Design, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", + "FullPath": "C:\\Users\\admin\\.nuget\\packages\\microsoft.windowsdesktop.app.ref\\8.0.27\\ref\\net8.0\\System.Drawing.Design.dll", + "RootDir": "C:\\", + "Filename": "System.Drawing.Design", + "Extension": ".dll", + "RelativeDir": "C:\\Users\\admin\\.nuget\\packages\\microsoft.windowsdesktop.app.ref\\8.0.27\\ref\\net8.0\\", + "Directory": "Users\\admin\\.nuget\\packages\\microsoft.windowsdesktop.app.ref\\8.0.27\\ref\\net8.0\\", + "RecursiveDir": "", + "ModifiedTime": "2026-04-30 15:26:10.0000000", + "CreatedTime": "2026-05-29 17:24:44.4371033", + "AccessedTime": "2026-06-09 15:08:48.3206845", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "C:\\Users\\admin\\.nuget\\packages\\microsoft.windowsdesktop.app.ref\\8.0.27\\ref\\net8.0\\System.Drawing.dll", + "FileVersion": "8.0.2726.23001", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "AssemblyName": "System.Drawing", + "NuGetPackageVersion": "8.0.27", + "Private": "false", + "PublicKeyToken": "b03f5f7f11d50a3a", + "Version": "", + "FrameworkReferenceVersion": "8.0.27", + "ExternallyResolved": "true", + "NuGetPackageId": "Microsoft.WindowsDesktop.App.Ref", + "ResolvedFrom": "{RawFileName}", + "FrameworkReferenceName": "Microsoft.WindowsDesktop.App.WindowsForms", + "ImageRuntime": "v4.0.30319", + "AssemblyVersion": "8.0.0.0", + "CopyLocal": "false", + "OriginalItemSpec": "C:\\Users\\admin\\.nuget\\packages\\microsoft.windowsdesktop.app.ref\\8.0.27\\ref/net8.0/System.Drawing.dll", + "FusionName": "System.Drawing, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", + "FullPath": "C:\\Users\\admin\\.nuget\\packages\\microsoft.windowsdesktop.app.ref\\8.0.27\\ref\\net8.0\\System.Drawing.dll", + "RootDir": "C:\\", + "Filename": "System.Drawing", + "Extension": ".dll", + "RelativeDir": "C:\\Users\\admin\\.nuget\\packages\\microsoft.windowsdesktop.app.ref\\8.0.27\\ref\\net8.0\\", + "Directory": "Users\\admin\\.nuget\\packages\\microsoft.windowsdesktop.app.ref\\8.0.27\\ref\\net8.0\\", + "RecursiveDir": "", + "ModifiedTime": "2026-04-30 15:26:14.0000000", + "CreatedTime": "2026-05-29 17:24:44.4461051", + "AccessedTime": "2026-06-09 15:08:48.3133345", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Drawing.Primitives.dll", + "FileVersion": "8.0.2726.22922", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "AssemblyName": "System.Drawing.Primitives", + "NuGetPackageVersion": "8.0.27", + "Private": "false", + "PublicKeyToken": "b03f5f7f11d50a3a", + "Version": "", + "FrameworkReferenceVersion": "8.0.27", + "ExternallyResolved": "true", + "NuGetPackageId": "Microsoft.NETCore.App.Ref", + "ResolvedFrom": "{RawFileName}", + "FrameworkReferenceName": "Microsoft.NETCore.App", + "ImageRuntime": "v4.0.30319", + "AssemblyVersion": "8.0.0.0", + "CopyLocal": "false", + "OriginalItemSpec": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref/net8.0/System.Drawing.Primitives.dll", + "FusionName": "System.Drawing.Primitives, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", + "FullPath": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Drawing.Primitives.dll", + "RootDir": "C:\\", + "Filename": "System.Drawing.Primitives", + "Extension": ".dll", + "RelativeDir": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "Directory": "Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "RecursiveDir": "", + "ModifiedTime": "2026-04-30 07:39:02.0000000", + "CreatedTime": "2026-05-29 17:24:45.2675763", + "AccessedTime": "2026-06-09 15:16:47.2369355", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Dynamic.Runtime.dll", + "FileVersion": "8.0.2726.22922", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "AssemblyName": "System.Dynamic.Runtime", + "NuGetPackageVersion": "8.0.27", + "Private": "false", + "PublicKeyToken": "b03f5f7f11d50a3a", + "Version": "", + "FrameworkReferenceVersion": "8.0.27", + "ExternallyResolved": "true", + "NuGetPackageId": "Microsoft.NETCore.App.Ref", + "ResolvedFrom": "{RawFileName}", + "FrameworkReferenceName": "Microsoft.NETCore.App", + "ImageRuntime": "v4.0.30319", + "AssemblyVersion": "8.0.0.0", + "CopyLocal": "false", + "OriginalItemSpec": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref/net8.0/System.Dynamic.Runtime.dll", + "FusionName": "System.Dynamic.Runtime, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", + "FullPath": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Dynamic.Runtime.dll", + "RootDir": "C:\\", + "Filename": "System.Dynamic.Runtime", + "Extension": ".dll", + "RelativeDir": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "Directory": "Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "RecursiveDir": "", + "ModifiedTime": "2026-04-30 07:42:30.0000000", + "CreatedTime": "2026-05-29 17:24:45.2772400", + "AccessedTime": "2026-06-09 15:16:47.2409353", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Formats.Asn1.dll", + "FileVersion": "8.0.2726.22922", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "AssemblyName": "System.Formats.Asn1", + "NuGetPackageVersion": "8.0.27", + "Private": "false", + "PublicKeyToken": "cc7b13ffcd2ddd51", + "Version": "", + "FrameworkReferenceVersion": "8.0.27", + "ExternallyResolved": "true", + "NuGetPackageId": "Microsoft.NETCore.App.Ref", + "ResolvedFrom": "{RawFileName}", + "FrameworkReferenceName": "Microsoft.NETCore.App", + "ImageRuntime": "v4.0.30319", + "AssemblyVersion": "8.0.0.0", + "CopyLocal": "false", + "OriginalItemSpec": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref/net8.0/System.Formats.Asn1.dll", + "FusionName": "System.Formats.Asn1, Version=8.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51", + "FullPath": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Formats.Asn1.dll", + "RootDir": "C:\\", + "Filename": "System.Formats.Asn1", + "Extension": ".dll", + "RelativeDir": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "Directory": "Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "RecursiveDir": "", + "ModifiedTime": "2026-04-30 07:39:00.0000000", + "CreatedTime": "2026-05-29 17:24:45.2866130", + "AccessedTime": "2026-06-09 15:16:47.2459362", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Formats.Tar.dll", + "FileVersion": "8.0.2726.22922", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "AssemblyName": "System.Formats.Tar", + "NuGetPackageVersion": "8.0.27", + "Private": "false", + "PublicKeyToken": "cc7b13ffcd2ddd51", + "Version": "", + "FrameworkReferenceVersion": "8.0.27", + "ExternallyResolved": "true", + "NuGetPackageId": "Microsoft.NETCore.App.Ref", + "ResolvedFrom": "{RawFileName}", + "FrameworkReferenceName": "Microsoft.NETCore.App", + "ImageRuntime": "v4.0.30319", + "AssemblyVersion": "8.0.0.0", + "CopyLocal": "false", + "OriginalItemSpec": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref/net8.0/System.Formats.Tar.dll", + "FusionName": "System.Formats.Tar, Version=8.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51", + "FullPath": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Formats.Tar.dll", + "RootDir": "C:\\", + "Filename": "System.Formats.Tar", + "Extension": ".dll", + "RelativeDir": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "Directory": "Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "RecursiveDir": "", + "ModifiedTime": "2026-04-30 07:38:58.0000000", + "CreatedTime": "2026-05-29 17:24:45.2956183", + "AccessedTime": "2026-06-09 15:16:47.2519352", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Globalization.Calendars.dll", + "FileVersion": "8.0.2726.22922", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "AssemblyName": "System.Globalization.Calendars", + "NuGetPackageVersion": "8.0.27", + "Private": "false", + "PublicKeyToken": "b03f5f7f11d50a3a", + "Version": "", + "FrameworkReferenceVersion": "8.0.27", + "ExternallyResolved": "true", + "NuGetPackageId": "Microsoft.NETCore.App.Ref", + "ResolvedFrom": "{RawFileName}", + "FrameworkReferenceName": "Microsoft.NETCore.App", + "ImageRuntime": "v4.0.30319", + "AssemblyVersion": "8.0.0.0", + "CopyLocal": "false", + "OriginalItemSpec": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref/net8.0/System.Globalization.Calendars.dll", + "FusionName": "System.Globalization.Calendars, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", + "FullPath": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Globalization.Calendars.dll", + "RootDir": "C:\\", + "Filename": "System.Globalization.Calendars", + "Extension": ".dll", + "RelativeDir": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "Directory": "Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "RecursiveDir": "", + "ModifiedTime": "2026-04-30 07:42:26.0000000", + "CreatedTime": "2026-05-29 17:24:45.3076209", + "AccessedTime": "2026-06-09 15:16:47.2539358", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Globalization.dll", + "FileVersion": "8.0.2726.22922", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "AssemblyName": "System.Globalization", + "NuGetPackageVersion": "8.0.27", + "Private": "false", + "PublicKeyToken": "b03f5f7f11d50a3a", + "Version": "", + "FrameworkReferenceVersion": "8.0.27", + "ExternallyResolved": "true", + "NuGetPackageId": "Microsoft.NETCore.App.Ref", + "ResolvedFrom": "{RawFileName}", + "FrameworkReferenceName": "Microsoft.NETCore.App", + "ImageRuntime": "v4.0.30319", + "AssemblyVersion": "8.0.0.0", + "CopyLocal": "false", + "OriginalItemSpec": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref/net8.0/System.Globalization.dll", + "FusionName": "System.Globalization, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", + "FullPath": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Globalization.dll", + "RootDir": "C:\\", + "Filename": "System.Globalization", + "Extension": ".dll", + "RelativeDir": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "Directory": "Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "RecursiveDir": "", + "ModifiedTime": "2026-04-30 07:42:26.0000000", + "CreatedTime": "2026-05-29 17:24:45.3179310", + "AccessedTime": "2026-06-09 15:16:47.2569397", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Globalization.Extensions.dll", + "FileVersion": "8.0.2726.22922", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "AssemblyName": "System.Globalization.Extensions", + "NuGetPackageVersion": "8.0.27", + "Private": "false", + "PublicKeyToken": "b03f5f7f11d50a3a", + "Version": "", + "FrameworkReferenceVersion": "8.0.27", + "ExternallyResolved": "true", + "NuGetPackageId": "Microsoft.NETCore.App.Ref", + "ResolvedFrom": "{RawFileName}", + "FrameworkReferenceName": "Microsoft.NETCore.App", + "ImageRuntime": "v4.0.30319", + "AssemblyVersion": "8.0.0.0", + "CopyLocal": "false", + "OriginalItemSpec": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref/net8.0/System.Globalization.Extensions.dll", + "FusionName": "System.Globalization.Extensions, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", + "FullPath": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Globalization.Extensions.dll", + "RootDir": "C:\\", + "Filename": "System.Globalization.Extensions", + "Extension": ".dll", + "RelativeDir": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "Directory": "Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "RecursiveDir": "", + "ModifiedTime": "2026-04-30 07:42:24.0000000", + "CreatedTime": "2026-05-29 17:24:45.3261619", + "AccessedTime": "2026-06-09 15:16:47.2635084", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.IO.Compression.Brotli.dll", + "FileVersion": "8.0.2726.22922", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "AssemblyName": "System.IO.Compression.Brotli", + "NuGetPackageVersion": "8.0.27", + "Private": "false", + "PublicKeyToken": "b77a5c561934e089", + "Version": "", + "FrameworkReferenceVersion": "8.0.27", + "ExternallyResolved": "true", + "NuGetPackageId": "Microsoft.NETCore.App.Ref", + "ResolvedFrom": "{RawFileName}", + "FrameworkReferenceName": "Microsoft.NETCore.App", + "ImageRuntime": "v4.0.30319", + "AssemblyVersion": "8.0.0.0", + "CopyLocal": "false", + "OriginalItemSpec": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref/net8.0/System.IO.Compression.Brotli.dll", + "FusionName": "System.IO.Compression.Brotli, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", + "FullPath": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.IO.Compression.Brotli.dll", + "RootDir": "C:\\", + "Filename": "System.IO.Compression.Brotli", + "Extension": ".dll", + "RelativeDir": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "Directory": "Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "RecursiveDir": "", + "ModifiedTime": "2026-04-30 07:39:00.0000000", + "CreatedTime": "2026-05-29 17:24:45.3361989", + "AccessedTime": "2026-06-09 15:16:47.2675120", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.IO.Compression.dll", + "FileVersion": "8.0.2726.22922", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "AssemblyName": "System.IO.Compression", + "NuGetPackageVersion": "8.0.27", + "Private": "false", + "PublicKeyToken": "b77a5c561934e089", + "Version": "", + "FrameworkReferenceVersion": "8.0.27", + "ExternallyResolved": "true", + "NuGetPackageId": "Microsoft.NETCore.App.Ref", + "ResolvedFrom": "{RawFileName}", + "FrameworkReferenceName": "Microsoft.NETCore.App", + "ImageRuntime": "v4.0.30319", + "AssemblyVersion": "8.0.0.0", + "CopyLocal": "false", + "OriginalItemSpec": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref/net8.0/System.IO.Compression.dll", + "FusionName": "System.IO.Compression, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", + "FullPath": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.IO.Compression.dll", + "RootDir": "C:\\", + "Filename": "System.IO.Compression", + "Extension": ".dll", + "RelativeDir": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "Directory": "Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "RecursiveDir": "", + "ModifiedTime": "2026-04-30 07:38:56.0000000", + "CreatedTime": "2026-05-29 17:24:45.3454224", + "AccessedTime": "2026-06-09 15:16:47.2708784", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.IO.Compression.FileSystem.dll", + "FileVersion": "8.0.2726.22922", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "AssemblyName": "System.IO.Compression.FileSystem", + "NuGetPackageVersion": "8.0.27", + "Private": "false", + "PublicKeyToken": "b77a5c561934e089", + "Version": "", + "FrameworkReferenceVersion": "8.0.27", + "ExternallyResolved": "true", + "NuGetPackageId": "Microsoft.NETCore.App.Ref", + "ResolvedFrom": "{RawFileName}", + "FrameworkReferenceName": "Microsoft.NETCore.App", + "ImageRuntime": "v4.0.30319", + "AssemblyVersion": "4.0.0.0", + "CopyLocal": "false", + "OriginalItemSpec": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref/net8.0/System.IO.Compression.FileSystem.dll", + "FusionName": "System.IO.Compression.FileSystem, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", + "FullPath": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.IO.Compression.FileSystem.dll", + "RootDir": "C:\\", + "Filename": "System.IO.Compression.FileSystem", + "Extension": ".dll", + "RelativeDir": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "Directory": "Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "RecursiveDir": "", + "ModifiedTime": "2026-04-30 07:42:48.0000000", + "CreatedTime": "2026-05-29 17:24:45.3539276", + "AccessedTime": "2026-06-09 15:16:47.2766369", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.IO.Compression.ZipFile.dll", + "FileVersion": "8.0.2726.22922", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "AssemblyName": "System.IO.Compression.ZipFile", + "NuGetPackageVersion": "8.0.27", + "Private": "false", + "PublicKeyToken": "b77a5c561934e089", + "Version": "", + "FrameworkReferenceVersion": "8.0.27", + "ExternallyResolved": "true", + "NuGetPackageId": "Microsoft.NETCore.App.Ref", + "ResolvedFrom": "{RawFileName}", + "FrameworkReferenceName": "Microsoft.NETCore.App", + "ImageRuntime": "v4.0.30319", + "AssemblyVersion": "8.0.0.0", + "CopyLocal": "false", + "OriginalItemSpec": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref/net8.0/System.IO.Compression.ZipFile.dll", + "FusionName": "System.IO.Compression.ZipFile, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", + "FullPath": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.IO.Compression.ZipFile.dll", + "RootDir": "C:\\", + "Filename": "System.IO.Compression.ZipFile", + "Extension": ".dll", + "RelativeDir": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "Directory": "Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "RecursiveDir": "", + "ModifiedTime": "2026-04-30 07:38:58.0000000", + "CreatedTime": "2026-05-29 17:24:45.3646528", + "AccessedTime": "2026-06-09 15:16:47.2806483", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.IO.dll", + "FileVersion": "8.0.2726.22922", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "AssemblyName": "System.IO", + "NuGetPackageVersion": "8.0.27", + "Private": "false", + "PublicKeyToken": "b03f5f7f11d50a3a", + "Version": "", + "FrameworkReferenceVersion": "8.0.27", + "ExternallyResolved": "true", + "NuGetPackageId": "Microsoft.NETCore.App.Ref", + "ResolvedFrom": "{RawFileName}", + "FrameworkReferenceName": "Microsoft.NETCore.App", + "ImageRuntime": "v4.0.30319", + "AssemblyVersion": "8.0.0.0", + "CopyLocal": "false", + "OriginalItemSpec": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref/net8.0/System.IO.dll", + "FusionName": "System.IO, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", + "FullPath": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.IO.dll", + "RootDir": "C:\\", + "Filename": "System.IO", + "Extension": ".dll", + "RelativeDir": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "Directory": "Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "RecursiveDir": "", + "ModifiedTime": "2026-04-30 07:42:26.0000000", + "CreatedTime": "2026-05-29 17:24:45.3750758", + "AccessedTime": "2026-06-09 15:16:47.2866481", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.IO.FileSystem.AccessControl.dll", + "FileVersion": "8.0.2726.22922", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "AssemblyName": "System.IO.FileSystem.AccessControl", + "NuGetPackageVersion": "8.0.27", + "Private": "false", + "PublicKeyToken": "b03f5f7f11d50a3a", + "Version": "", + "FrameworkReferenceVersion": "8.0.27", + "ExternallyResolved": "true", + "NuGetPackageId": "Microsoft.NETCore.App.Ref", + "ResolvedFrom": "{RawFileName}", + "FrameworkReferenceName": "Microsoft.NETCore.App", + "ImageRuntime": "v4.0.30319", + "AssemblyVersion": "8.0.0.0", + "CopyLocal": "false", + "OriginalItemSpec": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref/net8.0/System.IO.FileSystem.AccessControl.dll", + "FusionName": "System.IO.FileSystem.AccessControl, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", + "FullPath": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.IO.FileSystem.AccessControl.dll", + "RootDir": "C:\\", + "Filename": "System.IO.FileSystem.AccessControl", + "Extension": ".dll", + "RelativeDir": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "Directory": "Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "RecursiveDir": "", + "ModifiedTime": "2026-04-30 07:39:02.0000000", + "CreatedTime": "2026-05-29 17:24:45.3840740", + "AccessedTime": "2026-06-09 15:16:47.2896486", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.IO.FileSystem.dll", + "FileVersion": "8.0.2726.22922", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "AssemblyName": "System.IO.FileSystem", + "NuGetPackageVersion": "8.0.27", + "Private": "false", + "PublicKeyToken": "b03f5f7f11d50a3a", + "Version": "", + "FrameworkReferenceVersion": "8.0.27", + "ExternallyResolved": "true", + "NuGetPackageId": "Microsoft.NETCore.App.Ref", + "ResolvedFrom": "{RawFileName}", + "FrameworkReferenceName": "Microsoft.NETCore.App", + "ImageRuntime": "v4.0.30319", + "AssemblyVersion": "8.0.0.0", + "CopyLocal": "false", + "OriginalItemSpec": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref/net8.0/System.IO.FileSystem.dll", + "FusionName": "System.IO.FileSystem, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", + "FullPath": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.IO.FileSystem.dll", + "RootDir": "C:\\", + "Filename": "System.IO.FileSystem", + "Extension": ".dll", + "RelativeDir": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "Directory": "Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "RecursiveDir": "", + "ModifiedTime": "2026-04-30 07:42:28.0000000", + "CreatedTime": "2026-05-29 17:24:45.3946112", + "AccessedTime": "2026-06-09 15:16:47.2950671", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.IO.FileSystem.DriveInfo.dll", + "FileVersion": "8.0.2726.22922", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "AssemblyName": "System.IO.FileSystem.DriveInfo", + "NuGetPackageVersion": "8.0.27", + "Private": "false", + "PublicKeyToken": "b03f5f7f11d50a3a", + "Version": "", + "FrameworkReferenceVersion": "8.0.27", + "ExternallyResolved": "true", + "NuGetPackageId": "Microsoft.NETCore.App.Ref", + "ResolvedFrom": "{RawFileName}", + "FrameworkReferenceName": "Microsoft.NETCore.App", + "ImageRuntime": "v4.0.30319", + "AssemblyVersion": "8.0.0.0", + "CopyLocal": "false", + "OriginalItemSpec": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref/net8.0/System.IO.FileSystem.DriveInfo.dll", + "FusionName": "System.IO.FileSystem.DriveInfo, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", + "FullPath": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.IO.FileSystem.DriveInfo.dll", + "RootDir": "C:\\", + "Filename": "System.IO.FileSystem.DriveInfo", + "Extension": ".dll", + "RelativeDir": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "Directory": "Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "RecursiveDir": "", + "ModifiedTime": "2026-04-30 07:38:58.0000000", + "CreatedTime": "2026-05-29 17:24:45.4036101", + "AccessedTime": "2026-06-09 15:16:47.2980729", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.IO.FileSystem.Primitives.dll", + "FileVersion": "8.0.2726.22922", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "AssemblyName": "System.IO.FileSystem.Primitives", + "NuGetPackageVersion": "8.0.27", + "Private": "false", + "PublicKeyToken": "b03f5f7f11d50a3a", + "Version": "", + "FrameworkReferenceVersion": "8.0.27", + "ExternallyResolved": "true", + "NuGetPackageId": "Microsoft.NETCore.App.Ref", + "ResolvedFrom": "{RawFileName}", + "FrameworkReferenceName": "Microsoft.NETCore.App", + "ImageRuntime": "v4.0.30319", + "AssemblyVersion": "8.0.0.0", + "CopyLocal": "false", + "OriginalItemSpec": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref/net8.0/System.IO.FileSystem.Primitives.dll", + "FusionName": "System.IO.FileSystem.Primitives, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", + "FullPath": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.IO.FileSystem.Primitives.dll", + "RootDir": "C:\\", + "Filename": "System.IO.FileSystem.Primitives", + "Extension": ".dll", + "RelativeDir": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "Directory": "Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "RecursiveDir": "", + "ModifiedTime": "2026-04-30 07:42:26.0000000", + "CreatedTime": "2026-05-29 17:24:45.4142447", + "AccessedTime": "2026-06-09 15:16:47.3020687", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.IO.FileSystem.Watcher.dll", + "FileVersion": "8.0.2726.22922", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "AssemblyName": "System.IO.FileSystem.Watcher", + "NuGetPackageVersion": "8.0.27", + "Private": "false", + "PublicKeyToken": "b03f5f7f11d50a3a", + "Version": "", + "FrameworkReferenceVersion": "8.0.27", + "ExternallyResolved": "true", + "NuGetPackageId": "Microsoft.NETCore.App.Ref", + "ResolvedFrom": "{RawFileName}", + "FrameworkReferenceName": "Microsoft.NETCore.App", + "ImageRuntime": "v4.0.30319", + "AssemblyVersion": "8.0.0.0", + "CopyLocal": "false", + "OriginalItemSpec": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref/net8.0/System.IO.FileSystem.Watcher.dll", + "FusionName": "System.IO.FileSystem.Watcher, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", + "FullPath": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.IO.FileSystem.Watcher.dll", + "RootDir": "C:\\", + "Filename": "System.IO.FileSystem.Watcher", + "Extension": ".dll", + "RelativeDir": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "Directory": "Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "RecursiveDir": "", + "ModifiedTime": "2026-04-30 07:38:58.0000000", + "CreatedTime": "2026-05-29 17:24:45.4240296", + "AccessedTime": "2026-06-09 15:16:47.3050681", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.IO.IsolatedStorage.dll", + "FileVersion": "8.0.2726.22922", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "AssemblyName": "System.IO.IsolatedStorage", + "NuGetPackageVersion": "8.0.27", + "Private": "false", + "PublicKeyToken": "b03f5f7f11d50a3a", + "Version": "", + "FrameworkReferenceVersion": "8.0.27", + "ExternallyResolved": "true", + "NuGetPackageId": "Microsoft.NETCore.App.Ref", + "ResolvedFrom": "{RawFileName}", + "FrameworkReferenceName": "Microsoft.NETCore.App", + "ImageRuntime": "v4.0.30319", + "AssemblyVersion": "8.0.0.0", + "CopyLocal": "false", + "OriginalItemSpec": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref/net8.0/System.IO.IsolatedStorage.dll", + "FusionName": "System.IO.IsolatedStorage, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", + "FullPath": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.IO.IsolatedStorage.dll", + "RootDir": "C:\\", + "Filename": "System.IO.IsolatedStorage", + "Extension": ".dll", + "RelativeDir": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "Directory": "Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "RecursiveDir": "", + "ModifiedTime": "2026-04-30 07:38:58.0000000", + "CreatedTime": "2026-05-29 17:24:45.4320328", + "AccessedTime": "2026-06-09 15:16:47.3119959", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.IO.MemoryMappedFiles.dll", + "FileVersion": "8.0.2726.22922", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "AssemblyName": "System.IO.MemoryMappedFiles", + "NuGetPackageVersion": "8.0.27", + "Private": "false", + "PublicKeyToken": "b03f5f7f11d50a3a", + "Version": "", + "FrameworkReferenceVersion": "8.0.27", + "ExternallyResolved": "true", + "NuGetPackageId": "Microsoft.NETCore.App.Ref", + "ResolvedFrom": "{RawFileName}", + "FrameworkReferenceName": "Microsoft.NETCore.App", + "ImageRuntime": "v4.0.30319", + "AssemblyVersion": "8.0.0.0", + "CopyLocal": "false", + "OriginalItemSpec": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref/net8.0/System.IO.MemoryMappedFiles.dll", + "FusionName": "System.IO.MemoryMappedFiles, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", + "FullPath": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.IO.MemoryMappedFiles.dll", + "RootDir": "C:\\", + "Filename": "System.IO.MemoryMappedFiles", + "Extension": ".dll", + "RelativeDir": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "Directory": "Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "RecursiveDir": "", + "ModifiedTime": "2026-04-30 07:38:58.0000000", + "CreatedTime": "2026-05-29 17:24:45.4425196", + "AccessedTime": "2026-06-09 15:16:47.3150002", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "C:\\Users\\admin\\.nuget\\packages\\microsoft.windowsdesktop.app.ref\\8.0.27\\ref\\net8.0\\System.IO.Packaging.dll", + "FileVersion": "8.0.2726.22922", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "AssemblyName": "System.IO.Packaging", + "NuGetPackageVersion": "8.0.27", + "Private": "false", + "PublicKeyToken": "b03f5f7f11d50a3a", + "Version": "", + "FrameworkReferenceVersion": "8.0.27", + "ExternallyResolved": "true", + "NuGetPackageId": "Microsoft.WindowsDesktop.App.Ref", + "ResolvedFrom": "{RawFileName}", + "FrameworkReferenceName": "Microsoft.WindowsDesktop.App.WindowsForms", + "ImageRuntime": "v4.0.30319", + "AssemblyVersion": "8.0.0.0", + "CopyLocal": "false", + "OriginalItemSpec": "C:\\Users\\admin\\.nuget\\packages\\microsoft.windowsdesktop.app.ref\\8.0.27\\ref/net8.0/System.IO.Packaging.dll", + "FusionName": "System.IO.Packaging, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", + "FullPath": "C:\\Users\\admin\\.nuget\\packages\\microsoft.windowsdesktop.app.ref\\8.0.27\\ref\\net8.0\\System.IO.Packaging.dll", + "RootDir": "C:\\", + "Filename": "System.IO.Packaging", + "Extension": ".dll", + "RelativeDir": "C:\\Users\\admin\\.nuget\\packages\\microsoft.windowsdesktop.app.ref\\8.0.27\\ref\\net8.0\\", + "Directory": "Users\\admin\\.nuget\\packages\\microsoft.windowsdesktop.app.ref\\8.0.27\\ref\\net8.0\\", + "RecursiveDir": "", + "ModifiedTime": "2026-04-30 07:26:56.0000000", + "CreatedTime": "2026-05-29 17:24:44.4592348", + "AccessedTime": "2026-06-09 15:08:48.2314835", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.IO.Pipes.AccessControl.dll", + "FileVersion": "8.0.2726.22922", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "AssemblyName": "System.IO.Pipes.AccessControl", + "NuGetPackageVersion": "8.0.27", + "Private": "false", + "PublicKeyToken": "b03f5f7f11d50a3a", + "Version": "", + "FrameworkReferenceVersion": "8.0.27", + "ExternallyResolved": "true", + "NuGetPackageId": "Microsoft.NETCore.App.Ref", + "ResolvedFrom": "{RawFileName}", + "FrameworkReferenceName": "Microsoft.NETCore.App", + "ImageRuntime": "v4.0.30319", + "AssemblyVersion": "8.0.0.0", + "CopyLocal": "false", + "OriginalItemSpec": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref/net8.0/System.IO.Pipes.AccessControl.dll", + "FusionName": "System.IO.Pipes.AccessControl, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", + "FullPath": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.IO.Pipes.AccessControl.dll", + "RootDir": "C:\\", + "Filename": "System.IO.Pipes.AccessControl", + "Extension": ".dll", + "RelativeDir": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "Directory": "Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "RecursiveDir": "", + "ModifiedTime": "2026-04-30 07:39:02.0000000", + "CreatedTime": "2026-05-29 17:24:45.4546881", + "AccessedTime": "2026-06-09 15:16:47.3200067", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.IO.Pipes.dll", + "FileVersion": "8.0.2726.22922", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "AssemblyName": "System.IO.Pipes", + "NuGetPackageVersion": "8.0.27", + "Private": "false", + "PublicKeyToken": "b03f5f7f11d50a3a", + "Version": "", + "FrameworkReferenceVersion": "8.0.27", + "ExternallyResolved": "true", + "NuGetPackageId": "Microsoft.NETCore.App.Ref", + "ResolvedFrom": "{RawFileName}", + "FrameworkReferenceName": "Microsoft.NETCore.App", + "ImageRuntime": "v4.0.30319", + "AssemblyVersion": "8.0.0.0", + "CopyLocal": "false", + "OriginalItemSpec": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref/net8.0/System.IO.Pipes.dll", + "FusionName": "System.IO.Pipes, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", + "FullPath": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.IO.Pipes.dll", + "RootDir": "C:\\", + "Filename": "System.IO.Pipes", + "Extension": ".dll", + "RelativeDir": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "Directory": "Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "RecursiveDir": "", + "ModifiedTime": "2026-04-30 07:38:58.0000000", + "CreatedTime": "2026-05-29 17:24:45.4646898", + "AccessedTime": "2026-06-09 15:16:47.3249938", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "C:\\Users\\admin\\.nuget\\packages\\system.io.ports\\4.6.0\\ref\\netstandard2.0\\System.IO.Ports.dll", + "HintPath": "C:\\Users\\admin\\.nuget\\packages\\system.io.ports\\4.6.0\\ref\\netstandard2.0\\System.IO.Ports.dll", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "NuGetPackageVersion": "4.6.0", + "Private": "false", + "Version": "", + "ExternallyResolved": "true", + "NuGetPackageId": "System.IO.Ports", + "ResolvedFrom": "{HintPathFromItem}", + "ImageRuntime": "v4.0.30319", + "CopyLocal": "false", + "OriginalItemSpec": "C:\\Users\\admin\\.nuget\\packages\\system.io.ports\\4.6.0\\ref\\netstandard2.0\\System.IO.Ports.dll", + "FusionName": "System.IO.Ports, Version=4.0.1.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51", + "PathInPackage": "ref/netstandard2.0/System.IO.Ports.dll", + "NuGetSourceType": "Package", + "FullPath": "C:\\Users\\admin\\.nuget\\packages\\system.io.ports\\4.6.0\\ref\\netstandard2.0\\System.IO.Ports.dll", + "RootDir": "C:\\", + "Filename": "System.IO.Ports", + "Extension": ".dll", + "RelativeDir": "C:\\Users\\admin\\.nuget\\packages\\system.io.ports\\4.6.0\\ref\\netstandard2.0\\", + "Directory": "Users\\admin\\.nuget\\packages\\system.io.ports\\4.6.0\\ref\\netstandard2.0\\", + "RecursiveDir": "", + "ModifiedTime": "2019-09-13 10:25:16.0000000", + "CreatedTime": "2026-03-24 15:22:34.6043255", + "AccessedTime": "2026-06-09 15:08:48.2157260", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.IO.UnmanagedMemoryStream.dll", + "FileVersion": "8.0.2726.22922", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "AssemblyName": "System.IO.UnmanagedMemoryStream", + "NuGetPackageVersion": "8.0.27", + "Private": "false", + "PublicKeyToken": "b03f5f7f11d50a3a", + "Version": "", + "FrameworkReferenceVersion": "8.0.27", + "ExternallyResolved": "true", + "NuGetPackageId": "Microsoft.NETCore.App.Ref", + "ResolvedFrom": "{RawFileName}", + "FrameworkReferenceName": "Microsoft.NETCore.App", + "ImageRuntime": "v4.0.30319", + "AssemblyVersion": "8.0.0.0", + "CopyLocal": "false", + "OriginalItemSpec": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref/net8.0/System.IO.UnmanagedMemoryStream.dll", + "FusionName": "System.IO.UnmanagedMemoryStream, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", + "FullPath": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.IO.UnmanagedMemoryStream.dll", + "RootDir": "C:\\", + "Filename": "System.IO.UnmanagedMemoryStream", + "Extension": ".dll", + "RelativeDir": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "Directory": "Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "RecursiveDir": "", + "ModifiedTime": "2026-04-30 07:43:06.0000000", + "CreatedTime": "2026-05-29 17:24:45.4740873", + "AccessedTime": "2026-06-09 15:16:47.3289948", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Linq.dll", + "FileVersion": "8.0.2726.22922", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "AssemblyName": "System.Linq", + "NuGetPackageVersion": "8.0.27", + "Private": "false", + "PublicKeyToken": "b03f5f7f11d50a3a", + "Version": "", + "FrameworkReferenceVersion": "8.0.27", + "ExternallyResolved": "true", + "NuGetPackageId": "Microsoft.NETCore.App.Ref", + "ResolvedFrom": "{RawFileName}", + "FrameworkReferenceName": "Microsoft.NETCore.App", + "ImageRuntime": "v4.0.30319", + "AssemblyVersion": "8.0.0.0", + "CopyLocal": "false", + "OriginalItemSpec": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref/net8.0/System.Linq.dll", + "FusionName": "System.Linq, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", + "FullPath": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Linq.dll", + "RootDir": "C:\\", + "Filename": "System.Linq", + "Extension": ".dll", + "RelativeDir": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "Directory": "Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "RecursiveDir": "", + "ModifiedTime": "2026-04-30 07:38:58.0000000", + "CreatedTime": "2026-05-29 17:24:45.4820856", + "AccessedTime": "2026-06-09 15:16:47.3319943", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Linq.Expressions.dll", + "FileVersion": "8.0.2726.22922", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "AssemblyName": "System.Linq.Expressions", + "NuGetPackageVersion": "8.0.27", + "Private": "false", + "PublicKeyToken": "b03f5f7f11d50a3a", + "Version": "", + "FrameworkReferenceVersion": "8.0.27", + "ExternallyResolved": "true", + "NuGetPackageId": "Microsoft.NETCore.App.Ref", + "ResolvedFrom": "{RawFileName}", + "FrameworkReferenceName": "Microsoft.NETCore.App", + "ImageRuntime": "v4.0.30319", + "AssemblyVersion": "8.0.0.0", + "CopyLocal": "false", + "OriginalItemSpec": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref/net8.0/System.Linq.Expressions.dll", + "FusionName": "System.Linq.Expressions, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", + "FullPath": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Linq.Expressions.dll", + "RootDir": "C:\\", + "Filename": "System.Linq.Expressions", + "Extension": ".dll", + "RelativeDir": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "Directory": "Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "RecursiveDir": "", + "ModifiedTime": "2026-04-30 07:38:58.0000000", + "CreatedTime": "2026-05-29 17:24:45.4934536", + "AccessedTime": "2026-06-09 15:16:47.3339965", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Linq.Parallel.dll", + "FileVersion": "8.0.2726.22922", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "AssemblyName": "System.Linq.Parallel", + "NuGetPackageVersion": "8.0.27", + "Private": "false", + "PublicKeyToken": "b03f5f7f11d50a3a", + "Version": "", + "FrameworkReferenceVersion": "8.0.27", + "ExternallyResolved": "true", + "NuGetPackageId": "Microsoft.NETCore.App.Ref", + "ResolvedFrom": "{RawFileName}", + "FrameworkReferenceName": "Microsoft.NETCore.App", + "ImageRuntime": "v4.0.30319", + "AssemblyVersion": "8.0.0.0", + "CopyLocal": "false", + "OriginalItemSpec": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref/net8.0/System.Linq.Parallel.dll", + "FusionName": "System.Linq.Parallel, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", + "FullPath": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Linq.Parallel.dll", + "RootDir": "C:\\", + "Filename": "System.Linq.Parallel", + "Extension": ".dll", + "RelativeDir": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "Directory": "Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "RecursiveDir": "", + "ModifiedTime": "2026-04-30 07:39:02.0000000", + "CreatedTime": "2026-05-29 17:24:45.5019853", + "AccessedTime": "2026-06-09 15:16:47.3379971", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Linq.Queryable.dll", + "FileVersion": "8.0.2726.22922", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "AssemblyName": "System.Linq.Queryable", + "NuGetPackageVersion": "8.0.27", + "Private": "false", + "PublicKeyToken": "b03f5f7f11d50a3a", + "Version": "", + "FrameworkReferenceVersion": "8.0.27", + "ExternallyResolved": "true", + "NuGetPackageId": "Microsoft.NETCore.App.Ref", + "ResolvedFrom": "{RawFileName}", + "FrameworkReferenceName": "Microsoft.NETCore.App", + "ImageRuntime": "v4.0.30319", + "AssemblyVersion": "8.0.0.0", + "CopyLocal": "false", + "OriginalItemSpec": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref/net8.0/System.Linq.Queryable.dll", + "FusionName": "System.Linq.Queryable, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", + "FullPath": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Linq.Queryable.dll", + "RootDir": "C:\\", + "Filename": "System.Linq.Queryable", + "Extension": ".dll", + "RelativeDir": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "Directory": "Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "RecursiveDir": "", + "ModifiedTime": "2026-04-30 07:39:02.0000000", + "CreatedTime": "2026-05-29 17:24:45.5104968", + "AccessedTime": "2026-06-09 15:16:47.3429977", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Memory.dll", + "FileVersion": "8.0.2726.22922", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "AssemblyName": "System.Memory", + "NuGetPackageVersion": "8.0.27", + "Private": "false", + "PublicKeyToken": "cc7b13ffcd2ddd51", + "Version": "", + "FrameworkReferenceVersion": "8.0.27", + "ExternallyResolved": "true", + "NuGetPackageId": "Microsoft.NETCore.App.Ref", + "ResolvedFrom": "{RawFileName}", + "FrameworkReferenceName": "Microsoft.NETCore.App", + "ImageRuntime": "v4.0.30319", + "AssemblyVersion": "8.0.0.0", + "CopyLocal": "false", + "OriginalItemSpec": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref/net8.0/System.Memory.dll", + "FusionName": "System.Memory, Version=8.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51", + "FullPath": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Memory.dll", + "RootDir": "C:\\", + "Filename": "System.Memory", + "Extension": ".dll", + "RelativeDir": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "Directory": "Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "RecursiveDir": "", + "ModifiedTime": "2026-04-30 07:38:58.0000000", + "CreatedTime": "2026-05-29 17:24:45.5208490", + "AccessedTime": "2026-06-09 15:16:47.3469943", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Net.dll", + "FileVersion": "8.0.2726.22922", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "AssemblyName": "System.Net", + "NuGetPackageVersion": "8.0.27", + "Private": "false", + "PublicKeyToken": "b03f5f7f11d50a3a", + "Version": "", + "FrameworkReferenceVersion": "8.0.27", + "ExternallyResolved": "true", + "NuGetPackageId": "Microsoft.NETCore.App.Ref", + "ResolvedFrom": "{RawFileName}", + "FrameworkReferenceName": "Microsoft.NETCore.App", + "ImageRuntime": "v4.0.30319", + "AssemblyVersion": "4.0.0.0", + "CopyLocal": "false", + "OriginalItemSpec": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref/net8.0/System.Net.dll", + "FusionName": "System.Net, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", + "FullPath": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Net.dll", + "RootDir": "C:\\", + "Filename": "System.Net", + "Extension": ".dll", + "RelativeDir": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "Directory": "Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "RecursiveDir": "", + "ModifiedTime": "2026-04-30 07:42:54.0000000", + "CreatedTime": "2026-05-29 17:24:45.5268462", + "AccessedTime": "2026-06-09 15:16:47.3502430", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Net.Http.dll", + "FileVersion": "8.0.2726.22922", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "AssemblyName": "System.Net.Http", + "NuGetPackageVersion": "8.0.27", + "Private": "false", + "PublicKeyToken": "b03f5f7f11d50a3a", + "Version": "", + "FrameworkReferenceVersion": "8.0.27", + "ExternallyResolved": "true", + "NuGetPackageId": "Microsoft.NETCore.App.Ref", + "ResolvedFrom": "{RawFileName}", + "FrameworkReferenceName": "Microsoft.NETCore.App", + "ImageRuntime": "v4.0.30319", + "AssemblyVersion": "8.0.0.0", + "CopyLocal": "false", + "OriginalItemSpec": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref/net8.0/System.Net.Http.dll", + "FusionName": "System.Net.Http, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", + "FullPath": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Net.Http.dll", + "RootDir": "C:\\", + "Filename": "System.Net.Http", + "Extension": ".dll", + "RelativeDir": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "Directory": "Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "RecursiveDir": "", + "ModifiedTime": "2026-04-30 07:39:04.0000000", + "CreatedTime": "2026-05-29 17:24:45.5340727", + "AccessedTime": "2026-06-09 15:16:47.3572524", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Net.Http.Json.dll", + "FileVersion": "8.0.2726.22922", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "AssemblyName": "System.Net.Http.Json", + "NuGetPackageVersion": "8.0.27", + "Private": "false", + "PublicKeyToken": "cc7b13ffcd2ddd51", + "Version": "", + "FrameworkReferenceVersion": "8.0.27", + "ExternallyResolved": "true", + "NuGetPackageId": "Microsoft.NETCore.App.Ref", + "ResolvedFrom": "{RawFileName}", + "FrameworkReferenceName": "Microsoft.NETCore.App", + "ImageRuntime": "v4.0.30319", + "AssemblyVersion": "8.0.0.0", + "CopyLocal": "false", + "OriginalItemSpec": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref/net8.0/System.Net.Http.Json.dll", + "FusionName": "System.Net.Http.Json, Version=8.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51", + "FullPath": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Net.Http.Json.dll", + "RootDir": "C:\\", + "Filename": "System.Net.Http.Json", + "Extension": ".dll", + "RelativeDir": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "Directory": "Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "RecursiveDir": "", + "ModifiedTime": "2026-04-30 07:39:04.0000000", + "CreatedTime": "2026-05-29 17:24:45.5410717", + "AccessedTime": "2026-06-09 15:16:47.3602532", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Net.HttpListener.dll", + "FileVersion": "8.0.2726.22922", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "AssemblyName": "System.Net.HttpListener", + "NuGetPackageVersion": "8.0.27", + "Private": "false", + "PublicKeyToken": "cc7b13ffcd2ddd51", + "Version": "", + "FrameworkReferenceVersion": "8.0.27", + "ExternallyResolved": "true", + "NuGetPackageId": "Microsoft.NETCore.App.Ref", + "ResolvedFrom": "{RawFileName}", + "FrameworkReferenceName": "Microsoft.NETCore.App", + "ImageRuntime": "v4.0.30319", + "AssemblyVersion": "8.0.0.0", + "CopyLocal": "false", + "OriginalItemSpec": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref/net8.0/System.Net.HttpListener.dll", + "FusionName": "System.Net.HttpListener, Version=8.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51", + "FullPath": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Net.HttpListener.dll", + "RootDir": "C:\\", + "Filename": "System.Net.HttpListener", + "Extension": ".dll", + "RelativeDir": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "Directory": "Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "RecursiveDir": "", + "ModifiedTime": "2026-04-30 07:39:04.0000000", + "CreatedTime": "2026-05-29 17:24:45.5514224", + "AccessedTime": "2026-06-09 15:16:47.3612537", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Net.Mail.dll", + "FileVersion": "8.0.2726.22922", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "AssemblyName": "System.Net.Mail", + "NuGetPackageVersion": "8.0.27", + "Private": "false", + "PublicKeyToken": "cc7b13ffcd2ddd51", + "Version": "", + "FrameworkReferenceVersion": "8.0.27", + "ExternallyResolved": "true", + "NuGetPackageId": "Microsoft.NETCore.App.Ref", + "ResolvedFrom": "{RawFileName}", + "FrameworkReferenceName": "Microsoft.NETCore.App", + "ImageRuntime": "v4.0.30319", + "AssemblyVersion": "8.0.0.0", + "CopyLocal": "false", + "OriginalItemSpec": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref/net8.0/System.Net.Mail.dll", + "FusionName": "System.Net.Mail, Version=8.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51", + "FullPath": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Net.Mail.dll", + "RootDir": "C:\\", + "Filename": "System.Net.Mail", + "Extension": ".dll", + "RelativeDir": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "Directory": "Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "RecursiveDir": "", + "ModifiedTime": "2026-04-30 07:39:04.0000000", + "CreatedTime": "2026-05-29 17:24:45.5594221", + "AccessedTime": "2026-06-09 15:16:47.3662527", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Net.NameResolution.dll", + "FileVersion": "8.0.2726.22922", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "AssemblyName": "System.Net.NameResolution", + "NuGetPackageVersion": "8.0.27", + "Private": "false", + "PublicKeyToken": "b03f5f7f11d50a3a", + "Version": "", + "FrameworkReferenceVersion": "8.0.27", + "ExternallyResolved": "true", + "NuGetPackageId": "Microsoft.NETCore.App.Ref", + "ResolvedFrom": "{RawFileName}", + "FrameworkReferenceName": "Microsoft.NETCore.App", + "ImageRuntime": "v4.0.30319", + "AssemblyVersion": "8.0.0.0", + "CopyLocal": "false", + "OriginalItemSpec": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref/net8.0/System.Net.NameResolution.dll", + "FusionName": "System.Net.NameResolution, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", + "FullPath": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Net.NameResolution.dll", + "RootDir": "C:\\", + "Filename": "System.Net.NameResolution", + "Extension": ".dll", + "RelativeDir": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "Directory": "Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "RecursiveDir": "", + "ModifiedTime": "2026-04-30 07:39:00.0000000", + "CreatedTime": "2026-05-29 17:24:45.5682942", + "AccessedTime": "2026-06-09 15:16:47.3721628", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Net.NetworkInformation.dll", + "FileVersion": "8.0.2726.22922", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "AssemblyName": "System.Net.NetworkInformation", + "NuGetPackageVersion": "8.0.27", + "Private": "false", + "PublicKeyToken": "b03f5f7f11d50a3a", + "Version": "", + "FrameworkReferenceVersion": "8.0.27", + "ExternallyResolved": "true", + "NuGetPackageId": "Microsoft.NETCore.App.Ref", + "ResolvedFrom": "{RawFileName}", + "FrameworkReferenceName": "Microsoft.NETCore.App", + "ImageRuntime": "v4.0.30319", + "AssemblyVersion": "8.0.0.0", + "CopyLocal": "false", + "OriginalItemSpec": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref/net8.0/System.Net.NetworkInformation.dll", + "FusionName": "System.Net.NetworkInformation, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", + "FullPath": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Net.NetworkInformation.dll", + "RootDir": "C:\\", + "Filename": "System.Net.NetworkInformation", + "Extension": ".dll", + "RelativeDir": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "Directory": "Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "RecursiveDir": "", + "ModifiedTime": "2026-04-30 07:39:00.0000000", + "CreatedTime": "2026-05-29 17:24:45.5772934", + "AccessedTime": "2026-06-09 15:16:47.3751640", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Net.Ping.dll", + "FileVersion": "8.0.2726.22922", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "AssemblyName": "System.Net.Ping", + "NuGetPackageVersion": "8.0.27", + "Private": "false", + "PublicKeyToken": "b03f5f7f11d50a3a", + "Version": "", + "FrameworkReferenceVersion": "8.0.27", + "ExternallyResolved": "true", + "NuGetPackageId": "Microsoft.NETCore.App.Ref", + "ResolvedFrom": "{RawFileName}", + "FrameworkReferenceName": "Microsoft.NETCore.App", + "ImageRuntime": "v4.0.30319", + "AssemblyVersion": "8.0.0.0", + "CopyLocal": "false", + "OriginalItemSpec": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref/net8.0/System.Net.Ping.dll", + "FusionName": "System.Net.Ping, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", + "FullPath": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Net.Ping.dll", + "RootDir": "C:\\", + "Filename": "System.Net.Ping", + "Extension": ".dll", + "RelativeDir": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "Directory": "Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "RecursiveDir": "", + "ModifiedTime": "2026-04-30 07:39:00.0000000", + "CreatedTime": "2026-05-29 17:24:45.5852938", + "AccessedTime": "2026-06-09 15:16:47.3781641", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Net.Primitives.dll", + "FileVersion": "8.0.2726.22922", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "AssemblyName": "System.Net.Primitives", + "NuGetPackageVersion": "8.0.27", + "Private": "false", + "PublicKeyToken": "b03f5f7f11d50a3a", + "Version": "", + "FrameworkReferenceVersion": "8.0.27", + "ExternallyResolved": "true", + "NuGetPackageId": "Microsoft.NETCore.App.Ref", + "ResolvedFrom": "{RawFileName}", + "FrameworkReferenceName": "Microsoft.NETCore.App", + "ImageRuntime": "v4.0.30319", + "AssemblyVersion": "8.0.0.0", + "CopyLocal": "false", + "OriginalItemSpec": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref/net8.0/System.Net.Primitives.dll", + "FusionName": "System.Net.Primitives, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", + "FullPath": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Net.Primitives.dll", + "RootDir": "C:\\", + "Filename": "System.Net.Primitives", + "Extension": ".dll", + "RelativeDir": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "Directory": "Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "RecursiveDir": "", + "ModifiedTime": "2026-04-30 07:39:00.0000000", + "CreatedTime": "2026-05-29 17:24:45.5944601", + "AccessedTime": "2026-06-09 15:16:47.3829823", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Net.Quic.dll", + "FileVersion": "8.0.2726.22922", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "AssemblyName": "System.Net.Quic", + "NuGetPackageVersion": "8.0.27", + "Private": "false", + "PublicKeyToken": "b03f5f7f11d50a3a", + "Version": "", + "FrameworkReferenceVersion": "8.0.27", + "ExternallyResolved": "true", + "NuGetPackageId": "Microsoft.NETCore.App.Ref", + "ResolvedFrom": "{RawFileName}", + "FrameworkReferenceName": "Microsoft.NETCore.App", + "ImageRuntime": "v4.0.30319", + "AssemblyVersion": "8.0.0.0", + "CopyLocal": "false", + "OriginalItemSpec": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref/net8.0/System.Net.Quic.dll", + "FusionName": "System.Net.Quic, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", + "FullPath": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Net.Quic.dll", + "RootDir": "C:\\", + "Filename": "System.Net.Quic", + "Extension": ".dll", + "RelativeDir": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "Directory": "Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "RecursiveDir": "", + "ModifiedTime": "2026-04-30 07:39:02.0000000", + "CreatedTime": "2026-05-29 17:24:45.6024535", + "AccessedTime": "2026-06-09 15:16:47.3879837", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Net.Requests.dll", + "FileVersion": "8.0.2726.22922", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "AssemblyName": "System.Net.Requests", + "NuGetPackageVersion": "8.0.27", + "Private": "false", + "PublicKeyToken": "b03f5f7f11d50a3a", + "Version": "", + "FrameworkReferenceVersion": "8.0.27", + "ExternallyResolved": "true", + "NuGetPackageId": "Microsoft.NETCore.App.Ref", + "ResolvedFrom": "{RawFileName}", + "FrameworkReferenceName": "Microsoft.NETCore.App", + "ImageRuntime": "v4.0.30319", + "AssemblyVersion": "8.0.0.0", + "CopyLocal": "false", + "OriginalItemSpec": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref/net8.0/System.Net.Requests.dll", + "FusionName": "System.Net.Requests, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", + "FullPath": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Net.Requests.dll", + "RootDir": "C:\\", + "Filename": "System.Net.Requests", + "Extension": ".dll", + "RelativeDir": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "Directory": "Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "RecursiveDir": "", + "ModifiedTime": "2026-04-30 07:39:04.0000000", + "CreatedTime": "2026-05-29 17:24:45.6104571", + "AccessedTime": "2026-06-09 15:16:47.3919846", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Net.Security.dll", + "FileVersion": "8.0.2726.22922", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "AssemblyName": "System.Net.Security", + "NuGetPackageVersion": "8.0.27", + "Private": "false", + "PublicKeyToken": "b03f5f7f11d50a3a", + "Version": "", + "FrameworkReferenceVersion": "8.0.27", + "ExternallyResolved": "true", + "NuGetPackageId": "Microsoft.NETCore.App.Ref", + "ResolvedFrom": "{RawFileName}", + "FrameworkReferenceName": "Microsoft.NETCore.App", + "ImageRuntime": "v4.0.30319", + "AssemblyVersion": "8.0.0.0", + "CopyLocal": "false", + "OriginalItemSpec": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref/net8.0/System.Net.Security.dll", + "FusionName": "System.Net.Security, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", + "FullPath": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Net.Security.dll", + "RootDir": "C:\\", + "Filename": "System.Net.Security", + "Extension": ".dll", + "RelativeDir": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "Directory": "Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "RecursiveDir": "", + "ModifiedTime": "2026-04-30 07:39:02.0000000", + "CreatedTime": "2026-05-29 17:24:45.6174476", + "AccessedTime": "2026-06-09 15:16:47.3963450", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Net.ServicePoint.dll", + "FileVersion": "8.0.2726.22922", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "AssemblyName": "System.Net.ServicePoint", + "NuGetPackageVersion": "8.0.27", + "Private": "false", + "PublicKeyToken": "cc7b13ffcd2ddd51", + "Version": "", + "FrameworkReferenceVersion": "8.0.27", + "ExternallyResolved": "true", + "NuGetPackageId": "Microsoft.NETCore.App.Ref", + "ResolvedFrom": "{RawFileName}", + "FrameworkReferenceName": "Microsoft.NETCore.App", + "ImageRuntime": "v4.0.30319", + "AssemblyVersion": "8.0.0.0", + "CopyLocal": "false", + "OriginalItemSpec": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref/net8.0/System.Net.ServicePoint.dll", + "FusionName": "System.Net.ServicePoint, Version=8.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51", + "FullPath": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Net.ServicePoint.dll", + "RootDir": "C:\\", + "Filename": "System.Net.ServicePoint", + "Extension": ".dll", + "RelativeDir": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "Directory": "Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "RecursiveDir": "", + "ModifiedTime": "2026-04-30 07:39:02.0000000", + "CreatedTime": "2026-05-29 17:24:45.6241072", + "AccessedTime": "2026-06-09 15:16:47.4030187", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Net.Sockets.dll", + "FileVersion": "8.0.2726.22922", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "AssemblyName": "System.Net.Sockets", + "NuGetPackageVersion": "8.0.27", + "Private": "false", + "PublicKeyToken": "b03f5f7f11d50a3a", + "Version": "", + "FrameworkReferenceVersion": "8.0.27", + "ExternallyResolved": "true", + "NuGetPackageId": "Microsoft.NETCore.App.Ref", + "ResolvedFrom": "{RawFileName}", + "FrameworkReferenceName": "Microsoft.NETCore.App", + "ImageRuntime": "v4.0.30319", + "AssemblyVersion": "8.0.0.0", + "CopyLocal": "false", + "OriginalItemSpec": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref/net8.0/System.Net.Sockets.dll", + "FusionName": "System.Net.Sockets, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", + "FullPath": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Net.Sockets.dll", + "RootDir": "C:\\", + "Filename": "System.Net.Sockets", + "Extension": ".dll", + "RelativeDir": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "Directory": "Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "RecursiveDir": "", + "ModifiedTime": "2026-04-30 07:39:00.0000000", + "CreatedTime": "2026-05-29 17:24:45.6321056", + "AccessedTime": "2026-06-09 15:16:47.4070197", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Net.WebClient.dll", + "FileVersion": "8.0.2726.22922", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "AssemblyName": "System.Net.WebClient", + "NuGetPackageVersion": "8.0.27", + "Private": "false", + "PublicKeyToken": "cc7b13ffcd2ddd51", + "Version": "", + "FrameworkReferenceVersion": "8.0.27", + "ExternallyResolved": "true", + "NuGetPackageId": "Microsoft.NETCore.App.Ref", + "ResolvedFrom": "{RawFileName}", + "FrameworkReferenceName": "Microsoft.NETCore.App", + "ImageRuntime": "v4.0.30319", + "AssemblyVersion": "8.0.0.0", + "CopyLocal": "false", + "OriginalItemSpec": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref/net8.0/System.Net.WebClient.dll", + "FusionName": "System.Net.WebClient, Version=8.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51", + "FullPath": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Net.WebClient.dll", + "RootDir": "C:\\", + "Filename": "System.Net.WebClient", + "Extension": ".dll", + "RelativeDir": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "Directory": "Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "RecursiveDir": "", + "ModifiedTime": "2026-04-30 07:39:04.0000000", + "CreatedTime": "2026-05-29 17:24:45.6401422", + "AccessedTime": "2026-06-09 15:16:47.4137647", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Net.WebHeaderCollection.dll", + "FileVersion": "8.0.2726.22922", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "AssemblyName": "System.Net.WebHeaderCollection", + "NuGetPackageVersion": "8.0.27", + "Private": "false", + "PublicKeyToken": "b03f5f7f11d50a3a", + "Version": "", + "FrameworkReferenceVersion": "8.0.27", + "ExternallyResolved": "true", + "NuGetPackageId": "Microsoft.NETCore.App.Ref", + "ResolvedFrom": "{RawFileName}", + "FrameworkReferenceName": "Microsoft.NETCore.App", + "ImageRuntime": "v4.0.30319", + "AssemblyVersion": "8.0.0.0", + "CopyLocal": "false", + "OriginalItemSpec": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref/net8.0/System.Net.WebHeaderCollection.dll", + "FusionName": "System.Net.WebHeaderCollection, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", + "FullPath": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Net.WebHeaderCollection.dll", + "RootDir": "C:\\", + "Filename": "System.Net.WebHeaderCollection", + "Extension": ".dll", + "RelativeDir": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "Directory": "Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "RecursiveDir": "", + "ModifiedTime": "2026-04-30 07:39:00.0000000", + "CreatedTime": "2026-05-29 17:24:45.6501538", + "AccessedTime": "2026-06-09 15:16:47.4193862", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Net.WebProxy.dll", + "FileVersion": "8.0.2726.22922", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "AssemblyName": "System.Net.WebProxy", + "NuGetPackageVersion": "8.0.27", + "Private": "false", + "PublicKeyToken": "cc7b13ffcd2ddd51", + "Version": "", + "FrameworkReferenceVersion": "8.0.27", + "ExternallyResolved": "true", + "NuGetPackageId": "Microsoft.NETCore.App.Ref", + "ResolvedFrom": "{RawFileName}", + "FrameworkReferenceName": "Microsoft.NETCore.App", + "ImageRuntime": "v4.0.30319", + "AssemblyVersion": "8.0.0.0", + "CopyLocal": "false", + "OriginalItemSpec": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref/net8.0/System.Net.WebProxy.dll", + "FusionName": "System.Net.WebProxy, Version=8.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51", + "FullPath": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Net.WebProxy.dll", + "RootDir": "C:\\", + "Filename": "System.Net.WebProxy", + "Extension": ".dll", + "RelativeDir": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "Directory": "Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "RecursiveDir": "", + "ModifiedTime": "2026-04-30 07:39:04.0000000", + "CreatedTime": "2026-05-29 17:24:45.6587451", + "AccessedTime": "2026-06-09 15:16:47.4233879", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Net.WebSockets.Client.dll", + "FileVersion": "8.0.2726.22922", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "AssemblyName": "System.Net.WebSockets.Client", + "NuGetPackageVersion": "8.0.27", + "Private": "false", + "PublicKeyToken": "b03f5f7f11d50a3a", + "Version": "", + "FrameworkReferenceVersion": "8.0.27", + "ExternallyResolved": "true", + "NuGetPackageId": "Microsoft.NETCore.App.Ref", + "ResolvedFrom": "{RawFileName}", + "FrameworkReferenceName": "Microsoft.NETCore.App", + "ImageRuntime": "v4.0.30319", + "AssemblyVersion": "8.0.0.0", + "CopyLocal": "false", + "OriginalItemSpec": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref/net8.0/System.Net.WebSockets.Client.dll", + "FusionName": "System.Net.WebSockets.Client, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", + "FullPath": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Net.WebSockets.Client.dll", + "RootDir": "C:\\", + "Filename": "System.Net.WebSockets.Client", + "Extension": ".dll", + "RelativeDir": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "Directory": "Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "RecursiveDir": "", + "ModifiedTime": "2026-04-30 07:39:04.0000000", + "CreatedTime": "2026-05-29 17:24:45.6656764", + "AccessedTime": "2026-06-09 15:16:47.4311280", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Net.WebSockets.dll", + "FileVersion": "8.0.2726.22922", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "AssemblyName": "System.Net.WebSockets", + "NuGetPackageVersion": "8.0.27", + "Private": "false", + "PublicKeyToken": "b03f5f7f11d50a3a", + "Version": "", + "FrameworkReferenceVersion": "8.0.27", + "ExternallyResolved": "true", + "NuGetPackageId": "Microsoft.NETCore.App.Ref", + "ResolvedFrom": "{RawFileName}", + "FrameworkReferenceName": "Microsoft.NETCore.App", + "ImageRuntime": "v4.0.30319", + "AssemblyVersion": "8.0.0.0", + "CopyLocal": "false", + "OriginalItemSpec": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref/net8.0/System.Net.WebSockets.dll", + "FusionName": "System.Net.WebSockets, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", + "FullPath": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Net.WebSockets.dll", + "RootDir": "C:\\", + "Filename": "System.Net.WebSockets", + "Extension": ".dll", + "RelativeDir": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "Directory": "Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "RecursiveDir": "", + "ModifiedTime": "2026-04-30 07:39:00.0000000", + "CreatedTime": "2026-05-29 17:24:45.6767223", + "AccessedTime": "2026-06-09 15:16:47.4358836", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Numerics.dll", + "FileVersion": "8.0.2726.22922", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "AssemblyName": "System.Numerics", + "NuGetPackageVersion": "8.0.27", + "Private": "false", + "PublicKeyToken": "b77a5c561934e089", + "Version": "", + "FrameworkReferenceVersion": "8.0.27", + "ExternallyResolved": "true", + "NuGetPackageId": "Microsoft.NETCore.App.Ref", + "ResolvedFrom": "{RawFileName}", + "FrameworkReferenceName": "Microsoft.NETCore.App", + "ImageRuntime": "v4.0.30319", + "AssemblyVersion": "4.0.0.0", + "CopyLocal": "false", + "OriginalItemSpec": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref/net8.0/System.Numerics.dll", + "FusionName": "System.Numerics, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", + "FullPath": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Numerics.dll", + "RootDir": "C:\\", + "Filename": "System.Numerics", + "Extension": ".dll", + "RelativeDir": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "Directory": "Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "RecursiveDir": "", + "ModifiedTime": "2026-04-30 07:42:54.0000000", + "CreatedTime": "2026-05-29 17:24:45.6887227", + "AccessedTime": "2026-06-09 15:16:47.4388834", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Numerics.Vectors.dll", + "FileVersion": "8.0.2726.22922", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "AssemblyName": "System.Numerics.Vectors", + "NuGetPackageVersion": "8.0.27", + "Private": "false", + "PublicKeyToken": "b03f5f7f11d50a3a", + "Version": "", + "FrameworkReferenceVersion": "8.0.27", + "ExternallyResolved": "true", + "NuGetPackageId": "Microsoft.NETCore.App.Ref", + "ResolvedFrom": "{RawFileName}", + "FrameworkReferenceName": "Microsoft.NETCore.App", + "ImageRuntime": "v4.0.30319", + "AssemblyVersion": "8.0.0.0", + "CopyLocal": "false", + "OriginalItemSpec": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref/net8.0/System.Numerics.Vectors.dll", + "FusionName": "System.Numerics.Vectors, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", + "FullPath": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Numerics.Vectors.dll", + "RootDir": "C:\\", + "Filename": "System.Numerics.Vectors", + "Extension": ".dll", + "RelativeDir": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "Directory": "Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "RecursiveDir": "", + "ModifiedTime": "2026-04-30 07:38:58.0000000", + "CreatedTime": "2026-05-29 17:24:45.7017285", + "AccessedTime": "2026-06-09 15:16:47.4461025", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.ObjectModel.dll", + "FileVersion": "8.0.2726.22922", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "AssemblyName": "System.ObjectModel", + "NuGetPackageVersion": "8.0.27", + "Private": "false", + "PublicKeyToken": "b03f5f7f11d50a3a", + "Version": "", + "FrameworkReferenceVersion": "8.0.27", + "ExternallyResolved": "true", + "NuGetPackageId": "Microsoft.NETCore.App.Ref", + "ResolvedFrom": "{RawFileName}", + "FrameworkReferenceName": "Microsoft.NETCore.App", + "ImageRuntime": "v4.0.30319", + "AssemblyVersion": "8.0.0.0", + "CopyLocal": "false", + "OriginalItemSpec": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref/net8.0/System.ObjectModel.dll", + "FusionName": "System.ObjectModel, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", + "FullPath": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.ObjectModel.dll", + "RootDir": "C:\\", + "Filename": "System.ObjectModel", + "Extension": ".dll", + "RelativeDir": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "Directory": "Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "RecursiveDir": "", + "ModifiedTime": "2026-04-30 07:38:58.0000000", + "CreatedTime": "2026-05-29 17:24:45.7117250", + "AccessedTime": "2026-06-09 15:16:47.4494668", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Reflection.DispatchProxy.dll", + "FileVersion": "8.0.2726.22922", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "AssemblyName": "System.Reflection.DispatchProxy", + "NuGetPackageVersion": "8.0.27", + "Private": "false", + "PublicKeyToken": "b03f5f7f11d50a3a", + "Version": "", + "FrameworkReferenceVersion": "8.0.27", + "ExternallyResolved": "true", + "NuGetPackageId": "Microsoft.NETCore.App.Ref", + "ResolvedFrom": "{RawFileName}", + "FrameworkReferenceName": "Microsoft.NETCore.App", + "ImageRuntime": "v4.0.30319", + "AssemblyVersion": "8.0.0.0", + "CopyLocal": "false", + "OriginalItemSpec": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref/net8.0/System.Reflection.DispatchProxy.dll", + "FusionName": "System.Reflection.DispatchProxy, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", + "FullPath": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Reflection.DispatchProxy.dll", + "RootDir": "C:\\", + "Filename": "System.Reflection.DispatchProxy", + "Extension": ".dll", + "RelativeDir": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "Directory": "Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "RecursiveDir": "", + "ModifiedTime": "2026-04-30 07:38:56.0000000", + "CreatedTime": "2026-05-29 17:24:45.7249370", + "AccessedTime": "2026-06-09 15:16:47.4511134", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Reflection.dll", + "FileVersion": "8.0.2726.22922", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "AssemblyName": "System.Reflection", + "NuGetPackageVersion": "8.0.27", + "Private": "false", + "PublicKeyToken": "b03f5f7f11d50a3a", + "Version": "", + "FrameworkReferenceVersion": "8.0.27", + "ExternallyResolved": "true", + "NuGetPackageId": "Microsoft.NETCore.App.Ref", + "ResolvedFrom": "{RawFileName}", + "FrameworkReferenceName": "Microsoft.NETCore.App", + "ImageRuntime": "v4.0.30319", + "AssemblyVersion": "8.0.0.0", + "CopyLocal": "false", + "OriginalItemSpec": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref/net8.0/System.Reflection.dll", + "FusionName": "System.Reflection, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", + "FullPath": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Reflection.dll", + "RootDir": "C:\\", + "Filename": "System.Reflection", + "Extension": ".dll", + "RelativeDir": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "Directory": "Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "RecursiveDir": "", + "ModifiedTime": "2026-04-30 07:42:28.0000000", + "CreatedTime": "2026-05-29 17:24:45.7363021", + "AccessedTime": "2026-06-09 15:16:47.4557796", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Reflection.Emit.dll", + "FileVersion": "8.0.2726.22922", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "AssemblyName": "System.Reflection.Emit", + "NuGetPackageVersion": "8.0.27", + "Private": "false", + "PublicKeyToken": "b03f5f7f11d50a3a", + "Version": "", + "FrameworkReferenceVersion": "8.0.27", + "ExternallyResolved": "true", + "NuGetPackageId": "Microsoft.NETCore.App.Ref", + "ResolvedFrom": "{RawFileName}", + "FrameworkReferenceName": "Microsoft.NETCore.App", + "ImageRuntime": "v4.0.30319", + "AssemblyVersion": "8.0.0.0", + "CopyLocal": "false", + "OriginalItemSpec": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref/net8.0/System.Reflection.Emit.dll", + "FusionName": "System.Reflection.Emit, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", + "FullPath": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Reflection.Emit.dll", + "RootDir": "C:\\", + "Filename": "System.Reflection.Emit", + "Extension": ".dll", + "RelativeDir": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "Directory": "Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "RecursiveDir": "", + "ModifiedTime": "2026-04-30 07:39:02.0000000", + "CreatedTime": "2026-05-29 17:24:45.7483091", + "AccessedTime": "2026-06-09 15:16:47.4587776", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Reflection.Emit.ILGeneration.dll", + "FileVersion": "8.0.2726.22922", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "AssemblyName": "System.Reflection.Emit.ILGeneration", + "NuGetPackageVersion": "8.0.27", + "Private": "false", + "PublicKeyToken": "b03f5f7f11d50a3a", + "Version": "", + "FrameworkReferenceVersion": "8.0.27", + "ExternallyResolved": "true", + "NuGetPackageId": "Microsoft.NETCore.App.Ref", + "ResolvedFrom": "{RawFileName}", + "FrameworkReferenceName": "Microsoft.NETCore.App", + "ImageRuntime": "v4.0.30319", + "AssemblyVersion": "8.0.0.0", + "CopyLocal": "false", + "OriginalItemSpec": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref/net8.0/System.Reflection.Emit.ILGeneration.dll", + "FusionName": "System.Reflection.Emit.ILGeneration, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", + "FullPath": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Reflection.Emit.ILGeneration.dll", + "RootDir": "C:\\", + "Filename": "System.Reflection.Emit.ILGeneration", + "Extension": ".dll", + "RelativeDir": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "Directory": "Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "RecursiveDir": "", + "ModifiedTime": "2026-04-30 07:39:02.0000000", + "CreatedTime": "2026-05-29 17:24:45.7617234", + "AccessedTime": "2026-06-09 15:16:47.4649194", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Reflection.Emit.Lightweight.dll", + "FileVersion": "8.0.2726.22922", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "AssemblyName": "System.Reflection.Emit.Lightweight", + "NuGetPackageVersion": "8.0.27", + "Private": "false", + "PublicKeyToken": "b03f5f7f11d50a3a", + "Version": "", + "FrameworkReferenceVersion": "8.0.27", + "ExternallyResolved": "true", + "NuGetPackageId": "Microsoft.NETCore.App.Ref", + "ResolvedFrom": "{RawFileName}", + "FrameworkReferenceName": "Microsoft.NETCore.App", + "ImageRuntime": "v4.0.30319", + "AssemblyVersion": "8.0.0.0", + "CopyLocal": "false", + "OriginalItemSpec": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref/net8.0/System.Reflection.Emit.Lightweight.dll", + "FusionName": "System.Reflection.Emit.Lightweight, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", + "FullPath": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Reflection.Emit.Lightweight.dll", + "RootDir": "C:\\", + "Filename": "System.Reflection.Emit.Lightweight", + "Extension": ".dll", + "RelativeDir": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "Directory": "Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "RecursiveDir": "", + "ModifiedTime": "2026-04-30 07:39:02.0000000", + "CreatedTime": "2026-05-29 17:24:45.7727322", + "AccessedTime": "2026-06-09 15:16:47.4692003", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Reflection.Extensions.dll", + "FileVersion": "8.0.2726.22922", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "AssemblyName": "System.Reflection.Extensions", + "NuGetPackageVersion": "8.0.27", + "Private": "false", + "PublicKeyToken": "b03f5f7f11d50a3a", + "Version": "", + "FrameworkReferenceVersion": "8.0.27", + "ExternallyResolved": "true", + "NuGetPackageId": "Microsoft.NETCore.App.Ref", + "ResolvedFrom": "{RawFileName}", + "FrameworkReferenceName": "Microsoft.NETCore.App", + "ImageRuntime": "v4.0.30319", + "AssemblyVersion": "8.0.0.0", + "CopyLocal": "false", + "OriginalItemSpec": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref/net8.0/System.Reflection.Extensions.dll", + "FusionName": "System.Reflection.Extensions, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", + "FullPath": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Reflection.Extensions.dll", + "RootDir": "C:\\", + "Filename": "System.Reflection.Extensions", + "Extension": ".dll", + "RelativeDir": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "Directory": "Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "RecursiveDir": "", + "ModifiedTime": "2026-04-30 07:42:28.0000000", + "CreatedTime": "2026-05-29 17:24:45.7849449", + "AccessedTime": "2026-06-09 15:16:47.4728474", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Reflection.Metadata.dll", + "FileVersion": "8.0.2726.22922", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "AssemblyName": "System.Reflection.Metadata", + "NuGetPackageVersion": "8.0.27", + "Private": "false", + "PublicKeyToken": "b03f5f7f11d50a3a", + "Version": "", + "FrameworkReferenceVersion": "8.0.27", + "ExternallyResolved": "true", + "NuGetPackageId": "Microsoft.NETCore.App.Ref", + "ResolvedFrom": "{RawFileName}", + "FrameworkReferenceName": "Microsoft.NETCore.App", + "ImageRuntime": "v4.0.30319", + "AssemblyVersion": "8.0.0.0", + "CopyLocal": "false", + "OriginalItemSpec": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref/net8.0/System.Reflection.Metadata.dll", + "FusionName": "System.Reflection.Metadata, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", + "FullPath": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Reflection.Metadata.dll", + "RootDir": "C:\\", + "Filename": "System.Reflection.Metadata", + "Extension": ".dll", + "RelativeDir": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "Directory": "Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "RecursiveDir": "", + "ModifiedTime": "2026-04-30 07:39:04.0000000", + "CreatedTime": "2026-05-29 17:24:45.7957337", + "AccessedTime": "2026-06-09 15:16:47.4785251", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Reflection.Primitives.dll", + "FileVersion": "8.0.2726.22922", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "AssemblyName": "System.Reflection.Primitives", + "NuGetPackageVersion": "8.0.27", + "Private": "false", + "PublicKeyToken": "b03f5f7f11d50a3a", + "Version": "", + "FrameworkReferenceVersion": "8.0.27", + "ExternallyResolved": "true", + "NuGetPackageId": "Microsoft.NETCore.App.Ref", + "ResolvedFrom": "{RawFileName}", + "FrameworkReferenceName": "Microsoft.NETCore.App", + "ImageRuntime": "v4.0.30319", + "AssemblyVersion": "8.0.0.0", + "CopyLocal": "false", + "OriginalItemSpec": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref/net8.0/System.Reflection.Primitives.dll", + "FusionName": "System.Reflection.Primitives, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", + "FullPath": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Reflection.Primitives.dll", + "RootDir": "C:\\", + "Filename": "System.Reflection.Primitives", + "Extension": ".dll", + "RelativeDir": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "Directory": "Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "RecursiveDir": "", + "ModifiedTime": "2026-04-30 07:39:00.0000000", + "CreatedTime": "2026-05-29 17:24:45.8069931", + "AccessedTime": "2026-06-09 15:16:47.4815248", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Reflection.TypeExtensions.dll", + "FileVersion": "8.0.2726.22922", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "AssemblyName": "System.Reflection.TypeExtensions", + "NuGetPackageVersion": "8.0.27", + "Private": "false", + "PublicKeyToken": "b03f5f7f11d50a3a", + "Version": "", + "FrameworkReferenceVersion": "8.0.27", + "ExternallyResolved": "true", + "NuGetPackageId": "Microsoft.NETCore.App.Ref", + "ResolvedFrom": "{RawFileName}", + "FrameworkReferenceName": "Microsoft.NETCore.App", + "ImageRuntime": "v4.0.30319", + "AssemblyVersion": "8.0.0.0", + "CopyLocal": "false", + "OriginalItemSpec": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref/net8.0/System.Reflection.TypeExtensions.dll", + "FusionName": "System.Reflection.TypeExtensions, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", + "FullPath": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Reflection.TypeExtensions.dll", + "RootDir": "C:\\", + "Filename": "System.Reflection.TypeExtensions", + "Extension": ".dll", + "RelativeDir": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "Directory": "Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "RecursiveDir": "", + "ModifiedTime": "2026-04-30 07:38:56.0000000", + "CreatedTime": "2026-05-29 17:24:45.8193893", + "AccessedTime": "2026-06-09 15:16:47.4842445", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "C:\\Users\\admin\\.nuget\\packages\\microsoft.windowsdesktop.app.ref\\8.0.27\\ref\\net8.0\\System.Resources.Extensions.dll", + "FileVersion": "8.0.2726.22922", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "AssemblyName": "System.Resources.Extensions", + "NuGetPackageVersion": "8.0.27", + "Private": "false", + "PublicKeyToken": "cc7b13ffcd2ddd51", + "Version": "", + "FrameworkReferenceVersion": "8.0.27", + "ExternallyResolved": "true", + "NuGetPackageId": "Microsoft.WindowsDesktop.App.Ref", + "ResolvedFrom": "{RawFileName}", + "FrameworkReferenceName": "Microsoft.WindowsDesktop.App.WindowsForms", + "ImageRuntime": "v4.0.30319", + "AssemblyVersion": "8.0.0.0", + "CopyLocal": "false", + "OriginalItemSpec": "C:\\Users\\admin\\.nuget\\packages\\microsoft.windowsdesktop.app.ref\\8.0.27\\ref/net8.0/System.Resources.Extensions.dll", + "FusionName": "System.Resources.Extensions, Version=8.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51", + "FullPath": "C:\\Users\\admin\\.nuget\\packages\\microsoft.windowsdesktop.app.ref\\8.0.27\\ref\\net8.0\\System.Resources.Extensions.dll", + "RootDir": "C:\\", + "Filename": "System.Resources.Extensions", + "Extension": ".dll", + "RelativeDir": "C:\\Users\\admin\\.nuget\\packages\\microsoft.windowsdesktop.app.ref\\8.0.27\\ref\\net8.0\\", + "Directory": "Users\\admin\\.nuget\\packages\\microsoft.windowsdesktop.app.ref\\8.0.27\\ref\\net8.0\\", + "RecursiveDir": "", + "ModifiedTime": "2026-04-30 07:27:04.0000000", + "CreatedTime": "2026-05-29 17:24:44.4782340", + "AccessedTime": "2026-06-09 15:08:47.9614560", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Resources.Reader.dll", + "FileVersion": "8.0.2726.22922", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "AssemblyName": "System.Resources.Reader", + "NuGetPackageVersion": "8.0.27", + "Private": "false", + "PublicKeyToken": "b03f5f7f11d50a3a", + "Version": "", + "FrameworkReferenceVersion": "8.0.27", + "ExternallyResolved": "true", + "NuGetPackageId": "Microsoft.NETCore.App.Ref", + "ResolvedFrom": "{RawFileName}", + "FrameworkReferenceName": "Microsoft.NETCore.App", + "ImageRuntime": "v4.0.30319", + "AssemblyVersion": "8.0.0.0", + "CopyLocal": "false", + "OriginalItemSpec": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref/net8.0/System.Resources.Reader.dll", + "FusionName": "System.Resources.Reader, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", + "FullPath": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Resources.Reader.dll", + "RootDir": "C:\\", + "Filename": "System.Resources.Reader", + "Extension": ".dll", + "RelativeDir": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "Directory": "Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "RecursiveDir": "", + "ModifiedTime": "2026-04-30 07:42:26.0000000", + "CreatedTime": "2026-05-29 17:24:45.8336848", + "AccessedTime": "2026-06-09 15:16:47.4875619", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Resources.ResourceManager.dll", + "FileVersion": "8.0.2726.22922", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "AssemblyName": "System.Resources.ResourceManager", + "NuGetPackageVersion": "8.0.27", + "Private": "false", + "PublicKeyToken": "b03f5f7f11d50a3a", + "Version": "", + "FrameworkReferenceVersion": "8.0.27", + "ExternallyResolved": "true", + "NuGetPackageId": "Microsoft.NETCore.App.Ref", + "ResolvedFrom": "{RawFileName}", + "FrameworkReferenceName": "Microsoft.NETCore.App", + "ImageRuntime": "v4.0.30319", + "AssemblyVersion": "8.0.0.0", + "CopyLocal": "false", + "OriginalItemSpec": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref/net8.0/System.Resources.ResourceManager.dll", + "FusionName": "System.Resources.ResourceManager, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", + "FullPath": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Resources.ResourceManager.dll", + "RootDir": "C:\\", + "Filename": "System.Resources.ResourceManager", + "Extension": ".dll", + "RelativeDir": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "Directory": "Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "RecursiveDir": "", + "ModifiedTime": "2026-04-30 07:42:36.0000000", + "CreatedTime": "2026-05-29 17:24:45.8466829", + "AccessedTime": "2026-06-09 15:16:47.4908983", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Resources.Writer.dll", + "FileVersion": "8.0.2726.22922", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "AssemblyName": "System.Resources.Writer", + "NuGetPackageVersion": "8.0.27", + "Private": "false", + "PublicKeyToken": "b03f5f7f11d50a3a", + "Version": "", + "FrameworkReferenceVersion": "8.0.27", + "ExternallyResolved": "true", + "NuGetPackageId": "Microsoft.NETCore.App.Ref", + "ResolvedFrom": "{RawFileName}", + "FrameworkReferenceName": "Microsoft.NETCore.App", + "ImageRuntime": "v4.0.30319", + "AssemblyVersion": "8.0.0.0", + "CopyLocal": "false", + "OriginalItemSpec": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref/net8.0/System.Resources.Writer.dll", + "FusionName": "System.Resources.Writer, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", + "FullPath": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Resources.Writer.dll", + "RootDir": "C:\\", + "Filename": "System.Resources.Writer", + "Extension": ".dll", + "RelativeDir": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "Directory": "Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "RecursiveDir": "", + "ModifiedTime": "2026-04-30 07:38:58.0000000", + "CreatedTime": "2026-05-29 17:24:45.8608685", + "AccessedTime": "2026-06-09 15:16:47.4975432", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Runtime.CompilerServices.Unsafe.dll", + "FileVersion": "8.0.2726.22922", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "AssemblyName": "System.Runtime.CompilerServices.Unsafe", + "NuGetPackageVersion": "8.0.27", + "Private": "false", + "PublicKeyToken": "b03f5f7f11d50a3a", + "Version": "", + "FrameworkReferenceVersion": "8.0.27", + "ExternallyResolved": "true", + "NuGetPackageId": "Microsoft.NETCore.App.Ref", + "ResolvedFrom": "{RawFileName}", + "FrameworkReferenceName": "Microsoft.NETCore.App", + "ImageRuntime": "v4.0.30319", + "AssemblyVersion": "8.0.0.0", + "CopyLocal": "false", + "OriginalItemSpec": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref/net8.0/System.Runtime.CompilerServices.Unsafe.dll", + "FusionName": "System.Runtime.CompilerServices.Unsafe, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", + "FullPath": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Runtime.CompilerServices.Unsafe.dll", + "RootDir": "C:\\", + "Filename": "System.Runtime.CompilerServices.Unsafe", + "Extension": ".dll", + "RelativeDir": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "Directory": "Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "RecursiveDir": "", + "ModifiedTime": "2026-04-30 07:42:28.0000000", + "CreatedTime": "2026-05-29 17:24:45.8762909", + "AccessedTime": "2026-06-09 15:16:47.5015467", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Runtime.CompilerServices.VisualC.dll", + "FileVersion": "8.0.2726.22922", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "AssemblyName": "System.Runtime.CompilerServices.VisualC", + "NuGetPackageVersion": "8.0.27", + "Private": "false", + "PublicKeyToken": "b03f5f7f11d50a3a", + "Version": "", + "FrameworkReferenceVersion": "8.0.27", + "ExternallyResolved": "true", + "NuGetPackageId": "Microsoft.NETCore.App.Ref", + "ResolvedFrom": "{RawFileName}", + "FrameworkReferenceName": "Microsoft.NETCore.App", + "ImageRuntime": "v4.0.30319", + "AssemblyVersion": "8.0.0.0", + "CopyLocal": "false", + "OriginalItemSpec": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref/net8.0/System.Runtime.CompilerServices.VisualC.dll", + "FusionName": "System.Runtime.CompilerServices.VisualC, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", + "FullPath": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Runtime.CompilerServices.VisualC.dll", + "RootDir": "C:\\", + "Filename": "System.Runtime.CompilerServices.VisualC", + "Extension": ".dll", + "RelativeDir": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "Directory": "Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "RecursiveDir": "", + "ModifiedTime": "2026-04-30 07:38:58.0000000", + "CreatedTime": "2026-05-29 17:24:45.8868640", + "AccessedTime": "2026-06-09 15:16:47.5051975", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Runtime.dll", + "FileVersion": "8.0.2726.22922", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "AssemblyName": "System.Runtime", + "NuGetPackageVersion": "8.0.27", + "Private": "false", + "PublicKeyToken": "b03f5f7f11d50a3a", + "Version": "", + "FrameworkReferenceVersion": "8.0.27", + "ExternallyResolved": "true", + "NuGetPackageId": "Microsoft.NETCore.App.Ref", + "ResolvedFrom": "{RawFileName}", + "FrameworkReferenceName": "Microsoft.NETCore.App", + "ImageRuntime": "v4.0.30319", + "AssemblyVersion": "8.0.0.0", + "CopyLocal": "false", + "OriginalItemSpec": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref/net8.0/System.Runtime.dll", + "FusionName": "System.Runtime, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", + "FullPath": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Runtime.dll", + "RootDir": "C:\\", + "Filename": "System.Runtime", + "Extension": ".dll", + "RelativeDir": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "Directory": "Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "RecursiveDir": "", + "ModifiedTime": "2026-04-30 07:38:56.0000000", + "CreatedTime": "2026-05-29 17:24:45.8979115", + "AccessedTime": "2026-06-09 15:16:47.5105423", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Runtime.Extensions.dll", + "FileVersion": "8.0.2726.22922", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "AssemblyName": "System.Runtime.Extensions", + "NuGetPackageVersion": "8.0.27", + "Private": "false", + "PublicKeyToken": "b03f5f7f11d50a3a", + "Version": "", + "FrameworkReferenceVersion": "8.0.27", + "ExternallyResolved": "true", + "NuGetPackageId": "Microsoft.NETCore.App.Ref", + "ResolvedFrom": "{RawFileName}", + "FrameworkReferenceName": "Microsoft.NETCore.App", + "ImageRuntime": "v4.0.30319", + "AssemblyVersion": "8.0.0.0", + "CopyLocal": "false", + "OriginalItemSpec": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref/net8.0/System.Runtime.Extensions.dll", + "FusionName": "System.Runtime.Extensions, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", + "FullPath": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Runtime.Extensions.dll", + "RootDir": "C:\\", + "Filename": "System.Runtime.Extensions", + "Extension": ".dll", + "RelativeDir": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "Directory": "Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "RecursiveDir": "", + "ModifiedTime": "2026-04-30 07:42:30.0000000", + "CreatedTime": "2026-05-29 17:24:45.9145767", + "AccessedTime": "2026-06-09 15:16:47.5135384", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Runtime.Handles.dll", + "FileVersion": "8.0.2726.22922", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "AssemblyName": "System.Runtime.Handles", + "NuGetPackageVersion": "8.0.27", + "Private": "false", + "PublicKeyToken": "b03f5f7f11d50a3a", + "Version": "", + "FrameworkReferenceVersion": "8.0.27", + "ExternallyResolved": "true", + "NuGetPackageId": "Microsoft.NETCore.App.Ref", + "ResolvedFrom": "{RawFileName}", + "FrameworkReferenceName": "Microsoft.NETCore.App", + "ImageRuntime": "v4.0.30319", + "AssemblyVersion": "8.0.0.0", + "CopyLocal": "false", + "OriginalItemSpec": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref/net8.0/System.Runtime.Handles.dll", + "FusionName": "System.Runtime.Handles, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", + "FullPath": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Runtime.Handles.dll", + "RootDir": "C:\\", + "Filename": "System.Runtime.Handles", + "Extension": ".dll", + "RelativeDir": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "Directory": "Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "RecursiveDir": "", + "ModifiedTime": "2026-04-30 07:42:30.0000000", + "CreatedTime": "2026-05-29 17:24:45.9289101", + "AccessedTime": "2026-06-09 15:16:47.5156739", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Runtime.InteropServices.dll", + "FileVersion": "8.0.2726.22922", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "AssemblyName": "System.Runtime.InteropServices", + "NuGetPackageVersion": "8.0.27", + "Private": "false", + "PublicKeyToken": "b03f5f7f11d50a3a", + "Version": "", + "FrameworkReferenceVersion": "8.0.27", + "ExternallyResolved": "true", + "NuGetPackageId": "Microsoft.NETCore.App.Ref", + "ResolvedFrom": "{RawFileName}", + "FrameworkReferenceName": "Microsoft.NETCore.App", + "ImageRuntime": "v4.0.30319", + "AssemblyVersion": "8.0.0.0", + "CopyLocal": "false", + "OriginalItemSpec": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref/net8.0/System.Runtime.InteropServices.dll", + "FusionName": "System.Runtime.InteropServices, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", + "FullPath": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Runtime.InteropServices.dll", + "RootDir": "C:\\", + "Filename": "System.Runtime.InteropServices", + "Extension": ".dll", + "RelativeDir": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "Directory": "Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "RecursiveDir": "", + "ModifiedTime": "2026-04-30 07:38:56.0000000", + "CreatedTime": "2026-05-29 17:24:45.9423436", + "AccessedTime": "2026-06-09 15:16:47.5186815", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Runtime.InteropServices.JavaScript.dll", + "FileVersion": "8.0.2726.22922", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "AssemblyName": "System.Runtime.InteropServices.JavaScript", + "NuGetPackageVersion": "8.0.27", + "Private": "false", + "PublicKeyToken": "b03f5f7f11d50a3a", + "Version": "", + "FrameworkReferenceVersion": "8.0.27", + "ExternallyResolved": "true", + "NuGetPackageId": "Microsoft.NETCore.App.Ref", + "ResolvedFrom": "{RawFileName}", + "FrameworkReferenceName": "Microsoft.NETCore.App", + "ImageRuntime": "v4.0.30319", + "AssemblyVersion": "8.0.0.0", + "CopyLocal": "false", + "OriginalItemSpec": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref/net8.0/System.Runtime.InteropServices.JavaScript.dll", + "FusionName": "System.Runtime.InteropServices.JavaScript, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", + "FullPath": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Runtime.InteropServices.JavaScript.dll", + "RootDir": "C:\\", + "Filename": "System.Runtime.InteropServices.JavaScript", + "Extension": ".dll", + "RelativeDir": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "Directory": "Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "RecursiveDir": "", + "ModifiedTime": "2026-04-30 07:39:00.0000000", + "CreatedTime": "2026-05-29 17:24:45.9551391", + "AccessedTime": "2026-06-09 15:16:47.5244108", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Runtime.InteropServices.RuntimeInformation.dll", + "FileVersion": "8.0.2726.22922", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "AssemblyName": "System.Runtime.InteropServices.RuntimeInformation", + "NuGetPackageVersion": "8.0.27", + "Private": "false", + "PublicKeyToken": "b03f5f7f11d50a3a", + "Version": "", + "FrameworkReferenceVersion": "8.0.27", + "ExternallyResolved": "true", + "NuGetPackageId": "Microsoft.NETCore.App.Ref", + "ResolvedFrom": "{RawFileName}", + "FrameworkReferenceName": "Microsoft.NETCore.App", + "ImageRuntime": "v4.0.30319", + "AssemblyVersion": "8.0.0.0", + "CopyLocal": "false", + "OriginalItemSpec": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref/net8.0/System.Runtime.InteropServices.RuntimeInformation.dll", + "FusionName": "System.Runtime.InteropServices.RuntimeInformation, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", + "FullPath": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Runtime.InteropServices.RuntimeInformation.dll", + "RootDir": "C:\\", + "Filename": "System.Runtime.InteropServices.RuntimeInformation", + "Extension": ".dll", + "RelativeDir": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "Directory": "Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "RecursiveDir": "", + "ModifiedTime": "2026-04-30 07:42:34.0000000", + "CreatedTime": "2026-05-29 17:24:45.9650982", + "AccessedTime": "2026-06-09 15:16:47.5300227", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Runtime.Intrinsics.dll", + "FileVersion": "8.0.2726.22922", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "AssemblyName": "System.Runtime.Intrinsics", + "NuGetPackageVersion": "8.0.27", + "Private": "false", + "PublicKeyToken": "cc7b13ffcd2ddd51", + "Version": "", + "FrameworkReferenceVersion": "8.0.27", + "ExternallyResolved": "true", + "NuGetPackageId": "Microsoft.NETCore.App.Ref", + "ResolvedFrom": "{RawFileName}", + "FrameworkReferenceName": "Microsoft.NETCore.App", + "ImageRuntime": "v4.0.30319", + "AssemblyVersion": "8.0.0.0", + "CopyLocal": "false", + "OriginalItemSpec": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref/net8.0/System.Runtime.Intrinsics.dll", + "FusionName": "System.Runtime.Intrinsics, Version=8.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51", + "FullPath": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Runtime.Intrinsics.dll", + "RootDir": "C:\\", + "Filename": "System.Runtime.Intrinsics", + "Extension": ".dll", + "RelativeDir": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "Directory": "Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "RecursiveDir": "", + "ModifiedTime": "2026-04-30 07:39:02.0000000", + "CreatedTime": "2026-05-29 17:24:45.9751660", + "AccessedTime": "2026-06-09 15:16:47.5324428", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Runtime.Loader.dll", + "FileVersion": "8.0.2726.22922", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "AssemblyName": "System.Runtime.Loader", + "NuGetPackageVersion": "8.0.27", + "Private": "false", + "PublicKeyToken": "b03f5f7f11d50a3a", + "Version": "", + "FrameworkReferenceVersion": "8.0.27", + "ExternallyResolved": "true", + "NuGetPackageId": "Microsoft.NETCore.App.Ref", + "ResolvedFrom": "{RawFileName}", + "FrameworkReferenceName": "Microsoft.NETCore.App", + "ImageRuntime": "v4.0.30319", + "AssemblyVersion": "8.0.0.0", + "CopyLocal": "false", + "OriginalItemSpec": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref/net8.0/System.Runtime.Loader.dll", + "FusionName": "System.Runtime.Loader, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", + "FullPath": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Runtime.Loader.dll", + "RootDir": "C:\\", + "Filename": "System.Runtime.Loader", + "Extension": ".dll", + "RelativeDir": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "Directory": "Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "RecursiveDir": "", + "ModifiedTime": "2026-04-30 07:39:00.0000000", + "CreatedTime": "2026-05-29 17:24:45.9886827", + "AccessedTime": "2026-06-09 15:16:47.5364399", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Runtime.Numerics.dll", + "FileVersion": "8.0.2726.22922", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "AssemblyName": "System.Runtime.Numerics", + "NuGetPackageVersion": "8.0.27", + "Private": "false", + "PublicKeyToken": "b03f5f7f11d50a3a", + "Version": "", + "FrameworkReferenceVersion": "8.0.27", + "ExternallyResolved": "true", + "NuGetPackageId": "Microsoft.NETCore.App.Ref", + "ResolvedFrom": "{RawFileName}", + "FrameworkReferenceName": "Microsoft.NETCore.App", + "ImageRuntime": "v4.0.30319", + "AssemblyVersion": "8.0.0.0", + "CopyLocal": "false", + "OriginalItemSpec": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref/net8.0/System.Runtime.Numerics.dll", + "FusionName": "System.Runtime.Numerics, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", + "FullPath": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Runtime.Numerics.dll", + "RootDir": "C:\\", + "Filename": "System.Runtime.Numerics", + "Extension": ".dll", + "RelativeDir": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "Directory": "Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "RecursiveDir": "", + "ModifiedTime": "2026-04-30 07:38:58.0000000", + "CreatedTime": "2026-05-29 17:24:45.9969230", + "AccessedTime": "2026-06-09 15:16:47.5433186", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Runtime.Serialization.dll", + "FileVersion": "8.0.2726.22922", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "AssemblyName": "System.Runtime.Serialization", + "NuGetPackageVersion": "8.0.27", + "Private": "false", + "PublicKeyToken": "b77a5c561934e089", + "Version": "", + "FrameworkReferenceVersion": "8.0.27", + "ExternallyResolved": "true", + "NuGetPackageId": "Microsoft.NETCore.App.Ref", + "ResolvedFrom": "{RawFileName}", + "FrameworkReferenceName": "Microsoft.NETCore.App", + "ImageRuntime": "v4.0.30319", + "AssemblyVersion": "4.0.0.0", + "CopyLocal": "false", + "OriginalItemSpec": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref/net8.0/System.Runtime.Serialization.dll", + "FusionName": "System.Runtime.Serialization, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", + "FullPath": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Runtime.Serialization.dll", + "RootDir": "C:\\", + "Filename": "System.Runtime.Serialization", + "Extension": ".dll", + "RelativeDir": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "Directory": "Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "RecursiveDir": "", + "ModifiedTime": "2026-04-30 07:43:08.0000000", + "CreatedTime": "2026-05-29 17:24:46.0076951", + "AccessedTime": "2026-06-09 15:16:47.5465933", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Runtime.Serialization.Formatters.dll", + "FileVersion": "8.0.2726.22922", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "AssemblyName": "System.Runtime.Serialization.Formatters", + "NuGetPackageVersion": "8.0.27", + "Private": "false", + "PublicKeyToken": "b03f5f7f11d50a3a", + "Version": "", + "FrameworkReferenceVersion": "8.0.27", + "ExternallyResolved": "true", + "NuGetPackageId": "Microsoft.NETCore.App.Ref", + "ResolvedFrom": "{RawFileName}", + "FrameworkReferenceName": "Microsoft.NETCore.App", + "ImageRuntime": "v4.0.30319", + "AssemblyVersion": "8.0.0.0", + "CopyLocal": "false", + "OriginalItemSpec": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref/net8.0/System.Runtime.Serialization.Formatters.dll", + "FusionName": "System.Runtime.Serialization.Formatters, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", + "FullPath": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Runtime.Serialization.Formatters.dll", + "RootDir": "C:\\", + "Filename": "System.Runtime.Serialization.Formatters", + "Extension": ".dll", + "RelativeDir": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "Directory": "Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "RecursiveDir": "", + "ModifiedTime": "2026-04-30 07:39:00.0000000", + "CreatedTime": "2026-05-29 17:24:46.0186187", + "AccessedTime": "2026-06-09 15:16:47.5502276", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Runtime.Serialization.Json.dll", + "FileVersion": "8.0.2726.22922", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "AssemblyName": "System.Runtime.Serialization.Json", + "NuGetPackageVersion": "8.0.27", + "Private": "false", + "PublicKeyToken": "b03f5f7f11d50a3a", + "Version": "", + "FrameworkReferenceVersion": "8.0.27", + "ExternallyResolved": "true", + "NuGetPackageId": "Microsoft.NETCore.App.Ref", + "ResolvedFrom": "{RawFileName}", + "FrameworkReferenceName": "Microsoft.NETCore.App", + "ImageRuntime": "v4.0.30319", + "AssemblyVersion": "8.0.0.0", + "CopyLocal": "false", + "OriginalItemSpec": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref/net8.0/System.Runtime.Serialization.Json.dll", + "FusionName": "System.Runtime.Serialization.Json, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", + "FullPath": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Runtime.Serialization.Json.dll", + "RootDir": "C:\\", + "Filename": "System.Runtime.Serialization.Json", + "Extension": ".dll", + "RelativeDir": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "Directory": "Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "RecursiveDir": "", + "ModifiedTime": "2026-04-30 07:39:02.0000000", + "CreatedTime": "2026-05-29 17:24:46.0372726", + "AccessedTime": "2026-06-09 15:16:47.5564634", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Runtime.Serialization.Primitives.dll", + "FileVersion": "8.0.2726.22922", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "AssemblyName": "System.Runtime.Serialization.Primitives", + "NuGetPackageVersion": "8.0.27", + "Private": "false", + "PublicKeyToken": "b03f5f7f11d50a3a", + "Version": "", + "FrameworkReferenceVersion": "8.0.27", + "ExternallyResolved": "true", + "NuGetPackageId": "Microsoft.NETCore.App.Ref", + "ResolvedFrom": "{RawFileName}", + "FrameworkReferenceName": "Microsoft.NETCore.App", + "ImageRuntime": "v4.0.30319", + "AssemblyVersion": "8.0.0.0", + "CopyLocal": "false", + "OriginalItemSpec": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref/net8.0/System.Runtime.Serialization.Primitives.dll", + "FusionName": "System.Runtime.Serialization.Primitives, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", + "FullPath": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Runtime.Serialization.Primitives.dll", + "RootDir": "C:\\", + "Filename": "System.Runtime.Serialization.Primitives", + "Extension": ".dll", + "RelativeDir": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "Directory": "Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "RecursiveDir": "", + "ModifiedTime": "2026-04-30 07:38:56.0000000", + "CreatedTime": "2026-05-29 17:24:46.0460387", + "AccessedTime": "2026-06-09 15:16:47.5589926", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Runtime.Serialization.Xml.dll", + "FileVersion": "8.0.2726.22922", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "AssemblyName": "System.Runtime.Serialization.Xml", + "NuGetPackageVersion": "8.0.27", + "Private": "false", + "PublicKeyToken": "b03f5f7f11d50a3a", + "Version": "", + "FrameworkReferenceVersion": "8.0.27", + "ExternallyResolved": "true", + "NuGetPackageId": "Microsoft.NETCore.App.Ref", + "ResolvedFrom": "{RawFileName}", + "FrameworkReferenceName": "Microsoft.NETCore.App", + "ImageRuntime": "v4.0.30319", + "AssemblyVersion": "8.0.0.0", + "CopyLocal": "false", + "OriginalItemSpec": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref/net8.0/System.Runtime.Serialization.Xml.dll", + "FusionName": "System.Runtime.Serialization.Xml, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", + "FullPath": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Runtime.Serialization.Xml.dll", + "RootDir": "C:\\", + "Filename": "System.Runtime.Serialization.Xml", + "Extension": ".dll", + "RelativeDir": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "Directory": "Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "RecursiveDir": "", + "ModifiedTime": "2026-04-30 07:39:02.0000000", + "CreatedTime": "2026-05-29 17:24:46.0609215", + "AccessedTime": "2026-06-09 15:16:47.5623519", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Security.AccessControl.dll", + "FileVersion": "8.0.2726.22922", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "AssemblyName": "System.Security.AccessControl", + "NuGetPackageVersion": "8.0.27", + "Private": "false", + "PublicKeyToken": "b03f5f7f11d50a3a", + "Version": "", + "FrameworkReferenceVersion": "8.0.27", + "ExternallyResolved": "true", + "NuGetPackageId": "Microsoft.NETCore.App.Ref", + "ResolvedFrom": "{RawFileName}", + "FrameworkReferenceName": "Microsoft.NETCore.App", + "ImageRuntime": "v4.0.30319", + "AssemblyVersion": "8.0.0.0", + "CopyLocal": "false", + "OriginalItemSpec": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref/net8.0/System.Security.AccessControl.dll", + "FusionName": "System.Security.AccessControl, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", + "FullPath": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Security.AccessControl.dll", + "RootDir": "C:\\", + "Filename": "System.Security.AccessControl", + "Extension": ".dll", + "RelativeDir": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "Directory": "Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "RecursiveDir": "", + "ModifiedTime": "2026-04-30 07:38:58.0000000", + "CreatedTime": "2026-05-29 17:24:46.0737069", + "AccessedTime": "2026-06-09 15:16:47.5672797", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Security.Claims.dll", + "FileVersion": "8.0.2726.22922", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "AssemblyName": "System.Security.Claims", + "NuGetPackageVersion": "8.0.27", + "Private": "false", + "PublicKeyToken": "b03f5f7f11d50a3a", + "Version": "", + "FrameworkReferenceVersion": "8.0.27", + "ExternallyResolved": "true", + "NuGetPackageId": "Microsoft.NETCore.App.Ref", + "ResolvedFrom": "{RawFileName}", + "FrameworkReferenceName": "Microsoft.NETCore.App", + "ImageRuntime": "v4.0.30319", + "AssemblyVersion": "8.0.0.0", + "CopyLocal": "false", + "OriginalItemSpec": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref/net8.0/System.Security.Claims.dll", + "FusionName": "System.Security.Claims, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", + "FullPath": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Security.Claims.dll", + "RootDir": "C:\\", + "Filename": "System.Security.Claims", + "Extension": ".dll", + "RelativeDir": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "Directory": "Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "RecursiveDir": "", + "ModifiedTime": "2026-04-30 07:38:58.0000000", + "CreatedTime": "2026-05-29 17:24:46.0835306", + "AccessedTime": "2026-06-09 15:16:47.5733226", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Security.Cryptography.Algorithms.dll", + "FileVersion": "8.0.2726.22922", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "AssemblyName": "System.Security.Cryptography.Algorithms", + "NuGetPackageVersion": "8.0.27", + "Private": "false", + "PublicKeyToken": "b03f5f7f11d50a3a", + "Version": "", + "FrameworkReferenceVersion": "8.0.27", + "ExternallyResolved": "true", + "NuGetPackageId": "Microsoft.NETCore.App.Ref", + "ResolvedFrom": "{RawFileName}", + "FrameworkReferenceName": "Microsoft.NETCore.App", + "ImageRuntime": "v4.0.30319", + "AssemblyVersion": "8.0.0.0", + "CopyLocal": "false", + "OriginalItemSpec": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref/net8.0/System.Security.Cryptography.Algorithms.dll", + "FusionName": "System.Security.Cryptography.Algorithms, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", + "FullPath": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Security.Cryptography.Algorithms.dll", + "RootDir": "C:\\", + "Filename": "System.Security.Cryptography.Algorithms", + "Extension": ".dll", + "RelativeDir": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "Directory": "Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "RecursiveDir": "", + "ModifiedTime": "2026-04-30 07:42:36.0000000", + "CreatedTime": "2026-05-29 17:24:46.0936439", + "AccessedTime": "2026-06-09 15:16:47.5787843", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Security.Cryptography.Cng.dll", + "FileVersion": "8.0.2726.22922", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "AssemblyName": "System.Security.Cryptography.Cng", + "NuGetPackageVersion": "8.0.27", + "Private": "false", + "PublicKeyToken": "b03f5f7f11d50a3a", + "Version": "", + "FrameworkReferenceVersion": "8.0.27", + "ExternallyResolved": "true", + "NuGetPackageId": "Microsoft.NETCore.App.Ref", + "ResolvedFrom": "{RawFileName}", + "FrameworkReferenceName": "Microsoft.NETCore.App", + "ImageRuntime": "v4.0.30319", + "AssemblyVersion": "8.0.0.0", + "CopyLocal": "false", + "OriginalItemSpec": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref/net8.0/System.Security.Cryptography.Cng.dll", + "FusionName": "System.Security.Cryptography.Cng, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", + "FullPath": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Security.Cryptography.Cng.dll", + "RootDir": "C:\\", + "Filename": "System.Security.Cryptography.Cng", + "Extension": ".dll", + "RelativeDir": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "Directory": "Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "RecursiveDir": "", + "ModifiedTime": "2026-04-30 07:42:38.0000000", + "CreatedTime": "2026-05-29 17:24:46.1035999", + "AccessedTime": "2026-06-09 15:16:47.5819549", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Security.Cryptography.Csp.dll", + "FileVersion": "8.0.2726.22922", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "AssemblyName": "System.Security.Cryptography.Csp", + "NuGetPackageVersion": "8.0.27", + "Private": "false", + "PublicKeyToken": "b03f5f7f11d50a3a", + "Version": "", + "FrameworkReferenceVersion": "8.0.27", + "ExternallyResolved": "true", + "NuGetPackageId": "Microsoft.NETCore.App.Ref", + "ResolvedFrom": "{RawFileName}", + "FrameworkReferenceName": "Microsoft.NETCore.App", + "ImageRuntime": "v4.0.30319", + "AssemblyVersion": "8.0.0.0", + "CopyLocal": "false", + "OriginalItemSpec": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref/net8.0/System.Security.Cryptography.Csp.dll", + "FusionName": "System.Security.Cryptography.Csp, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", + "FullPath": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Security.Cryptography.Csp.dll", + "RootDir": "C:\\", + "Filename": "System.Security.Cryptography.Csp", + "Extension": ".dll", + "RelativeDir": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "Directory": "Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "RecursiveDir": "", + "ModifiedTime": "2026-04-30 07:42:38.0000000", + "CreatedTime": "2026-05-29 17:24:46.1109979", + "AccessedTime": "2026-06-09 15:16:47.5868369", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Security.Cryptography.dll", + "FileVersion": "8.0.2726.22922", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "AssemblyName": "System.Security.Cryptography", + "NuGetPackageVersion": "8.0.27", + "Private": "false", + "PublicKeyToken": "b03f5f7f11d50a3a", + "Version": "", + "FrameworkReferenceVersion": "8.0.27", + "ExternallyResolved": "true", + "NuGetPackageId": "Microsoft.NETCore.App.Ref", + "ResolvedFrom": "{RawFileName}", + "FrameworkReferenceName": "Microsoft.NETCore.App", + "ImageRuntime": "v4.0.30319", + "AssemblyVersion": "8.0.0.0", + "CopyLocal": "false", + "OriginalItemSpec": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref/net8.0/System.Security.Cryptography.dll", + "FusionName": "System.Security.Cryptography, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", + "FullPath": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Security.Cryptography.dll", + "RootDir": "C:\\", + "Filename": "System.Security.Cryptography", + "Extension": ".dll", + "RelativeDir": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "Directory": "Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "RecursiveDir": "", + "ModifiedTime": "2026-04-30 07:39:02.0000000", + "CreatedTime": "2026-05-29 17:24:46.1239008", + "AccessedTime": "2026-06-09 15:16:47.5909749", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Security.Cryptography.Encoding.dll", + "FileVersion": "8.0.2726.22922", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "AssemblyName": "System.Security.Cryptography.Encoding", + "NuGetPackageVersion": "8.0.27", + "Private": "false", + "PublicKeyToken": "b03f5f7f11d50a3a", + "Version": "", + "FrameworkReferenceVersion": "8.0.27", + "ExternallyResolved": "true", + "NuGetPackageId": "Microsoft.NETCore.App.Ref", + "ResolvedFrom": "{RawFileName}", + "FrameworkReferenceName": "Microsoft.NETCore.App", + "ImageRuntime": "v4.0.30319", + "AssemblyVersion": "8.0.0.0", + "CopyLocal": "false", + "OriginalItemSpec": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref/net8.0/System.Security.Cryptography.Encoding.dll", + "FusionName": "System.Security.Cryptography.Encoding, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", + "FullPath": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Security.Cryptography.Encoding.dll", + "RootDir": "C:\\", + "Filename": "System.Security.Cryptography.Encoding", + "Extension": ".dll", + "RelativeDir": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "Directory": "Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "RecursiveDir": "", + "ModifiedTime": "2026-04-30 07:42:36.0000000", + "CreatedTime": "2026-05-29 17:24:46.1349645", + "AccessedTime": "2026-06-09 15:16:47.5939766", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Security.Cryptography.OpenSsl.dll", + "FileVersion": "8.0.2726.22922", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "AssemblyName": "System.Security.Cryptography.OpenSsl", + "NuGetPackageVersion": "8.0.27", + "Private": "false", + "PublicKeyToken": "b03f5f7f11d50a3a", + "Version": "", + "FrameworkReferenceVersion": "8.0.27", + "ExternallyResolved": "true", + "NuGetPackageId": "Microsoft.NETCore.App.Ref", + "ResolvedFrom": "{RawFileName}", + "FrameworkReferenceName": "Microsoft.NETCore.App", + "ImageRuntime": "v4.0.30319", + "AssemblyVersion": "8.0.0.0", + "CopyLocal": "false", + "OriginalItemSpec": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref/net8.0/System.Security.Cryptography.OpenSsl.dll", + "FusionName": "System.Security.Cryptography.OpenSsl, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", + "FullPath": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Security.Cryptography.OpenSsl.dll", + "RootDir": "C:\\", + "Filename": "System.Security.Cryptography.OpenSsl", + "Extension": ".dll", + "RelativeDir": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "Directory": "Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "RecursiveDir": "", + "ModifiedTime": "2026-04-30 07:42:38.0000000", + "CreatedTime": "2026-05-29 17:24:46.1457377", + "AccessedTime": "2026-06-09 15:16:47.5969753", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "C:\\Users\\admin\\.nuget\\packages\\microsoft.windowsdesktop.app.ref\\8.0.27\\ref\\net8.0\\System.Security.Cryptography.Pkcs.dll", + "FileVersion": "8.0.2726.22922", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "AssemblyName": "System.Security.Cryptography.Pkcs", + "NuGetPackageVersion": "8.0.27", + "Private": "false", + "PublicKeyToken": "b03f5f7f11d50a3a", + "Version": "", + "FrameworkReferenceVersion": "8.0.27", + "ExternallyResolved": "true", + "NuGetPackageId": "Microsoft.WindowsDesktop.App.Ref", + "ResolvedFrom": "{RawFileName}", + "FrameworkReferenceName": "Microsoft.WindowsDesktop.App.WindowsForms", + "ImageRuntime": "v4.0.30319", + "AssemblyVersion": "8.0.0.0", + "CopyLocal": "false", + "OriginalItemSpec": "C:\\Users\\admin\\.nuget\\packages\\microsoft.windowsdesktop.app.ref\\8.0.27\\ref/net8.0/System.Security.Cryptography.Pkcs.dll", + "FusionName": "System.Security.Cryptography.Pkcs, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", + "FullPath": "C:\\Users\\admin\\.nuget\\packages\\microsoft.windowsdesktop.app.ref\\8.0.27\\ref\\net8.0\\System.Security.Cryptography.Pkcs.dll", + "RootDir": "C:\\", + "Filename": "System.Security.Cryptography.Pkcs", + "Extension": ".dll", + "RelativeDir": "C:\\Users\\admin\\.nuget\\packages\\microsoft.windowsdesktop.app.ref\\8.0.27\\ref\\net8.0\\", + "Directory": "Users\\admin\\.nuget\\packages\\microsoft.windowsdesktop.app.ref\\8.0.27\\ref\\net8.0\\", + "RecursiveDir": "", + "ModifiedTime": "2026-04-30 07:26:26.0000000", + "CreatedTime": "2026-05-29 17:24:44.4903431", + "AccessedTime": "2026-06-09 15:08:47.7844294", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Security.Cryptography.Primitives.dll", + "FileVersion": "8.0.2726.22922", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "AssemblyName": "System.Security.Cryptography.Primitives", + "NuGetPackageVersion": "8.0.27", + "Private": "false", + "PublicKeyToken": "b03f5f7f11d50a3a", + "Version": "", + "FrameworkReferenceVersion": "8.0.27", + "ExternallyResolved": "true", + "NuGetPackageId": "Microsoft.NETCore.App.Ref", + "ResolvedFrom": "{RawFileName}", + "FrameworkReferenceName": "Microsoft.NETCore.App", + "ImageRuntime": "v4.0.30319", + "AssemblyVersion": "8.0.0.0", + "CopyLocal": "false", + "OriginalItemSpec": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref/net8.0/System.Security.Cryptography.Primitives.dll", + "FusionName": "System.Security.Cryptography.Primitives, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", + "FullPath": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Security.Cryptography.Primitives.dll", + "RootDir": "C:\\", + "Filename": "System.Security.Cryptography.Primitives", + "Extension": ".dll", + "RelativeDir": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "Directory": "Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "RecursiveDir": "", + "ModifiedTime": "2026-04-30 07:42:36.0000000", + "CreatedTime": "2026-05-29 17:24:46.1586320", + "AccessedTime": "2026-06-09 15:16:47.6038954", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "C:\\Users\\admin\\.nuget\\packages\\microsoft.windowsdesktop.app.ref\\8.0.27\\ref\\net8.0\\System.Security.Cryptography.ProtectedData.dll", + "FileVersion": "8.0.2726.22922", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "AssemblyName": "System.Security.Cryptography.ProtectedData", + "NuGetPackageVersion": "8.0.27", + "Private": "false", + "PublicKeyToken": "b03f5f7f11d50a3a", + "Version": "", + "FrameworkReferenceVersion": "8.0.27", + "ExternallyResolved": "true", + "NuGetPackageId": "Microsoft.WindowsDesktop.App.Ref", + "ResolvedFrom": "{RawFileName}", + "FrameworkReferenceName": "Microsoft.WindowsDesktop.App.WindowsForms", + "ImageRuntime": "v4.0.30319", + "AssemblyVersion": "8.0.0.0", + "CopyLocal": "false", + "OriginalItemSpec": "C:\\Users\\admin\\.nuget\\packages\\microsoft.windowsdesktop.app.ref\\8.0.27\\ref/net8.0/System.Security.Cryptography.ProtectedData.dll", + "FusionName": "System.Security.Cryptography.ProtectedData, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", + "FullPath": "C:\\Users\\admin\\.nuget\\packages\\microsoft.windowsdesktop.app.ref\\8.0.27\\ref\\net8.0\\System.Security.Cryptography.ProtectedData.dll", + "RootDir": "C:\\", + "Filename": "System.Security.Cryptography.ProtectedData", + "Extension": ".dll", + "RelativeDir": "C:\\Users\\admin\\.nuget\\packages\\microsoft.windowsdesktop.app.ref\\8.0.27\\ref\\net8.0\\", + "Directory": "Users\\admin\\.nuget\\packages\\microsoft.windowsdesktop.app.ref\\8.0.27\\ref\\net8.0\\", + "RecursiveDir": "", + "ModifiedTime": "2026-04-30 07:26:46.0000000", + "CreatedTime": "2026-05-29 17:24:44.5014683", + "AccessedTime": "2026-06-09 15:08:47.7733098", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Security.Cryptography.X509Certificates.dll", + "FileVersion": "8.0.2726.22922", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "AssemblyName": "System.Security.Cryptography.X509Certificates", + "NuGetPackageVersion": "8.0.27", + "Private": "false", + "PublicKeyToken": "b03f5f7f11d50a3a", + "Version": "", + "FrameworkReferenceVersion": "8.0.27", + "ExternallyResolved": "true", + "NuGetPackageId": "Microsoft.NETCore.App.Ref", + "ResolvedFrom": "{RawFileName}", + "FrameworkReferenceName": "Microsoft.NETCore.App", + "ImageRuntime": "v4.0.30319", + "AssemblyVersion": "8.0.0.0", + "CopyLocal": "false", + "OriginalItemSpec": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref/net8.0/System.Security.Cryptography.X509Certificates.dll", + "FusionName": "System.Security.Cryptography.X509Certificates, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", + "FullPath": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Security.Cryptography.X509Certificates.dll", + "RootDir": "C:\\", + "Filename": "System.Security.Cryptography.X509Certificates", + "Extension": ".dll", + "RelativeDir": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "Directory": "Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "RecursiveDir": "", + "ModifiedTime": "2026-04-30 07:42:38.0000000", + "CreatedTime": "2026-05-29 17:24:46.1694921", + "AccessedTime": "2026-06-09 15:16:47.6071131", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "C:\\Users\\admin\\.nuget\\packages\\microsoft.windowsdesktop.app.ref\\8.0.27\\ref\\net8.0\\System.Security.Cryptography.Xml.dll", + "FileVersion": "8.0.2726.22922", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "AssemblyName": "System.Security.Cryptography.Xml", + "NuGetPackageVersion": "8.0.27", + "Private": "false", + "PublicKeyToken": "cc7b13ffcd2ddd51", + "Version": "", + "FrameworkReferenceVersion": "8.0.27", + "ExternallyResolved": "true", + "NuGetPackageId": "Microsoft.WindowsDesktop.App.Ref", + "ResolvedFrom": "{RawFileName}", + "FrameworkReferenceName": "Microsoft.WindowsDesktop.App.WindowsForms", + "ImageRuntime": "v4.0.30319", + "AssemblyVersion": "8.0.0.0", + "CopyLocal": "false", + "OriginalItemSpec": "C:\\Users\\admin\\.nuget\\packages\\microsoft.windowsdesktop.app.ref\\8.0.27\\ref/net8.0/System.Security.Cryptography.Xml.dll", + "FusionName": "System.Security.Cryptography.Xml, Version=8.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51", + "FullPath": "C:\\Users\\admin\\.nuget\\packages\\microsoft.windowsdesktop.app.ref\\8.0.27\\ref\\net8.0\\System.Security.Cryptography.Xml.dll", + "RootDir": "C:\\", + "Filename": "System.Security.Cryptography.Xml", + "Extension": ".dll", + "RelativeDir": "C:\\Users\\admin\\.nuget\\packages\\microsoft.windowsdesktop.app.ref\\8.0.27\\ref\\net8.0\\", + "Directory": "Users\\admin\\.nuget\\packages\\microsoft.windowsdesktop.app.ref\\8.0.27\\ref\\net8.0\\", + "RecursiveDir": "", + "ModifiedTime": "2026-04-30 07:26:38.0000000", + "CreatedTime": "2026-05-29 17:24:44.5148586", + "AccessedTime": "2026-06-09 15:08:47.7594793", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Security.dll", + "FileVersion": "8.0.2726.22922", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "AssemblyName": "System.Security", + "NuGetPackageVersion": "8.0.27", + "Private": "false", + "PublicKeyToken": "b03f5f7f11d50a3a", + "Version": "", + "FrameworkReferenceVersion": "8.0.27", + "ExternallyResolved": "true", + "NuGetPackageId": "Microsoft.NETCore.App.Ref", + "ResolvedFrom": "{RawFileName}", + "FrameworkReferenceName": "Microsoft.NETCore.App", + "ImageRuntime": "v4.0.30319", + "AssemblyVersion": "4.0.0.0", + "CopyLocal": "false", + "OriginalItemSpec": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref/net8.0/System.Security.dll", + "FusionName": "System.Security, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", + "FullPath": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Security.dll", + "RootDir": "C:\\", + "Filename": "System.Security", + "Extension": ".dll", + "RelativeDir": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "Directory": "Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "RecursiveDir": "", + "ModifiedTime": "2026-04-30 07:42:38.0000000", + "CreatedTime": "2026-05-29 17:24:46.1811344", + "AccessedTime": "2026-06-09 15:16:47.6111218", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "C:\\Users\\admin\\.nuget\\packages\\microsoft.windowsdesktop.app.ref\\8.0.27\\ref\\net8.0\\System.Security.Permissions.dll", + "FileVersion": "8.0.2726.22922", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "AssemblyName": "System.Security.Permissions", + "NuGetPackageVersion": "8.0.27", + "Private": "false", + "PublicKeyToken": "cc7b13ffcd2ddd51", + "Version": "", + "FrameworkReferenceVersion": "8.0.27", + "ExternallyResolved": "true", + "NuGetPackageId": "Microsoft.WindowsDesktop.App.Ref", + "ResolvedFrom": "{RawFileName}", + "FrameworkReferenceName": "Microsoft.WindowsDesktop.App.WindowsForms", + "ImageRuntime": "v4.0.30319", + "AssemblyVersion": "8.0.0.0", + "CopyLocal": "false", + "OriginalItemSpec": "C:\\Users\\admin\\.nuget\\packages\\microsoft.windowsdesktop.app.ref\\8.0.27\\ref/net8.0/System.Security.Permissions.dll", + "FusionName": "System.Security.Permissions, Version=8.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51", + "FullPath": "C:\\Users\\admin\\.nuget\\packages\\microsoft.windowsdesktop.app.ref\\8.0.27\\ref\\net8.0\\System.Security.Permissions.dll", + "RootDir": "C:\\", + "Filename": "System.Security.Permissions", + "Extension": ".dll", + "RelativeDir": "C:\\Users\\admin\\.nuget\\packages\\microsoft.windowsdesktop.app.ref\\8.0.27\\ref\\net8.0\\", + "Directory": "Users\\admin\\.nuget\\packages\\microsoft.windowsdesktop.app.ref\\8.0.27\\ref\\net8.0\\", + "RecursiveDir": "", + "ModifiedTime": "2026-04-30 07:27:22.0000000", + "CreatedTime": "2026-05-29 17:24:44.5248069", + "AccessedTime": "2026-06-09 15:08:47.7471699", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Security.Principal.dll", + "FileVersion": "8.0.2726.22922", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "AssemblyName": "System.Security.Principal", + "NuGetPackageVersion": "8.0.27", + "Private": "false", + "PublicKeyToken": "b03f5f7f11d50a3a", + "Version": "", + "FrameworkReferenceVersion": "8.0.27", + "ExternallyResolved": "true", + "NuGetPackageId": "Microsoft.NETCore.App.Ref", + "ResolvedFrom": "{RawFileName}", + "FrameworkReferenceName": "Microsoft.NETCore.App", + "ImageRuntime": "v4.0.30319", + "AssemblyVersion": "8.0.0.0", + "CopyLocal": "false", + "OriginalItemSpec": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref/net8.0/System.Security.Principal.dll", + "FusionName": "System.Security.Principal, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", + "FullPath": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Security.Principal.dll", + "RootDir": "C:\\", + "Filename": "System.Security.Principal", + "Extension": ".dll", + "RelativeDir": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "Directory": "Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "RecursiveDir": "", + "ModifiedTime": "2026-04-30 07:42:30.0000000", + "CreatedTime": "2026-05-29 17:24:46.1895267", + "AccessedTime": "2026-06-09 15:16:47.6131201", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Security.Principal.Windows.dll", + "FileVersion": "8.0.2726.22922", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "AssemblyName": "System.Security.Principal.Windows", + "NuGetPackageVersion": "8.0.27", + "Private": "false", + "PublicKeyToken": "b03f5f7f11d50a3a", + "Version": "", + "FrameworkReferenceVersion": "8.0.27", + "ExternallyResolved": "true", + "NuGetPackageId": "Microsoft.NETCore.App.Ref", + "ResolvedFrom": "{RawFileName}", + "FrameworkReferenceName": "Microsoft.NETCore.App", + "ImageRuntime": "v4.0.30319", + "AssemblyVersion": "8.0.0.0", + "CopyLocal": "false", + "OriginalItemSpec": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref/net8.0/System.Security.Principal.Windows.dll", + "FusionName": "System.Security.Principal.Windows, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", + "FullPath": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Security.Principal.Windows.dll", + "RootDir": "C:\\", + "Filename": "System.Security.Principal.Windows", + "Extension": ".dll", + "RelativeDir": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "Directory": "Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "RecursiveDir": "", + "ModifiedTime": "2026-04-30 07:38:58.0000000", + "CreatedTime": "2026-05-29 17:24:46.1994320", + "AccessedTime": "2026-06-09 15:16:47.6160737", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Security.SecureString.dll", + "FileVersion": "8.0.2726.22922", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "AssemblyName": "System.Security.SecureString", + "NuGetPackageVersion": "8.0.27", + "Private": "false", + "PublicKeyToken": "b03f5f7f11d50a3a", + "Version": "", + "FrameworkReferenceVersion": "8.0.27", + "ExternallyResolved": "true", + "NuGetPackageId": "Microsoft.NETCore.App.Ref", + "ResolvedFrom": "{RawFileName}", + "FrameworkReferenceName": "Microsoft.NETCore.App", + "ImageRuntime": "v4.0.30319", + "AssemblyVersion": "8.0.0.0", + "CopyLocal": "false", + "OriginalItemSpec": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref/net8.0/System.Security.SecureString.dll", + "FusionName": "System.Security.SecureString, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", + "FullPath": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Security.SecureString.dll", + "RootDir": "C:\\", + "Filename": "System.Security.SecureString", + "Extension": ".dll", + "RelativeDir": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "Directory": "Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "RecursiveDir": "", + "ModifiedTime": "2026-04-30 07:43:06.0000000", + "CreatedTime": "2026-05-29 17:24:46.2089640", + "AccessedTime": "2026-06-09 15:16:47.6192869", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.ServiceModel.Web.dll", + "FileVersion": "8.0.2726.22922", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "AssemblyName": "System.ServiceModel.Web", + "NuGetPackageVersion": "8.0.27", + "Private": "false", + "PublicKeyToken": "31bf3856ad364e35", + "Version": "", + "FrameworkReferenceVersion": "8.0.27", + "ExternallyResolved": "true", + "NuGetPackageId": "Microsoft.NETCore.App.Ref", + "ResolvedFrom": "{RawFileName}", + "FrameworkReferenceName": "Microsoft.NETCore.App", + "ImageRuntime": "v4.0.30319", + "AssemblyVersion": "4.0.0.0", + "CopyLocal": "false", + "OriginalItemSpec": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref/net8.0/System.ServiceModel.Web.dll", + "FusionName": "System.ServiceModel.Web, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "FullPath": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.ServiceModel.Web.dll", + "RootDir": "C:\\", + "Filename": "System.ServiceModel.Web", + "Extension": ".dll", + "RelativeDir": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "Directory": "Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "RecursiveDir": "", + "ModifiedTime": "2026-04-30 07:43:10.0000000", + "CreatedTime": "2026-05-29 17:24:46.2197555", + "AccessedTime": "2026-06-09 15:16:47.6225646", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.ServiceProcess.dll", + "FileVersion": "8.0.2726.22922", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "AssemblyName": "System.ServiceProcess", + "NuGetPackageVersion": "8.0.27", + "Private": "false", + "PublicKeyToken": "b03f5f7f11d50a3a", + "Version": "", + "FrameworkReferenceVersion": "8.0.27", + "ExternallyResolved": "true", + "NuGetPackageId": "Microsoft.NETCore.App.Ref", + "ResolvedFrom": "{RawFileName}", + "FrameworkReferenceName": "Microsoft.NETCore.App", + "ImageRuntime": "v4.0.30319", + "AssemblyVersion": "4.0.0.0", + "CopyLocal": "false", + "OriginalItemSpec": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref/net8.0/System.ServiceProcess.dll", + "FusionName": "System.ServiceProcess, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", + "FullPath": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.ServiceProcess.dll", + "RootDir": "C:\\", + "Filename": "System.ServiceProcess", + "Extension": ".dll", + "RelativeDir": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "Directory": "Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "RecursiveDir": "", + "ModifiedTime": "2026-04-30 07:42:32.0000000", + "CreatedTime": "2026-05-29 17:24:46.2314710", + "AccessedTime": "2026-06-09 15:16:47.6268647", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Text.Encoding.CodePages.dll", + "FileVersion": "8.0.2726.22922", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "AssemblyName": "System.Text.Encoding.CodePages", + "NuGetPackageVersion": "8.0.27", + "Private": "false", + "PublicKeyToken": "b03f5f7f11d50a3a", + "Version": "", + "FrameworkReferenceVersion": "8.0.27", + "ExternallyResolved": "true", + "NuGetPackageId": "Microsoft.NETCore.App.Ref", + "ResolvedFrom": "{RawFileName}", + "FrameworkReferenceName": "Microsoft.NETCore.App", + "ImageRuntime": "v4.0.30319", + "AssemblyVersion": "8.0.0.0", + "CopyLocal": "false", + "OriginalItemSpec": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref/net8.0/System.Text.Encoding.CodePages.dll", + "FusionName": "System.Text.Encoding.CodePages, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", + "FullPath": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Text.Encoding.CodePages.dll", + "RootDir": "C:\\", + "Filename": "System.Text.Encoding.CodePages", + "Extension": ".dll", + "RelativeDir": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "Directory": "Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "RecursiveDir": "", + "ModifiedTime": "2026-04-30 07:39:04.0000000", + "CreatedTime": "2026-05-29 17:24:46.2419814", + "AccessedTime": "2026-06-09 15:16:47.6301903", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Text.Encoding.dll", + "FileVersion": "8.0.2726.22922", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "AssemblyName": "System.Text.Encoding", + "NuGetPackageVersion": "8.0.27", + "Private": "false", + "PublicKeyToken": "b03f5f7f11d50a3a", + "Version": "", + "FrameworkReferenceVersion": "8.0.27", + "ExternallyResolved": "true", + "NuGetPackageId": "Microsoft.NETCore.App.Ref", + "ResolvedFrom": "{RawFileName}", + "FrameworkReferenceName": "Microsoft.NETCore.App", + "ImageRuntime": "v4.0.30319", + "AssemblyVersion": "8.0.0.0", + "CopyLocal": "false", + "OriginalItemSpec": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref/net8.0/System.Text.Encoding.dll", + "FusionName": "System.Text.Encoding, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", + "FullPath": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Text.Encoding.dll", + "RootDir": "C:\\", + "Filename": "System.Text.Encoding", + "Extension": ".dll", + "RelativeDir": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "Directory": "Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "RecursiveDir": "", + "ModifiedTime": "2026-04-30 07:42:28.0000000", + "CreatedTime": "2026-05-29 17:24:46.2529373", + "AccessedTime": "2026-06-09 15:16:47.6361989", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Text.Encoding.Extensions.dll", + "FileVersion": "8.0.2726.22922", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "AssemblyName": "System.Text.Encoding.Extensions", + "NuGetPackageVersion": "8.0.27", + "Private": "false", + "PublicKeyToken": "b03f5f7f11d50a3a", + "Version": "", + "FrameworkReferenceVersion": "8.0.27", + "ExternallyResolved": "true", + "NuGetPackageId": "Microsoft.NETCore.App.Ref", + "ResolvedFrom": "{RawFileName}", + "FrameworkReferenceName": "Microsoft.NETCore.App", + "ImageRuntime": "v4.0.30319", + "AssemblyVersion": "8.0.0.0", + "CopyLocal": "false", + "OriginalItemSpec": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref/net8.0/System.Text.Encoding.Extensions.dll", + "FusionName": "System.Text.Encoding.Extensions, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", + "FullPath": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Text.Encoding.Extensions.dll", + "RootDir": "C:\\", + "Filename": "System.Text.Encoding.Extensions", + "Extension": ".dll", + "RelativeDir": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "Directory": "Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "RecursiveDir": "", + "ModifiedTime": "2026-04-30 07:39:00.0000000", + "CreatedTime": "2026-05-29 17:24:46.2604700", + "AccessedTime": "2026-06-09 15:16:47.6410372", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Text.Encodings.Web.dll", + "FileVersion": "8.0.2726.22922", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "AssemblyName": "System.Text.Encodings.Web", + "NuGetPackageVersion": "8.0.27", + "Private": "false", + "PublicKeyToken": "cc7b13ffcd2ddd51", + "Version": "", + "FrameworkReferenceVersion": "8.0.27", + "ExternallyResolved": "true", + "NuGetPackageId": "Microsoft.NETCore.App.Ref", + "ResolvedFrom": "{RawFileName}", + "FrameworkReferenceName": "Microsoft.NETCore.App", + "ImageRuntime": "v4.0.30319", + "AssemblyVersion": "8.0.0.0", + "CopyLocal": "false", + "OriginalItemSpec": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref/net8.0/System.Text.Encodings.Web.dll", + "FusionName": "System.Text.Encodings.Web, Version=8.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51", + "FullPath": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Text.Encodings.Web.dll", + "RootDir": "C:\\", + "Filename": "System.Text.Encodings.Web", + "Extension": ".dll", + "RelativeDir": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "Directory": "Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "RecursiveDir": "", + "ModifiedTime": "2026-04-30 07:39:04.0000000", + "CreatedTime": "2026-05-29 17:24:46.2684492", + "AccessedTime": "2026-06-09 15:16:47.6475569", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Text.Json.dll", + "FileVersion": "8.0.2726.22922", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "AssemblyName": "System.Text.Json", + "NuGetPackageVersion": "8.0.27", + "Private": "false", + "PublicKeyToken": "cc7b13ffcd2ddd51", + "Version": "", + "FrameworkReferenceVersion": "8.0.27", + "ExternallyResolved": "true", + "NuGetPackageId": "Microsoft.NETCore.App.Ref", + "ResolvedFrom": "{RawFileName}", + "FrameworkReferenceName": "Microsoft.NETCore.App", + "ImageRuntime": "v4.0.30319", + "AssemblyVersion": "8.0.0.0", + "CopyLocal": "false", + "OriginalItemSpec": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref/net8.0/System.Text.Json.dll", + "FusionName": "System.Text.Json, Version=8.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51", + "FullPath": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Text.Json.dll", + "RootDir": "C:\\", + "Filename": "System.Text.Json", + "Extension": ".dll", + "RelativeDir": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "Directory": "Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "RecursiveDir": "", + "ModifiedTime": "2026-04-30 07:39:04.0000000", + "CreatedTime": "2026-05-29 17:24:46.2773651", + "AccessedTime": "2026-06-09 15:16:47.6539079", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Text.RegularExpressions.dll", + "FileVersion": "8.0.2726.22922", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "AssemblyName": "System.Text.RegularExpressions", + "NuGetPackageVersion": "8.0.27", + "Private": "false", + "PublicKeyToken": "b03f5f7f11d50a3a", + "Version": "", + "FrameworkReferenceVersion": "8.0.27", + "ExternallyResolved": "true", + "NuGetPackageId": "Microsoft.NETCore.App.Ref", + "ResolvedFrom": "{RawFileName}", + "FrameworkReferenceName": "Microsoft.NETCore.App", + "ImageRuntime": "v4.0.30319", + "AssemblyVersion": "8.0.0.0", + "CopyLocal": "false", + "OriginalItemSpec": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref/net8.0/System.Text.RegularExpressions.dll", + "FusionName": "System.Text.RegularExpressions, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", + "FullPath": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Text.RegularExpressions.dll", + "RootDir": "C:\\", + "Filename": "System.Text.RegularExpressions", + "Extension": ".dll", + "RelativeDir": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "Directory": "Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "RecursiveDir": "", + "ModifiedTime": "2026-04-30 07:39:02.0000000", + "CreatedTime": "2026-05-29 17:24:46.2851599", + "AccessedTime": "2026-06-09 15:16:47.6577178", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "C:\\Users\\admin\\.nuget\\packages\\microsoft.windowsdesktop.app.ref\\8.0.27\\ref\\net8.0\\System.Threading.AccessControl.dll", + "FileVersion": "8.0.2726.22922", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "AssemblyName": "System.Threading.AccessControl", + "NuGetPackageVersion": "8.0.27", + "Private": "false", + "PublicKeyToken": "b03f5f7f11d50a3a", + "Version": "", + "FrameworkReferenceVersion": "8.0.27", + "ExternallyResolved": "true", + "NuGetPackageId": "Microsoft.WindowsDesktop.App.Ref", + "ResolvedFrom": "{RawFileName}", + "FrameworkReferenceName": "Microsoft.WindowsDesktop.App.WindowsForms", + "ImageRuntime": "v4.0.30319", + "AssemblyVersion": "8.0.0.0", + "CopyLocal": "false", + "OriginalItemSpec": "C:\\Users\\admin\\.nuget\\packages\\microsoft.windowsdesktop.app.ref\\8.0.27\\ref/net8.0/System.Threading.AccessControl.dll", + "FusionName": "System.Threading.AccessControl, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", + "FullPath": "C:\\Users\\admin\\.nuget\\packages\\microsoft.windowsdesktop.app.ref\\8.0.27\\ref\\net8.0\\System.Threading.AccessControl.dll", + "RootDir": "C:\\", + "Filename": "System.Threading.AccessControl", + "Extension": ".dll", + "RelativeDir": "C:\\Users\\admin\\.nuget\\packages\\microsoft.windowsdesktop.app.ref\\8.0.27\\ref\\net8.0\\", + "Directory": "Users\\admin\\.nuget\\packages\\microsoft.windowsdesktop.app.ref\\8.0.27\\ref\\net8.0\\", + "RecursiveDir": "", + "ModifiedTime": "2026-04-30 07:27:30.0000000", + "CreatedTime": "2026-05-29 17:24:44.5348189", + "AccessedTime": "2026-06-09 15:08:47.6899308", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Threading.Channels.dll", + "FileVersion": "8.0.2726.22922", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "AssemblyName": "System.Threading.Channels", + "NuGetPackageVersion": "8.0.27", + "Private": "false", + "PublicKeyToken": "cc7b13ffcd2ddd51", + "Version": "", + "FrameworkReferenceVersion": "8.0.27", + "ExternallyResolved": "true", + "NuGetPackageId": "Microsoft.NETCore.App.Ref", + "ResolvedFrom": "{RawFileName}", + "FrameworkReferenceName": "Microsoft.NETCore.App", + "ImageRuntime": "v4.0.30319", + "AssemblyVersion": "8.0.0.0", + "CopyLocal": "false", + "OriginalItemSpec": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref/net8.0/System.Threading.Channels.dll", + "FusionName": "System.Threading.Channels, Version=8.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51", + "FullPath": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Threading.Channels.dll", + "RootDir": "C:\\", + "Filename": "System.Threading.Channels", + "Extension": ".dll", + "RelativeDir": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "Directory": "Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "RecursiveDir": "", + "ModifiedTime": "2026-04-30 07:39:04.0000000", + "CreatedTime": "2026-05-29 17:24:46.2972450", + "AccessedTime": "2026-06-09 15:16:47.6610579", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Threading.dll", + "FileVersion": "8.0.2726.22922", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "AssemblyName": "System.Threading", + "NuGetPackageVersion": "8.0.27", + "Private": "false", + "PublicKeyToken": "b03f5f7f11d50a3a", + "Version": "", + "FrameworkReferenceVersion": "8.0.27", + "ExternallyResolved": "true", + "NuGetPackageId": "Microsoft.NETCore.App.Ref", + "ResolvedFrom": "{RawFileName}", + "FrameworkReferenceName": "Microsoft.NETCore.App", + "ImageRuntime": "v4.0.30319", + "AssemblyVersion": "8.0.0.0", + "CopyLocal": "false", + "OriginalItemSpec": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref/net8.0/System.Threading.dll", + "FusionName": "System.Threading, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", + "FullPath": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Threading.dll", + "RootDir": "C:\\", + "Filename": "System.Threading", + "Extension": ".dll", + "RelativeDir": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "Directory": "Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "RecursiveDir": "", + "ModifiedTime": "2026-04-30 07:38:56.0000000", + "CreatedTime": "2026-05-29 17:24:46.3059768", + "AccessedTime": "2026-06-09 15:16:47.6653245", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Threading.Overlapped.dll", + "FileVersion": "8.0.2726.22922", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "AssemblyName": "System.Threading.Overlapped", + "NuGetPackageVersion": "8.0.27", + "Private": "false", + "PublicKeyToken": "b03f5f7f11d50a3a", + "Version": "", + "FrameworkReferenceVersion": "8.0.27", + "ExternallyResolved": "true", + "NuGetPackageId": "Microsoft.NETCore.App.Ref", + "ResolvedFrom": "{RawFileName}", + "FrameworkReferenceName": "Microsoft.NETCore.App", + "ImageRuntime": "v4.0.30319", + "AssemblyVersion": "8.0.0.0", + "CopyLocal": "false", + "OriginalItemSpec": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref/net8.0/System.Threading.Overlapped.dll", + "FusionName": "System.Threading.Overlapped, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", + "FullPath": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Threading.Overlapped.dll", + "RootDir": "C:\\", + "Filename": "System.Threading.Overlapped", + "Extension": ".dll", + "RelativeDir": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "Directory": "Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "RecursiveDir": "", + "ModifiedTime": "2026-04-30 07:38:56.0000000", + "CreatedTime": "2026-05-29 17:24:46.3161887", + "AccessedTime": "2026-06-09 15:16:47.6698486", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Threading.Tasks.Dataflow.dll", + "FileVersion": "8.0.2726.22922", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "AssemblyName": "System.Threading.Tasks.Dataflow", + "NuGetPackageVersion": "8.0.27", + "Private": "false", + "PublicKeyToken": "b03f5f7f11d50a3a", + "Version": "", + "FrameworkReferenceVersion": "8.0.27", + "ExternallyResolved": "true", + "NuGetPackageId": "Microsoft.NETCore.App.Ref", + "ResolvedFrom": "{RawFileName}", + "FrameworkReferenceName": "Microsoft.NETCore.App", + "ImageRuntime": "v4.0.30319", + "AssemblyVersion": "8.0.0.0", + "CopyLocal": "false", + "OriginalItemSpec": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref/net8.0/System.Threading.Tasks.Dataflow.dll", + "FusionName": "System.Threading.Tasks.Dataflow, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", + "FullPath": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Threading.Tasks.Dataflow.dll", + "RootDir": "C:\\", + "Filename": "System.Threading.Tasks.Dataflow", + "Extension": ".dll", + "RelativeDir": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "Directory": "Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "RecursiveDir": "", + "ModifiedTime": "2026-04-30 07:39:04.0000000", + "CreatedTime": "2026-05-29 17:24:46.3276480", + "AccessedTime": "2026-06-09 15:16:47.6738553", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Threading.Tasks.dll", + "FileVersion": "8.0.2726.22922", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "AssemblyName": "System.Threading.Tasks", + "NuGetPackageVersion": "8.0.27", + "Private": "false", + "PublicKeyToken": "b03f5f7f11d50a3a", + "Version": "", + "FrameworkReferenceVersion": "8.0.27", + "ExternallyResolved": "true", + "NuGetPackageId": "Microsoft.NETCore.App.Ref", + "ResolvedFrom": "{RawFileName}", + "FrameworkReferenceName": "Microsoft.NETCore.App", + "ImageRuntime": "v4.0.30319", + "AssemblyVersion": "8.0.0.0", + "CopyLocal": "false", + "OriginalItemSpec": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref/net8.0/System.Threading.Tasks.dll", + "FusionName": "System.Threading.Tasks, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", + "FullPath": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Threading.Tasks.dll", + "RootDir": "C:\\", + "Filename": "System.Threading.Tasks", + "Extension": ".dll", + "RelativeDir": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "Directory": "Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "RecursiveDir": "", + "ModifiedTime": "2026-04-30 07:42:28.0000000", + "CreatedTime": "2026-05-29 17:24:46.3391714", + "AccessedTime": "2026-06-09 15:16:47.6790214", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Threading.Tasks.Extensions.dll", + "FileVersion": "8.0.2726.22922", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "AssemblyName": "System.Threading.Tasks.Extensions", + "NuGetPackageVersion": "8.0.27", + "Private": "false", + "PublicKeyToken": "cc7b13ffcd2ddd51", + "Version": "", + "FrameworkReferenceVersion": "8.0.27", + "ExternallyResolved": "true", + "NuGetPackageId": "Microsoft.NETCore.App.Ref", + "ResolvedFrom": "{RawFileName}", + "FrameworkReferenceName": "Microsoft.NETCore.App", + "ImageRuntime": "v4.0.30319", + "AssemblyVersion": "8.0.0.0", + "CopyLocal": "false", + "OriginalItemSpec": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref/net8.0/System.Threading.Tasks.Extensions.dll", + "FusionName": "System.Threading.Tasks.Extensions, Version=8.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51", + "FullPath": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Threading.Tasks.Extensions.dll", + "RootDir": "C:\\", + "Filename": "System.Threading.Tasks.Extensions", + "Extension": ".dll", + "RelativeDir": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "Directory": "Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "RecursiveDir": "", + "ModifiedTime": "2026-04-30 07:42:34.0000000", + "CreatedTime": "2026-05-29 17:24:46.3503412", + "AccessedTime": "2026-06-09 15:16:47.6833902", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Threading.Tasks.Parallel.dll", + "FileVersion": "8.0.2726.22922", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "AssemblyName": "System.Threading.Tasks.Parallel", + "NuGetPackageVersion": "8.0.27", + "Private": "false", + "PublicKeyToken": "b03f5f7f11d50a3a", + "Version": "", + "FrameworkReferenceVersion": "8.0.27", + "ExternallyResolved": "true", + "NuGetPackageId": "Microsoft.NETCore.App.Ref", + "ResolvedFrom": "{RawFileName}", + "FrameworkReferenceName": "Microsoft.NETCore.App", + "ImageRuntime": "v4.0.30319", + "AssemblyVersion": "8.0.0.0", + "CopyLocal": "false", + "OriginalItemSpec": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref/net8.0/System.Threading.Tasks.Parallel.dll", + "FusionName": "System.Threading.Tasks.Parallel, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", + "FullPath": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Threading.Tasks.Parallel.dll", + "RootDir": "C:\\", + "Filename": "System.Threading.Tasks.Parallel", + "Extension": ".dll", + "RelativeDir": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "Directory": "Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "RecursiveDir": "", + "ModifiedTime": "2026-04-30 07:38:58.0000000", + "CreatedTime": "2026-05-29 17:24:46.3603779", + "AccessedTime": "2026-06-09 15:16:47.6863898", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Threading.Thread.dll", + "FileVersion": "8.0.2726.22922", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "AssemblyName": "System.Threading.Thread", + "NuGetPackageVersion": "8.0.27", + "Private": "false", + "PublicKeyToken": "b03f5f7f11d50a3a", + "Version": "", + "FrameworkReferenceVersion": "8.0.27", + "ExternallyResolved": "true", + "NuGetPackageId": "Microsoft.NETCore.App.Ref", + "ResolvedFrom": "{RawFileName}", + "FrameworkReferenceName": "Microsoft.NETCore.App", + "ImageRuntime": "v4.0.30319", + "AssemblyVersion": "8.0.0.0", + "CopyLocal": "false", + "OriginalItemSpec": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref/net8.0/System.Threading.Thread.dll", + "FusionName": "System.Threading.Thread, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", + "FullPath": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Threading.Thread.dll", + "RootDir": "C:\\", + "Filename": "System.Threading.Thread", + "Extension": ".dll", + "RelativeDir": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "Directory": "Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "RecursiveDir": "", + "ModifiedTime": "2026-04-30 07:39:00.0000000", + "CreatedTime": "2026-05-29 17:24:46.3729205", + "AccessedTime": "2026-06-09 15:16:47.6893882", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Threading.ThreadPool.dll", + "FileVersion": "8.0.2726.22922", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "AssemblyName": "System.Threading.ThreadPool", + "NuGetPackageVersion": "8.0.27", + "Private": "false", + "PublicKeyToken": "b03f5f7f11d50a3a", + "Version": "", + "FrameworkReferenceVersion": "8.0.27", + "ExternallyResolved": "true", + "NuGetPackageId": "Microsoft.NETCore.App.Ref", + "ResolvedFrom": "{RawFileName}", + "FrameworkReferenceName": "Microsoft.NETCore.App", + "ImageRuntime": "v4.0.30319", + "AssemblyVersion": "8.0.0.0", + "CopyLocal": "false", + "OriginalItemSpec": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref/net8.0/System.Threading.ThreadPool.dll", + "FusionName": "System.Threading.ThreadPool, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", + "FullPath": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Threading.ThreadPool.dll", + "RootDir": "C:\\", + "Filename": "System.Threading.ThreadPool", + "Extension": ".dll", + "RelativeDir": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "Directory": "Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "RecursiveDir": "", + "ModifiedTime": "2026-04-30 07:39:00.0000000", + "CreatedTime": "2026-05-29 17:24:46.3826571", + "AccessedTime": "2026-06-09 15:16:47.6929017", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Threading.Timer.dll", + "FileVersion": "8.0.2726.22922", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "AssemblyName": "System.Threading.Timer", + "NuGetPackageVersion": "8.0.27", + "Private": "false", + "PublicKeyToken": "b03f5f7f11d50a3a", + "Version": "", + "FrameworkReferenceVersion": "8.0.27", + "ExternallyResolved": "true", + "NuGetPackageId": "Microsoft.NETCore.App.Ref", + "ResolvedFrom": "{RawFileName}", + "FrameworkReferenceName": "Microsoft.NETCore.App", + "ImageRuntime": "v4.0.30319", + "AssemblyVersion": "8.0.0.0", + "CopyLocal": "false", + "OriginalItemSpec": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref/net8.0/System.Threading.Timer.dll", + "FusionName": "System.Threading.Timer, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", + "FullPath": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Threading.Timer.dll", + "RootDir": "C:\\", + "Filename": "System.Threading.Timer", + "Extension": ".dll", + "RelativeDir": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "Directory": "Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "RecursiveDir": "", + "ModifiedTime": "2026-04-30 07:42:32.0000000", + "CreatedTime": "2026-05-29 17:24:46.3936445", + "AccessedTime": "2026-06-09 15:16:47.6962562", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Transactions.dll", + "FileVersion": "8.0.2726.22922", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "AssemblyName": "System.Transactions", + "NuGetPackageVersion": "8.0.27", + "Private": "false", + "PublicKeyToken": "b77a5c561934e089", + "Version": "", + "FrameworkReferenceVersion": "8.0.27", + "ExternallyResolved": "true", + "NuGetPackageId": "Microsoft.NETCore.App.Ref", + "ResolvedFrom": "{RawFileName}", + "FrameworkReferenceName": "Microsoft.NETCore.App", + "ImageRuntime": "v4.0.30319", + "AssemblyVersion": "4.0.0.0", + "CopyLocal": "false", + "OriginalItemSpec": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref/net8.0/System.Transactions.dll", + "FusionName": "System.Transactions, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", + "FullPath": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Transactions.dll", + "RootDir": "C:\\", + "Filename": "System.Transactions", + "Extension": ".dll", + "RelativeDir": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "Directory": "Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "RecursiveDir": "", + "ModifiedTime": "2026-04-30 07:42:46.0000000", + "CreatedTime": "2026-05-29 17:24:46.4037248", + "AccessedTime": "2026-06-09 15:16:47.7010360", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Transactions.Local.dll", + "FileVersion": "8.0.2726.22922", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "AssemblyName": "System.Transactions.Local", + "NuGetPackageVersion": "8.0.27", + "Private": "false", + "PublicKeyToken": "cc7b13ffcd2ddd51", + "Version": "", + "FrameworkReferenceVersion": "8.0.27", + "ExternallyResolved": "true", + "NuGetPackageId": "Microsoft.NETCore.App.Ref", + "ResolvedFrom": "{RawFileName}", + "FrameworkReferenceName": "Microsoft.NETCore.App", + "ImageRuntime": "v4.0.30319", + "AssemblyVersion": "8.0.0.0", + "CopyLocal": "false", + "OriginalItemSpec": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref/net8.0/System.Transactions.Local.dll", + "FusionName": "System.Transactions.Local, Version=8.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51", + "FullPath": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Transactions.Local.dll", + "RootDir": "C:\\", + "Filename": "System.Transactions.Local", + "Extension": ".dll", + "RelativeDir": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "Directory": "Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "RecursiveDir": "", + "ModifiedTime": "2026-04-30 07:38:58.0000000", + "CreatedTime": "2026-05-29 17:24:46.4112634", + "AccessedTime": "2026-06-09 15:16:47.7046926", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.ValueTuple.dll", + "FileVersion": "8.0.2726.22922", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "AssemblyName": "System.ValueTuple", + "NuGetPackageVersion": "8.0.27", + "Private": "false", + "PublicKeyToken": "cc7b13ffcd2ddd51", + "Version": "", + "FrameworkReferenceVersion": "8.0.27", + "ExternallyResolved": "true", + "NuGetPackageId": "Microsoft.NETCore.App.Ref", + "ResolvedFrom": "{RawFileName}", + "FrameworkReferenceName": "Microsoft.NETCore.App", + "ImageRuntime": "v4.0.30319", + "AssemblyVersion": "8.0.0.0", + "CopyLocal": "false", + "OriginalItemSpec": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref/net8.0/System.ValueTuple.dll", + "FusionName": "System.ValueTuple, Version=8.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51", + "FullPath": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.ValueTuple.dll", + "RootDir": "C:\\", + "Filename": "System.ValueTuple", + "Extension": ".dll", + "RelativeDir": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "Directory": "Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "RecursiveDir": "", + "ModifiedTime": "2026-04-30 07:42:32.0000000", + "CreatedTime": "2026-05-29 17:24:46.4213399", + "AccessedTime": "2026-06-09 15:16:47.7106932", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Web.dll", + "FileVersion": "8.0.2726.22922", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "AssemblyName": "System.Web", + "NuGetPackageVersion": "8.0.27", + "Private": "false", + "PublicKeyToken": "b03f5f7f11d50a3a", + "Version": "", + "FrameworkReferenceVersion": "8.0.27", + "ExternallyResolved": "true", + "NuGetPackageId": "Microsoft.NETCore.App.Ref", + "ResolvedFrom": "{RawFileName}", + "FrameworkReferenceName": "Microsoft.NETCore.App", + "ImageRuntime": "v4.0.30319", + "AssemblyVersion": "4.0.0.0", + "CopyLocal": "false", + "OriginalItemSpec": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref/net8.0/System.Web.dll", + "FusionName": "System.Web, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", + "FullPath": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Web.dll", + "RootDir": "C:\\", + "Filename": "System.Web", + "Extension": ".dll", + "RelativeDir": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "Directory": "Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "RecursiveDir": "", + "ModifiedTime": "2026-04-30 07:42:32.0000000", + "CreatedTime": "2026-05-29 17:24:46.4331283", + "AccessedTime": "2026-06-09 15:16:47.7132038", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Web.HttpUtility.dll", + "FileVersion": "8.0.2726.22922", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "AssemblyName": "System.Web.HttpUtility", + "NuGetPackageVersion": "8.0.27", + "Private": "false", + "PublicKeyToken": "cc7b13ffcd2ddd51", + "Version": "", + "FrameworkReferenceVersion": "8.0.27", + "ExternallyResolved": "true", + "NuGetPackageId": "Microsoft.NETCore.App.Ref", + "ResolvedFrom": "{RawFileName}", + "FrameworkReferenceName": "Microsoft.NETCore.App", + "ImageRuntime": "v4.0.30319", + "AssemblyVersion": "8.0.0.0", + "CopyLocal": "false", + "OriginalItemSpec": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref/net8.0/System.Web.HttpUtility.dll", + "FusionName": "System.Web.HttpUtility, Version=8.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51", + "FullPath": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Web.HttpUtility.dll", + "RootDir": "C:\\", + "Filename": "System.Web.HttpUtility", + "Extension": ".dll", + "RelativeDir": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "Directory": "Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "RecursiveDir": "", + "ModifiedTime": "2026-04-30 07:39:00.0000000", + "CreatedTime": "2026-05-29 17:24:46.4411008", + "AccessedTime": "2026-06-09 15:16:47.7165303", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Windows.dll", + "FileVersion": "8.0.2726.22922", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "AssemblyName": "System.Windows", + "NuGetPackageVersion": "8.0.27", + "Private": "false", + "PublicKeyToken": "b03f5f7f11d50a3a", + "Version": "", + "FrameworkReferenceVersion": "8.0.27", + "ExternallyResolved": "true", + "NuGetPackageId": "Microsoft.NETCore.App.Ref", + "ResolvedFrom": "{RawFileName}", + "FrameworkReferenceName": "Microsoft.NETCore.App", + "ImageRuntime": "v4.0.30319", + "AssemblyVersion": "4.0.0.0", + "CopyLocal": "false", + "OriginalItemSpec": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref/net8.0/System.Windows.dll", + "FusionName": "System.Windows, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", + "FullPath": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Windows.dll", + "RootDir": "C:\\", + "Filename": "System.Windows", + "Extension": ".dll", + "RelativeDir": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "Directory": "Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "RecursiveDir": "", + "ModifiedTime": "2026-04-30 07:42:34.0000000", + "CreatedTime": "2026-05-29 17:24:46.4543885", + "AccessedTime": "2026-06-09 15:16:47.7203261", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "C:\\Users\\admin\\.nuget\\packages\\microsoft.windowsdesktop.app.ref\\8.0.27\\ref\\net8.0\\System.Windows.Extensions.dll", + "FileVersion": "8.0.2726.22922", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "AssemblyName": "System.Windows.Extensions", + "NuGetPackageVersion": "8.0.27", + "Private": "false", + "PublicKeyToken": "cc7b13ffcd2ddd51", + "Version": "", + "FrameworkReferenceVersion": "8.0.27", + "ExternallyResolved": "true", + "NuGetPackageId": "Microsoft.WindowsDesktop.App.Ref", + "ResolvedFrom": "{RawFileName}", + "FrameworkReferenceName": "Microsoft.WindowsDesktop.App.WindowsForms", + "ImageRuntime": "v4.0.30319", + "AssemblyVersion": "8.0.0.0", + "CopyLocal": "false", + "OriginalItemSpec": "C:\\Users\\admin\\.nuget\\packages\\microsoft.windowsdesktop.app.ref\\8.0.27\\ref/net8.0/System.Windows.Extensions.dll", + "FusionName": "System.Windows.Extensions, Version=8.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51", + "FullPath": "C:\\Users\\admin\\.nuget\\packages\\microsoft.windowsdesktop.app.ref\\8.0.27\\ref\\net8.0\\System.Windows.Extensions.dll", + "RootDir": "C:\\", + "Filename": "System.Windows.Extensions", + "Extension": ".dll", + "RelativeDir": "C:\\Users\\admin\\.nuget\\packages\\microsoft.windowsdesktop.app.ref\\8.0.27\\ref\\net8.0\\", + "Directory": "Users\\admin\\.nuget\\packages\\microsoft.windowsdesktop.app.ref\\8.0.27\\ref\\net8.0\\", + "RecursiveDir": "", + "ModifiedTime": "2026-04-30 07:27:18.0000000", + "CreatedTime": "2026-05-29 17:24:44.5600429", + "AccessedTime": "2026-06-09 15:08:47.6032630", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "C:\\Users\\admin\\.nuget\\packages\\microsoft.windowsdesktop.app.ref\\8.0.27\\ref\\net8.0\\System.Windows.Forms.Design.dll", + "FileVersion": "8.0.2726.23001", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "AssemblyName": "System.Windows.Forms.Design", + "NuGetPackageVersion": "8.0.27", + "Private": "false", + "PublicKeyToken": "b77a5c561934e089", + "Version": "", + "FrameworkReferenceVersion": "8.0.27", + "ExternallyResolved": "true", + "NuGetPackageId": "Microsoft.WindowsDesktop.App.Ref", + "ResolvedFrom": "{RawFileName}", + "FrameworkReferenceName": "Microsoft.WindowsDesktop.App.WindowsForms", + "ImageRuntime": "v4.0.30319", + "AssemblyVersion": "8.0.0.0", + "CopyLocal": "false", + "OriginalItemSpec": "C:\\Users\\admin\\.nuget\\packages\\microsoft.windowsdesktop.app.ref\\8.0.27\\ref/net8.0/System.Windows.Forms.Design.dll", + "FusionName": "System.Windows.Forms.Design, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", + "FullPath": "C:\\Users\\admin\\.nuget\\packages\\microsoft.windowsdesktop.app.ref\\8.0.27\\ref\\net8.0\\System.Windows.Forms.Design.dll", + "RootDir": "C:\\", + "Filename": "System.Windows.Forms.Design", + "Extension": ".dll", + "RelativeDir": "C:\\Users\\admin\\.nuget\\packages\\microsoft.windowsdesktop.app.ref\\8.0.27\\ref\\net8.0\\", + "Directory": "Users\\admin\\.nuget\\packages\\microsoft.windowsdesktop.app.ref\\8.0.27\\ref\\net8.0\\", + "RecursiveDir": "", + "ModifiedTime": "2026-04-30 15:26:08.0000000", + "CreatedTime": "2026-05-29 17:24:44.5721979", + "AccessedTime": "2026-06-09 15:08:47.5982726", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "C:\\Users\\admin\\.nuget\\packages\\microsoft.windowsdesktop.app.ref\\8.0.27\\ref\\net8.0\\System.Windows.Forms.Design.Editors.dll", + "FileVersion": "8.0.2726.23001", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "AssemblyName": "System.Windows.Forms.Design.Editors", + "NuGetPackageVersion": "8.0.27", + "Private": "false", + "PublicKeyToken": "b03f5f7f11d50a3a", + "Version": "", + "FrameworkReferenceVersion": "8.0.27", + "ExternallyResolved": "true", + "NuGetPackageId": "Microsoft.WindowsDesktop.App.Ref", + "ResolvedFrom": "{RawFileName}", + "FrameworkReferenceName": "Microsoft.WindowsDesktop.App.WindowsForms", + "ImageRuntime": "v4.0.30319", + "AssemblyVersion": "8.0.0.0", + "CopyLocal": "false", + "OriginalItemSpec": "C:\\Users\\admin\\.nuget\\packages\\microsoft.windowsdesktop.app.ref\\8.0.27\\ref/net8.0/System.Windows.Forms.Design.Editors.dll", + "FusionName": "System.Windows.Forms.Design.Editors, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", + "FullPath": "C:\\Users\\admin\\.nuget\\packages\\microsoft.windowsdesktop.app.ref\\8.0.27\\ref\\net8.0\\System.Windows.Forms.Design.Editors.dll", + "RootDir": "C:\\", + "Filename": "System.Windows.Forms.Design.Editors", + "Extension": ".dll", + "RelativeDir": "C:\\Users\\admin\\.nuget\\packages\\microsoft.windowsdesktop.app.ref\\8.0.27\\ref\\net8.0\\", + "Directory": "Users\\admin\\.nuget\\packages\\microsoft.windowsdesktop.app.ref\\8.0.27\\ref\\net8.0\\", + "RecursiveDir": "", + "ModifiedTime": "2026-04-30 15:26:18.0000000", + "CreatedTime": "2026-05-29 17:24:44.5851989", + "AccessedTime": "2026-06-09 15:08:47.5952729", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "C:\\Users\\admin\\.nuget\\packages\\microsoft.windowsdesktop.app.ref\\8.0.27\\ref\\net8.0\\System.Windows.Forms.dll", + "FileVersion": "8.0.2726.23001", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "AssemblyName": "System.Windows.Forms", + "NuGetPackageVersion": "8.0.27", + "Private": "false", + "PublicKeyToken": "b77a5c561934e089", + "Version": "", + "FrameworkReferenceVersion": "8.0.27", + "ExternallyResolved": "true", + "NuGetPackageId": "Microsoft.WindowsDesktop.App.Ref", + "ResolvedFrom": "{RawFileName}", + "FrameworkReferenceName": "Microsoft.WindowsDesktop.App.WindowsForms", + "ImageRuntime": "v4.0.30319", + "AssemblyVersion": "8.0.0.0", + "CopyLocal": "false", + "OriginalItemSpec": "C:\\Users\\admin\\.nuget\\packages\\microsoft.windowsdesktop.app.ref\\8.0.27\\ref/net8.0/System.Windows.Forms.dll", + "FusionName": "System.Windows.Forms, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", + "FullPath": "C:\\Users\\admin\\.nuget\\packages\\microsoft.windowsdesktop.app.ref\\8.0.27\\ref\\net8.0\\System.Windows.Forms.dll", + "RootDir": "C:\\", + "Filename": "System.Windows.Forms", + "Extension": ".dll", + "RelativeDir": "C:\\Users\\admin\\.nuget\\packages\\microsoft.windowsdesktop.app.ref\\8.0.27\\ref\\net8.0\\", + "Directory": "Users\\admin\\.nuget\\packages\\microsoft.windowsdesktop.app.ref\\8.0.27\\ref\\net8.0\\", + "RecursiveDir": "", + "ModifiedTime": "2026-04-30 15:25:54.0000000", + "CreatedTime": "2026-05-29 17:24:44.5961979", + "AccessedTime": "2026-06-09 15:08:47.5892700", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "C:\\Users\\admin\\.nuget\\packages\\microsoft.windowsdesktop.app.ref\\8.0.27\\ref\\net8.0\\System.Windows.Forms.Primitives.dll", + "FileVersion": "8.0.2726.23001", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "AssemblyName": "System.Windows.Forms.Primitives", + "NuGetPackageVersion": "8.0.27", + "Private": "false", + "PublicKeyToken": "b77a5c561934e089", + "Version": "", + "FrameworkReferenceVersion": "8.0.27", + "ExternallyResolved": "true", + "NuGetPackageId": "Microsoft.WindowsDesktop.App.Ref", + "ResolvedFrom": "{RawFileName}", + "FrameworkReferenceName": "Microsoft.WindowsDesktop.App.WindowsForms", + "ImageRuntime": "v4.0.30319", + "AssemblyVersion": "8.0.0.0", + "CopyLocal": "false", + "OriginalItemSpec": "C:\\Users\\admin\\.nuget\\packages\\microsoft.windowsdesktop.app.ref\\8.0.27\\ref/net8.0/System.Windows.Forms.Primitives.dll", + "FusionName": "System.Windows.Forms.Primitives, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", + "FullPath": "C:\\Users\\admin\\.nuget\\packages\\microsoft.windowsdesktop.app.ref\\8.0.27\\ref\\net8.0\\System.Windows.Forms.Primitives.dll", + "RootDir": "C:\\", + "Filename": "System.Windows.Forms.Primitives", + "Extension": ".dll", + "RelativeDir": "C:\\Users\\admin\\.nuget\\packages\\microsoft.windowsdesktop.app.ref\\8.0.27\\ref\\net8.0\\", + "Directory": "Users\\admin\\.nuget\\packages\\microsoft.windowsdesktop.app.ref\\8.0.27\\ref\\net8.0\\", + "RecursiveDir": "", + "ModifiedTime": "2026-04-30 15:25:14.0000000", + "CreatedTime": "2026-05-29 17:24:44.6165471", + "AccessedTime": "2026-06-09 15:08:47.5842764", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Xml.dll", + "FileVersion": "8.0.2726.22922", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "AssemblyName": "System.Xml", + "NuGetPackageVersion": "8.0.27", + "Private": "false", + "PublicKeyToken": "b77a5c561934e089", + "Version": "", + "FrameworkReferenceVersion": "8.0.27", + "ExternallyResolved": "true", + "NuGetPackageId": "Microsoft.NETCore.App.Ref", + "ResolvedFrom": "{RawFileName}", + "FrameworkReferenceName": "Microsoft.NETCore.App", + "ImageRuntime": "v4.0.30319", + "AssemblyVersion": "4.0.0.0", + "CopyLocal": "false", + "OriginalItemSpec": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref/net8.0/System.Xml.dll", + "FusionName": "System.Xml, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", + "FullPath": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Xml.dll", + "RootDir": "C:\\", + "Filename": "System.Xml", + "Extension": ".dll", + "RelativeDir": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "Directory": "Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "RecursiveDir": "", + "ModifiedTime": "2026-04-30 07:43:10.0000000", + "CreatedTime": "2026-05-29 17:24:46.4659293", + "AccessedTime": "2026-06-09 15:16:47.7234799", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Xml.Linq.dll", + "FileVersion": "8.0.2726.22922", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "AssemblyName": "System.Xml.Linq", + "NuGetPackageVersion": "8.0.27", + "Private": "false", + "PublicKeyToken": "b77a5c561934e089", + "Version": "", + "FrameworkReferenceVersion": "8.0.27", + "ExternallyResolved": "true", + "NuGetPackageId": "Microsoft.NETCore.App.Ref", + "ResolvedFrom": "{RawFileName}", + "FrameworkReferenceName": "Microsoft.NETCore.App", + "ImageRuntime": "v4.0.30319", + "AssemblyVersion": "4.0.0.0", + "CopyLocal": "false", + "OriginalItemSpec": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref/net8.0/System.Xml.Linq.dll", + "FusionName": "System.Xml.Linq, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", + "FullPath": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Xml.Linq.dll", + "RootDir": "C:\\", + "Filename": "System.Xml.Linq", + "Extension": ".dll", + "RelativeDir": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "Directory": "Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "RecursiveDir": "", + "ModifiedTime": "2026-04-30 07:43:00.0000000", + "CreatedTime": "2026-05-29 17:24:46.4781585", + "AccessedTime": "2026-06-09 15:16:47.7275751", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Xml.ReaderWriter.dll", + "FileVersion": "8.0.2726.22922", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "AssemblyName": "System.Xml.ReaderWriter", + "NuGetPackageVersion": "8.0.27", + "Private": "false", + "PublicKeyToken": "b03f5f7f11d50a3a", + "Version": "", + "FrameworkReferenceVersion": "8.0.27", + "ExternallyResolved": "true", + "NuGetPackageId": "Microsoft.NETCore.App.Ref", + "ResolvedFrom": "{RawFileName}", + "FrameworkReferenceName": "Microsoft.NETCore.App", + "ImageRuntime": "v4.0.30319", + "AssemblyVersion": "8.0.0.0", + "CopyLocal": "false", + "OriginalItemSpec": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref/net8.0/System.Xml.ReaderWriter.dll", + "FusionName": "System.Xml.ReaderWriter, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", + "FullPath": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Xml.ReaderWriter.dll", + "RootDir": "C:\\", + "Filename": "System.Xml.ReaderWriter", + "Extension": ".dll", + "RelativeDir": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "Directory": "Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "RecursiveDir": "", + "ModifiedTime": "2026-04-30 07:39:00.0000000", + "CreatedTime": "2026-05-29 17:24:46.4909223", + "AccessedTime": "2026-06-09 15:16:47.7305727", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Xml.Serialization.dll", + "FileVersion": "8.0.2726.22922", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "AssemblyName": "System.Xml.Serialization", + "NuGetPackageVersion": "8.0.27", + "Private": "false", + "PublicKeyToken": "b77a5c561934e089", + "Version": "", + "FrameworkReferenceVersion": "8.0.27", + "ExternallyResolved": "true", + "NuGetPackageId": "Microsoft.NETCore.App.Ref", + "ResolvedFrom": "{RawFileName}", + "FrameworkReferenceName": "Microsoft.NETCore.App", + "ImageRuntime": "v4.0.30319", + "AssemblyVersion": "4.0.0.0", + "CopyLocal": "false", + "OriginalItemSpec": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref/net8.0/System.Xml.Serialization.dll", + "FusionName": "System.Xml.Serialization, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", + "FullPath": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Xml.Serialization.dll", + "RootDir": "C:\\", + "Filename": "System.Xml.Serialization", + "Extension": ".dll", + "RelativeDir": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "Directory": "Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "RecursiveDir": "", + "ModifiedTime": "2026-04-30 07:43:06.0000000", + "CreatedTime": "2026-05-29 17:24:46.4999707", + "AccessedTime": "2026-06-09 15:16:47.7360761", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Xml.XDocument.dll", + "FileVersion": "8.0.2726.22922", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "AssemblyName": "System.Xml.XDocument", + "NuGetPackageVersion": "8.0.27", + "Private": "false", + "PublicKeyToken": "b03f5f7f11d50a3a", + "Version": "", + "FrameworkReferenceVersion": "8.0.27", + "ExternallyResolved": "true", + "NuGetPackageId": "Microsoft.NETCore.App.Ref", + "ResolvedFrom": "{RawFileName}", + "FrameworkReferenceName": "Microsoft.NETCore.App", + "ImageRuntime": "v4.0.30319", + "AssemblyVersion": "8.0.0.0", + "CopyLocal": "false", + "OriginalItemSpec": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref/net8.0/System.Xml.XDocument.dll", + "FusionName": "System.Xml.XDocument, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", + "FullPath": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Xml.XDocument.dll", + "RootDir": "C:\\", + "Filename": "System.Xml.XDocument", + "Extension": ".dll", + "RelativeDir": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "Directory": "Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "RecursiveDir": "", + "ModifiedTime": "2026-04-30 07:39:04.0000000", + "CreatedTime": "2026-05-29 17:24:46.5111530", + "AccessedTime": "2026-06-09 15:16:47.7417700", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Xml.XmlDocument.dll", + "FileVersion": "8.0.2726.22922", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "AssemblyName": "System.Xml.XmlDocument", + "NuGetPackageVersion": "8.0.27", + "Private": "false", + "PublicKeyToken": "b03f5f7f11d50a3a", + "Version": "", + "FrameworkReferenceVersion": "8.0.27", + "ExternallyResolved": "true", + "NuGetPackageId": "Microsoft.NETCore.App.Ref", + "ResolvedFrom": "{RawFileName}", + "FrameworkReferenceName": "Microsoft.NETCore.App", + "ImageRuntime": "v4.0.30319", + "AssemblyVersion": "8.0.0.0", + "CopyLocal": "false", + "OriginalItemSpec": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref/net8.0/System.Xml.XmlDocument.dll", + "FusionName": "System.Xml.XmlDocument, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", + "FullPath": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Xml.XmlDocument.dll", + "RootDir": "C:\\", + "Filename": "System.Xml.XmlDocument", + "Extension": ".dll", + "RelativeDir": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "Directory": "Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "RecursiveDir": "", + "ModifiedTime": "2026-04-30 07:42:30.0000000", + "CreatedTime": "2026-05-29 17:24:46.5226470", + "AccessedTime": "2026-06-09 15:16:47.7457729", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Xml.XmlSerializer.dll", + "FileVersion": "8.0.2726.22922", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "AssemblyName": "System.Xml.XmlSerializer", + "NuGetPackageVersion": "8.0.27", + "Private": "false", + "PublicKeyToken": "b03f5f7f11d50a3a", + "Version": "", + "FrameworkReferenceVersion": "8.0.27", + "ExternallyResolved": "true", + "NuGetPackageId": "Microsoft.NETCore.App.Ref", + "ResolvedFrom": "{RawFileName}", + "FrameworkReferenceName": "Microsoft.NETCore.App", + "ImageRuntime": "v4.0.30319", + "AssemblyVersion": "8.0.0.0", + "CopyLocal": "false", + "OriginalItemSpec": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref/net8.0/System.Xml.XmlSerializer.dll", + "FusionName": "System.Xml.XmlSerializer, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", + "FullPath": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Xml.XmlSerializer.dll", + "RootDir": "C:\\", + "Filename": "System.Xml.XmlSerializer", + "Extension": ".dll", + "RelativeDir": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "Directory": "Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "RecursiveDir": "", + "ModifiedTime": "2026-04-30 07:39:02.0000000", + "CreatedTime": "2026-05-29 17:24:46.5311261", + "AccessedTime": "2026-06-09 15:16:47.7467696", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Xml.XPath.dll", + "FileVersion": "8.0.2726.22922", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "AssemblyName": "System.Xml.XPath", + "NuGetPackageVersion": "8.0.27", + "Private": "false", + "PublicKeyToken": "b03f5f7f11d50a3a", + "Version": "", + "FrameworkReferenceVersion": "8.0.27", + "ExternallyResolved": "true", + "NuGetPackageId": "Microsoft.NETCore.App.Ref", + "ResolvedFrom": "{RawFileName}", + "FrameworkReferenceName": "Microsoft.NETCore.App", + "ImageRuntime": "v4.0.30319", + "AssemblyVersion": "8.0.0.0", + "CopyLocal": "false", + "OriginalItemSpec": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref/net8.0/System.Xml.XPath.dll", + "FusionName": "System.Xml.XPath, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", + "FullPath": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Xml.XPath.dll", + "RootDir": "C:\\", + "Filename": "System.Xml.XPath", + "Extension": ".dll", + "RelativeDir": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "Directory": "Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "RecursiveDir": "", + "ModifiedTime": "2026-04-30 07:39:04.0000000", + "CreatedTime": "2026-05-29 17:24:46.5415589", + "AccessedTime": "2026-06-09 15:16:47.7509340", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Xml.XPath.XDocument.dll", + "FileVersion": "8.0.2726.22922", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "AssemblyName": "System.Xml.XPath.XDocument", + "NuGetPackageVersion": "8.0.27", + "Private": "false", + "PublicKeyToken": "b03f5f7f11d50a3a", + "Version": "", + "FrameworkReferenceVersion": "8.0.27", + "ExternallyResolved": "true", + "NuGetPackageId": "Microsoft.NETCore.App.Ref", + "ResolvedFrom": "{RawFileName}", + "FrameworkReferenceName": "Microsoft.NETCore.App", + "ImageRuntime": "v4.0.30319", + "AssemblyVersion": "8.0.0.0", + "CopyLocal": "false", + "OriginalItemSpec": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref/net8.0/System.Xml.XPath.XDocument.dll", + "FusionName": "System.Xml.XPath.XDocument, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", + "FullPath": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\System.Xml.XPath.XDocument.dll", + "RootDir": "C:\\", + "Filename": "System.Xml.XPath.XDocument", + "Extension": ".dll", + "RelativeDir": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "Directory": "Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "RecursiveDir": "", + "ModifiedTime": "2026-04-30 07:39:04.0000000", + "CreatedTime": "2026-05-29 17:24:46.5480018", + "AccessedTime": "2026-06-09 15:16:47.7557478", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "E:\\Work\\Core\\Simple-FR\\Simple\\tools\\Topaz.dll", + "HintPath": "E:\\Work\\Core\\Simple-FR\\Simple\\tools\\Topaz.dll", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "Version": "", + "ResolvedFrom": "{HintPathFromItem}", + "ImageRuntime": "v4.0.30319", + "CopyLocal": "true", + "OriginalItemSpec": "Topaz", + "FusionName": "Topaz, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null", + "FullPath": "E:\\Work\\Core\\Simple-FR\\Simple\\tools\\Topaz.dll", + "RootDir": "E:\\", + "Filename": "Topaz", + "Extension": ".dll", + "RelativeDir": "E:\\Work\\Core\\Simple-FR\\Simple\\tools\\", + "Directory": "Work\\Core\\Simple-FR\\Simple\\tools\\", + "RecursiveDir": "", + "ModifiedTime": "2026-05-26 12:59:05.1578294", + "CreatedTime": "2026-05-29 15:47:48.1945652", + "AccessedTime": "2026-06-09 15:31:23.7071357", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + }, + { + "Identity": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\WindowsBase.dll", + "FileVersion": "8.0.2726.22922", + "ReferenceSourceTarget": "ResolveAssemblyReference", + "AssemblyName": "WindowsBase", + "NuGetPackageVersion": "8.0.27", + "Private": "false", + "PublicKeyToken": "31bf3856ad364e35", + "Version": "", + "FrameworkReferenceVersion": "8.0.27", + "ExternallyResolved": "true", + "NuGetPackageId": "Microsoft.NETCore.App.Ref", + "ResolvedFrom": "{RawFileName}", + "FrameworkReferenceName": "Microsoft.NETCore.App", + "ImageRuntime": "v4.0.30319", + "AssemblyVersion": "4.0.0.0", + "CopyLocal": "false", + "OriginalItemSpec": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref/net8.0/WindowsBase.dll", + "FusionName": "WindowsBase, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "FullPath": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\WindowsBase.dll", + "RootDir": "C:\\", + "Filename": "WindowsBase", + "Extension": ".dll", + "RelativeDir": "C:\\Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "Directory": "Users\\admin\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.27\\ref\\net8.0\\", + "RecursiveDir": "", + "ModifiedTime": "2026-04-30 07:42:36.0000000", + "CreatedTime": "2026-05-29 17:24:46.5589208", + "AccessedTime": "2026-06-09 15:16:47.7613800", + "DefiningProjectFullPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\Microsoft.Common.CurrentVersion.targets", + "DefiningProjectDirectory": "C:\\Program Files\\dotnet\\sdk\\10.0.300\\", + "DefiningProjectName": "Microsoft.Common.CurrentVersion", + "DefiningProjectExtension": ".targets" + } + ] + } +} diff --git a/StandardScene.Devices/ButtonBox/AzowieButtonBox.cs b/StandardScene.Devices/ButtonBox/AzowieButtonBox.cs new file mode 100644 index 0000000..17ea589 --- /dev/null +++ b/StandardScene.Devices/ButtonBox/AzowieButtonBox.cs @@ -0,0 +1,547 @@ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using StandardScene.Utils; +using SimpleCore; +using SimpleCore.Library; + +namespace StandardScene.ExtendDevice.ButtonBox +{ + /// + /// Azowie 鍛煎彨鍣ㄦ寜閽洅瀹炵幇 + /// 鍩轰簬 Modbus TCP 鍗忚 + /// + public class AzowieButtonBox : BasicButtonBox + { + /// + /// Modbus TCP 瀹㈡埛绔 + /// + private ModbusRtu _modbusClient; + + /// + /// 鍚屾閿 + /// + private readonly object _syncLock = new object(); + + /// + /// 鏄惁宸插惎鍔 + /// + private bool _isStarted = false; + + /// + /// 瀹氭椂璇诲彇浠诲姟鍙栨秷浠ょ墝 + /// + private CancellationTokenSource _cancellationTokenSource; + + /// + /// 瀹氭椂璇诲彇浠诲姟 + /// + private Task _readTask; + + /// + /// 璇诲彇闂撮殧锛堟绉掞級锛岄粯璁500ms + /// + public int ReadInterval { get; set; } = 500; + + /// + /// 閲嶈繛闂撮殧锛堟绉掞級锛岄粯璁3000ms锛岄伩鍏嶈繃浜庨绻佺殑閲嶈繛 + /// + public int ReconnectInterval { get; set; } = 3000; + + /// + /// 涓婃閲嶈繛灏濊瘯鏃堕棿 + /// + private DateTime _lastReconnectAttempt = DateTime.MinValue; + + /// + /// Modbus 浠庣珯鍦板潃锛岄粯璁1 + /// + public byte SlaveAddress { get; set; } = 1; + + /// + /// 鍛煎彨鍣ㄧ紪鍙凤紙鍙锛 + /// + public int DeviceId { get; private set; } = 0; + + /// + /// 鐢垫睜鐢甸噺锛0-100锛 + /// + public int BatteryLevel { get; private set; } = 0; + + /// + /// 鎸夐挳鐏姸鎬佸瓧鍏 + /// + public Dictionary ButtonLightStates { get; private set; } = new Dictionary(); + + /// + /// 鎸夐挳鐏姸鎬佹灇涓 + /// + public enum ButtonLightState + { + /// + /// 甯哥伃 + /// + Off = 0, + /// + /// 甯镐寒 + /// + On = 1, + /// + /// 蹇棯锛岄棿闅0.5绉 + /// + FastBlink = 2, + /// + /// 鎱㈤棯锛岄棿闅2绉 + /// + SlowBlink = 3 + } + + /// + /// 杩炴帴鎸夐挳鐩 + /// + public override void Connect() + { + lock (_syncLock) + { + if (_isStarted) + { + return; + } + + try + { + UpdateState(ButtonBoxState.Connecting); + + // 鏍规嵁閰嶇疆鐨勬寜閽垵濮嬪寲鎸夐挳鐘舵 + var buttonIndices = ButtonConfigs.Keys.OrderBy(k => k).ToList(); + if (buttonIndices.Count == 0) + { + // 濡傛灉娌℃湁閰嶇疆锛岄粯璁ゅ垵濮嬪寲鎸夐挳1-8 + buttonIndices = new List { 1, 2, 3, 4, 5, 6, 7, 8 }; + } + InitializeButtons(buttonIndices); + + // 鍒濆鍖栨寜閽伅鐘舵 + foreach (var index in buttonIndices) + { + ButtonLightStates[index] = ButtonLightState.Off; + } + + // 灏濊瘯杩炴帴 Modbus TCP 瀹㈡埛绔 + try + { + _modbusClient = new ModbusRtu(); + _modbusClient.StartTcpRtu(Ip, Port); + UpdateState(ButtonBoxState.Online); + } + catch (Exception connectEx) + { + // 鍒濇杩炴帴澶辫触锛屼絾涓嶉樆姝㈠惎鍔ㄨ鍙栧惊鐜紝寰幆涓細鎸佺画閲嶈繛 + UpdateState(ButtonBoxState.Connecting); + Diagnosis.Log($"AzowieButtonBox[{Index}] 鍒濇杩炴帴澶辫触锛屽皢鍦ㄥ悗鍙版寔缁噸杩: {ExceptionFormatter.FormatEx(connectEx)}", "AzowieButtonBox", true); + } + + // 鍚姩瀹氭椂璇诲彇浠诲姟锛堝嵆浣胯繛鎺ュけ璐ヤ篃浼氬惎鍔紝寰幆涓細鎸佺画閲嶈繛锛 + _cancellationTokenSource = new CancellationTokenSource(); + _readTask = Task.Run(() => ReadButtonStatesLoop(_cancellationTokenSource.Token)); + + _isStarted = true; + } + catch (Exception ex) + { + UpdateState(ButtonBoxState.Error, $"鍒濆鍖栧け璐: {ex.Message}"); + _isStarted = false; + Diagnosis.Log($"AzowieButtonBox[{Index}] 鍒濆鍖栧け璐: {ExceptionFormatter.FormatEx(ex)}", "AzowieButtonBox", true); + } + } + } + + /// + /// 鏂紑杩炴帴 + /// + public override void Disconnect() + { + lock (_syncLock) + { + if (!_isStarted || _modbusClient == null) + { + return; + } + + try + { + // 鍋滄璇诲彇浠诲姟 + _cancellationTokenSource?.Cancel(); + _readTask?.Wait(1000); + + // 鍏抽棴 Modbus 杩炴帴 + _modbusClient?.Close(); + _modbusClient = null; + + _isStarted = false; + UpdateState(ButtonBoxState.Offline); + } + catch (Exception ex) + { + UpdateState(ButtonBoxState.Error, $"鏂紑杩炴帴澶辫触: {ex.Message}"); + Diagnosis.Log($"AzowieButtonBox[{Index}] 鏂紑杩炴帴澶辫触: {ExceptionFormatter.FormatEx(ex)}", "AzowieButtonBox", true); + } + } + } + + /// + /// 瀹氭椂璇诲彇鎸夐挳鐘舵佸惊鐜 + /// + private void ReadButtonStatesLoop(CancellationToken cancellationToken) + { + while (!cancellationToken.IsCancellationRequested) + { + try + { + if (!_isStarted) + { + break; + } + + // 妫鏌ヨ繛鎺ョ姸鎬侊紝濡傛灉鏈繛鎺ユ垨杩炴帴鏂紑锛屽皾璇曢噸杩 + bool isConnected = _modbusClient?.modbusRtu?.Connected ?? false; + if (_modbusClient == null || !isConnected) + { + UpdateState(ButtonBoxState.Connecting); + + // 鎺у埗閲嶈繛棰戠巼锛岄伩鍏嶈繃浜庨绻佺殑閲嶈繛瀵艰嚧璧勬簮娴垂 + var timeSinceLastReconnect = (DateTime.Now - _lastReconnectAttempt).TotalMilliseconds; + if (timeSinceLastReconnect >= ReconnectInterval) + { + _lastReconnectAttempt = DateTime.Now; + TryReconnect(); + } + + // 濡傛灉閲嶈繛鍚庝粛鐒舵湭杩炴帴锛岀瓑寰呭悗缁х画涓嬩竴娆″惊鐜 + isConnected = _modbusClient?.modbusRtu?.Connected ?? false; + if (_modbusClient == null || !isConnected) + { + Thread.Sleep(ReadInterval); + continue; + } + } + + // 璇诲彇鎸夐挳鐘舵侊紙瀵勫瓨鍣 01-04 瀵瑰簲鎸夐挳1-4锛21-24 瀵瑰簲鎸夐挳5-8锛 + ReadButtonStates(); + + // 璇诲彇璁惧淇℃伅锛堟瘡5绉掕鍙栦竴娆★級 + //if (DateTime.Now.Second % 5 == 0) + //{ + // ReadDeviceInfo(); + //} + + // 鏇存柊鍦ㄧ嚎鐘舵 + UpdateState(ButtonBoxState.Online); + } + catch (Exception ex) + { + Diagnosis.Log($"AzowieButtonBox[{Index}] 璇诲彇鐘舵佸け璐: {ExceptionFormatter.FormatEx(ex)}", "AzowieButtonBox", true); + UpdateState(ButtonBoxState.Error, $"璇诲彇鐘舵佸け璐: {ex.Message}"); + + // 濡傛灉杩炴帴澶辫触锛屽皾璇曢噸杩 + TryReconnect(); + } + + // 绛夊緟鎸囧畾闂撮殧 + Thread.Sleep(ReadInterval); + } + } + + /// + /// 灏濊瘯閲嶈繛 Modbus TCP 杩炴帴 + /// + private void TryReconnect() + { + ModbusRtu oldClient = null; + try + { + // 瀹夊叏鍏抽棴鏃ц繛鎺 + if (_modbusClient != null) + { + oldClient = _modbusClient; + _modbusClient = null; // 鍏堢疆绌猴紝閬垮厤骞跺彂璁块棶 + + try + { + oldClient.Close(); + } + catch + { + // 蹇界暐鍏抽棴鏃剁殑寮傚父锛岀户缁垱寤烘柊杩炴帴 + } + finally + { + oldClient = null; // 纭繚寮曠敤閲婃斁 + } + } + + // 鍒涘缓鏂拌繛鎺 + _modbusClient = new ModbusRtu(); + _modbusClient.StartTcpRtu(Ip, Port); + + Diagnosis.Log($"AzowieButtonBox[{Index}] 閲嶈繛鎴愬姛", "AzowieButtonBox", false); + } + catch (Exception ex) + { + // 閲嶈繛澶辫触锛岀‘淇濊祫婧愰噴鏀 + if (_modbusClient != null) + { + try + { + _modbusClient.Close(); + } + catch + { + // 蹇界暐鍏抽棴寮傚父 + } + _modbusClient = null; + } + + // 璁板綍鏃ュ織浣嗕笉鎶涘嚭寮傚父锛岀瓑寰呬笅娆″惊鐜户缁皾璇 + Diagnosis.Log($"AzowieButtonBox[{Index}] 閲嶈繛澶辫触: {ExceptionFormatter.FormatEx(ex)}", "AzowieButtonBox", false); + } + } + + /// + /// 璇诲彇鎸夐挳鐘舵 + /// 鏍规嵁閰嶇疆鐨勬寜閽暟閲忓拰缂栧彿璇诲彇瀵瑰簲鐨勫瘎瀛樺櫒 + /// + private void ReadButtonStates() + { + try + { + // 鑾峰彇閰嶇疆鐨勬寜閽储寮曞垪琛 + var configuredButtons = ButtonConfigs.Keys.OrderBy(k => k).ToList(); + if (configuredButtons.Count == 0) + { + // 濡傛灉娌℃湁閰嶇疆锛岄粯璁よ鍙栨寜閽1-8 + configuredButtons = new List { 1, 2, 3, 4, 5, 6, 7, 8 }; + } + + // 灏嗘寜閽垎涓轰袱缁勶細鎸夐挳1-4锛堝瘎瀛樺櫒01-04锛夊拰鎸夐挳5-8锛堝瘎瀛樺櫒21-24锛 + var buttons1_4 = configuredButtons.Where(b => b >= 1 && b <= 4).OrderBy(b => b).ToList(); + var buttons5_8 = configuredButtons.Where(b => b >= 5 && b <= 8).OrderBy(b => b).ToList(); + + // 璇诲彇鎸夐挳1-4鐨勭姸鎬侊紙瀵勫瓨鍣ㄥ湴鍧 01-04锛 + // 鎸夐挳绱㈠紩鐩存帴瀵瑰簲瀵勫瓨鍣ㄥ湴鍧锛氭寜閽1->瀵勫瓨鍣01锛屾寜閽2->瀵勫瓨鍣02锛屾寜閽3->瀵勫瓨鍣03锛屾寜閽4->瀵勫瓨鍣04 + if (buttons1_4.Count > 0) + { + // 璁$畻闇瑕佽鍙栫殑瀵勫瓨鍣ㄨ寖鍥达紙浠庢渶灏忔寜閽储寮曞埌鏈澶ф寜閽储寮曪級 + var minButton = buttons1_4.Min(); + var maxButton = buttons1_4.Max(); + var startReg = (ushort)minButton; // 鎸夐挳绱㈠紩鐩存帴瀵瑰簲瀵勫瓨鍣ㄥ湴鍧 + var count = maxButton - minButton + 1; + + var buttonStates1_4 = _modbusClient.ReadRegisterBuffer_03(SlaveAddress, startReg, (ushort)count); + + // 灏嗚鍙栫粨鏋滄槧灏勫埌瀵瑰簲鐨勬寜閽储寮 + foreach (var buttonIndex in buttons1_4) + { + // 璁$畻璇ユ寜閽湪璇诲彇缁撴灉鏁扮粍涓殑浣嶇疆 + var arrayIndex = buttonIndex - minButton; + if (arrayIndex >= 0 && arrayIndex < buttonStates1_4.Length) + { + var state = buttonStates1_4[arrayIndex]; + UpdateButtonState(buttonIndex, state == 1 ? ButtonState.Pressed : ButtonState.Released); + } + } + } + + // 璇诲彇鎸夐挳5-8鐨勭姸鎬侊紙瀵勫瓨鍣ㄥ湴鍧 21-24锛 + // 鎸夐挳绱㈠紩瀵瑰簲瀵勫瓨鍣ㄥ湴鍧锛氭寜閽5->瀵勫瓨鍣21锛屾寜閽6->瀵勫瓨鍣22锛屾寜閽7->瀵勫瓨鍣23锛屾寜閽8->瀵勫瓨鍣24 + // 瀵勫瓨鍣ㄥ湴鍧 = 20 + 鎸夐挳绱㈠紩 + if (buttons5_8.Count > 0) + { + var minButton = buttons5_8.Min(); + var maxButton = buttons5_8.Max(); + var startReg = (ushort)(20 + minButton); // 鎸夐挳5瀵瑰簲瀵勫瓨鍣21 + var count = maxButton - minButton + 1; + + var buttonStates5_8 = _modbusClient.ReadRegisterBuffer_03(SlaveAddress, startReg, (ushort)count); + + // 灏嗚鍙栫粨鏋滄槧灏勫埌瀵瑰簲鐨勬寜閽储寮 + foreach (var buttonIndex in buttons5_8) + { + // 璁$畻璇ユ寜閽湪璇诲彇缁撴灉鏁扮粍涓殑浣嶇疆 + var arrayIndex = buttonIndex - minButton; + if (arrayIndex >= 0 && arrayIndex < buttonStates5_8.Length) + { + var state = buttonStates5_8[arrayIndex]; + UpdateButtonState(buttonIndex, state == 1 ? ButtonState.Pressed : ButtonState.Released); + } + } + } + } + catch (Exception ex) + { + throw new Exception($"璇诲彇鎸夐挳鐘舵佸け璐: {ex.Message}", ex); + } + } + + /// + /// 鏇存柊鎸夐挳閰嶇疆淇℃伅 + /// 褰撻厤缃彉鍖栨椂锛屽悓姝ユ洿鏂版寜閽姸鎬佸拰鎸夐挳鐏姸鎬佸瓧鍏 + /// + /// 鎸夐挳閰嶇疆鍒楄〃 + public override void UpdateButtonConfigs(List buttonConfigs) + { + lock (_syncLock) + { + // 淇濆瓨鏃х殑鎸夐挳绱㈠紩鍒楄〃 + var oldButtonIndices = new HashSet(ButtonConfigs.Keys); + + // 璋冪敤鍩虹被鏂规硶鏇存柊閰嶇疆 + base.UpdateButtonConfigs(buttonConfigs); + + // 鑾峰彇鏂扮殑鎸夐挳绱㈠紩鍒楄〃 + var newButtonIndices = new HashSet(ButtonConfigs.Keys); + + // 濡傛灉閰嶇疆鍙戠敓鍙樺寲锛屾洿鏂版寜閽姸鎬佸拰鎸夐挳鐏姸鎬佸瓧鍏 + if (!oldButtonIndices.SetEquals(newButtonIndices)) + { + // 绉婚櫎宸插垹闄ょ殑鎸夐挳鐘舵 + var toRemove = oldButtonIndices.Where(k => !newButtonIndices.Contains(k)).ToList(); + foreach (var index in toRemove) + { + ButtonStates.Remove(index); + ButtonLightStates.Remove(index); + } + + // 娣诲姞鏂版寜閽殑鐘舵侊紙鍒濆鍖栦负鏈寜涓嬪拰甯哥伃锛 + var toAdd = newButtonIndices.Where(k => !oldButtonIndices.Contains(k)).ToList(); + foreach (var index in toAdd) + { + if (!ButtonStates.ContainsKey(index)) + { + ButtonStates[index] = ButtonState.Released; + } + if (!ButtonLightStates.ContainsKey(index)) + { + ButtonLightStates[index] = ButtonLightState.Off; + } + } + + // 濡傛灉娌℃湁閰嶇疆锛岄粯璁ゅ垵濮嬪寲鎸夐挳1-8 + if (newButtonIndices.Count == 0) + { + var defaultButtons = new List { 1, 2, 3, 4, 5, 6, 7, 8 }; + InitializeButtons(defaultButtons); + foreach (var index in defaultButtons) + { + if (!ButtonLightStates.ContainsKey(index)) + { + ButtonLightStates[index] = ButtonLightState.Off; + } + } + } + + Diagnosis.Log($"AzowieButtonBox[{Index}] 鎸夐挳閰嶇疆宸叉洿鏂: 鏃ч厤缃畕oldButtonIndices.Count}涓寜閽, 鏂伴厤缃畕newButtonIndices.Count}涓寜閽", "AzowieButtonBox", false); + } + } + } + + /// + /// 娓呴浂鎸囧畾鎸夐挳鐨勫瘎瀛樺櫒鐘舵 + /// + /// 鎸夐挳绱㈠紩锛1-8锛 + public override void ClearButtonRegister(int buttonIndex) + { + lock (_syncLock) + { + if (!_isStarted) + { + return; + } + + if (buttonIndex < 1 || buttonIndex > 8) + { + Diagnosis.Log($"AzowieButtonBox[{Index}] 鎸夐挳绱㈠紩瓒呭嚭鑼冨洿: {buttonIndex}", "AzowieButtonBox", true); + return; + } + + try + { + // 妫鏌ヨ繛鎺ョ姸鎬侊紝濡傛灉鏈繛鎺ュ垯灏濊瘯閲嶈繛 + bool isConnected = _modbusClient?.modbusRtu?.Connected ?? false; + if (!isConnected) + { + // 灏濊瘯閲嶈繛 + TryReconnect(); + isConnected = _modbusClient?.modbusRtu?.Connected ?? false; + + // 濡傛灉閲嶈繛澶辫触锛岃褰曟棩蹇楀苟杩斿洖 + if (!isConnected) + { + Diagnosis.Log($"AzowieButtonBox[{Index}] 娓呴浂鎸夐挳{buttonIndex}瀵勫瓨鍣ㄥけ璐: 杩炴帴鏈缓绔", "AzowieButtonBox", true); + return; + } + } + + // 鏍规嵁鎸夐挳绱㈠紩纭畾瀵勫瓨鍣ㄥ湴鍧 + // 鎸夐挳1-4瀵瑰簲瀵勫瓨鍣01-04锛屾寜閽5-8瀵瑰簲瀵勫瓨鍣21-24 + ushort buttonRegisterAddress; + if (buttonIndex >= 1 && buttonIndex <= 4) + { + buttonRegisterAddress = (ushort)buttonIndex; // 鎸夐挳绱㈠紩鐩存帴瀵瑰簲瀵勫瓨鍣ㄥ湴鍧 + } + else + { + buttonRegisterAddress = (ushort)(20 + buttonIndex); // 鎸夐挳5-8瀵瑰簲瀵勫瓨鍣21-24 + } + + // 娓呴浂鎸夐挳鐘舵佸瘎瀛樺櫒 + _modbusClient.WriteSingleRegister_06(SlaveAddress, buttonRegisterAddress, 0); + + // 娓呴浂瀵瑰簲鐨勬寜閽伅鐘舵佸瘎瀛樺櫒 + // 鎸夐挳鐏1-4瀵瑰簲瀵勫瓨鍣05-08锛堟寜閽储寮 + 4锛夛紝鎸夐挳鐏5-8瀵瑰簲瀵勫瓨鍣25-28锛堟寜閽储寮 + 20锛 + ushort lightRegisterAddress; + if (buttonIndex is >= 1 and <= 4) + { + lightRegisterAddress = (ushort)(4 + buttonIndex); // 鎸夐挳鐏瘎瀛樺櫒 = 鎸夐挳绱㈠紩 + 4 + } + else + { + lightRegisterAddress = (ushort)(20 + buttonIndex); // 鎸夐挳鐏瘎瀛樺櫒 = 鎸夐挳绱㈠紩 + 20 + } + + _modbusClient.WriteSingleRegister_06(SlaveAddress, lightRegisterAddress, 0); + + // 鏇存柊鏈湴鎸夐挳鐏姸鎬 + if (ButtonLightStates.ContainsKey(buttonIndex)) + { + ButtonLightStates[buttonIndex] = ButtonLightState.Off; + } + + Diagnosis.Log($"AzowieButtonBox[{Index}] 娓呴浂鎸夐挳{buttonIndex}鐘舵佸拰鐏厜瀵勫瓨鍣ㄦ垚鍔", "AzowieButtonBox", false); + } + catch (Exception ex) + { + Diagnosis.Log($"AzowieButtonBox[{Index}] 娓呴浂鎸夐挳{buttonIndex}瀵勫瓨鍣ㄥけ璐: {ExceptionFormatter.FormatEx(ex)}", "AzowieButtonBox", true); + + // 濡傛灉鏄洜涓鸿繛鎺ラ棶棰樺鑷寸殑寮傚父锛屽皾璇曢噸杩 + bool isConnected = _modbusClient?.modbusRtu?.Connected ?? false; + if (!isConnected) + { + TryReconnect(); + } + } + } + } + + /// + /// 鏋愭瀯鍑芥暟锛岀‘淇濊祫婧愰噴鏀 + /// + ~AzowieButtonBox() + { + Disconnect(); + } + } +} diff --git a/StandardScene.Devices/ButtonBox/LeegButtonBox.cs b/StandardScene.Devices/ButtonBox/LeegButtonBox.cs new file mode 100644 index 0000000..f62cee9 --- /dev/null +++ b/StandardScene.Devices/ButtonBox/LeegButtonBox.cs @@ -0,0 +1,225 @@ +锘縰sing System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using leegiot; +using SimpleCore; +using SimpleCore.Library; + +namespace StandardScene.ExtendDevice.ButtonBox +{ + public class LeegButtonBox : BasicButtonBox + { + /// + /// LeegKey璁惧瀹炰緥 + /// + private LeegKeyDevice _device; + + /// + /// 鍚屾閿 + /// + private readonly object _syncLock = new object(); + + /// + /// 鏄惁宸插惎鍔 + /// + private bool _isStarted = false; + + /// + /// 杩炴帴鎸夐挳鐩 + /// + public override void Connect() + { + lock (_syncLock) + { + if (_isStarted) + { + return; + } + + try + { + UpdateState(ButtonBoxState.Connecting); + + // 鍒涘缓LeegKey璁惧瀹炰緥 + _device = new LeegKeyDevice(Ip, Port); + + // 璁剧疆浜嬩欢鍥炶皟锛屼娇鐢↖ndex浣滀负userData + _device.setEventCallback(OnLeegKeyEvent, Index); + _device.autoHeartbeatEnable(2,5); + // 鍚姩璁惧 + _device.start(true); + + _isStarted = true; + UpdateState(ButtonBoxState.Online); + } + catch (Exception ex) + { + UpdateState(ButtonBoxState.Error, $"杩炴帴澶辫触: {ex.Message}"); + _isStarted = false; + } + } + } + + /// + /// 鏂紑杩炴帴 + /// + public override void Disconnect() + { + lock (_syncLock) + { + if (!_isStarted || _device == null) + { + return; + } + + try + { + _device.stop(); + _device = null; + _isStarted = false; + UpdateState(ButtonBoxState.Offline); + } + catch (Exception ex) + { + UpdateState(ButtonBoxState.Error, $"鏂紑杩炴帴澶辫触: {ex.Message}"); + } + } + } + + /// + /// LeegKey浜嬩欢鍥炶皟 + /// + private void OnLeegKeyEvent(LeegKeyEvent evt, object msg, object userData) + { + try + { + switch (evt) + { + case LeegKeyEvent.KEY_HIT: + HandleKeyHit((LeegKeyMsgKey)msg); + break; + + case LeegKeyEvent.STATUS_REP: + HandleStatusReport((LeegKeyMsgStatus)msg); + break; + + case LeegKeyEvent.TIME: + // 鏃堕棿鍚屾浜嬩欢锛屽彲浠ョ敤浜庝繚鎸佽繛鎺ョ姸鎬 + UpdateState(ButtonBoxState.Online); + break; + + case LeegKeyEvent.LOG: + // 鏃ュ織浜嬩欢锛屽彲浠ョ敤浜庤皟璇 + HandleLogEvent((LeegKeyMsgLog)msg); + break; + + default: + break; + } + } + catch (Exception ex) + { + UpdateState(ButtonBoxState.Error, $"澶勭悊浜嬩欢澶辫触: {ex.Message}"); + } + } + + /// + /// 澶勭悊鎸夐敭鎸変笅浜嬩欢 + /// + private void HandleKeyHit(LeegKeyMsgKey msg) + { + var keyTag= msg.content.keys[0].First(); + var buttonIndexStr = keyTag.Key.Substring(3); + if(!int.TryParse(buttonIndexStr,out var buttonIndex)) + return; + switch (keyTag.Value) + { + case "up": + UpdateButtonState(buttonIndex,ButtonState.Released); + break; + case "down": + UpdateButtonState(buttonIndex,ButtonState.Pressed); + break; + } + } + + /// + /// 澶勭悊鐘舵佹姤鍛婁簨浠 + /// + private void HandleStatusReport(LeegKeyMsgStatus msg) + { + // 鐘舵佹姤鍛婅〃绀鸿澶囧湪绾 + UpdateState(ButtonBoxState.Online); + } + + /// + /// 澶勭悊鏃ュ織浜嬩欢 + /// + private void HandleLogEvent(LeegKeyMsgLog msg) + { + // 鍙互鏍规嵁鏃ュ織鍐呭鏇存柊鐘舵 + // 杩欓噷鍙互鏍规嵁瀹為檯闇姹傚疄鐜 + } + + + /// + /// 璁剧疆鎸夐挳鐏厜 + /// + /// 鎸夐挳绱㈠紩 + /// RGB棰滆壊鍊 + public void SetButtonLight(int buttonIndex, Rgb rgb) + { + lock (_syncLock) + { + if (!_isStarted || _device == null) + { + return; + } + + try + { + _device.lightSet(rgb, buttonIndex); + } + catch (Exception ex) + { + Diagnosis.Log($"璁剧疆鎸夐挳鐏厜澶辫触: {ExceptionFormatter.FormatEx(ex)}", "LeegButtonBox", true); + } + } + } + + /// + /// 鎺у埗IO杈撳嚭 + /// + /// IO鎺у埗鍐呭 + public void ControlIO(IoctrlContent content) + { + lock (_syncLock) + { + if (!_isStarted || _device == null) + { + return; + } + + try + { + _device.ioctrl(content); + } + catch (Exception ex) + { + Diagnosis.Log($"鎺у埗IO澶辫触: {ExceptionFormatter.FormatEx(ex)}", "LeegButtonBox", true); + } + } + } + + /// + /// 鏋愭瀯鍑芥暟锛岀‘淇濊祫婧愰噴鏀 + /// + ~LeegButtonBox() + { + Disconnect(); + } + } +} diff --git a/StandardScene.Devices/Charge/FLChargeStation.cs b/StandardScene.Devices/Charge/FLChargeStation.cs new file mode 100644 index 0000000..111924d --- /dev/null +++ b/StandardScene.Devices/Charge/FLChargeStation.cs @@ -0,0 +1,405 @@ +using SimpleLite.RCS; +using SimpleLite.RCS.CarTypes; +using SimpleCore; +using SimpleCore.Library; +using SimpleCore.PropType; +using StandardScene.Charge; +using StandardScene.TCP; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net; +using System.Net.Sockets; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading; +using System.Threading.Tasks; + +namespace StandardScene.ChargeStationType +{ + public class FLChargeStation : AbstractChargeStation + { + private AsyncTcpClient Client; + private IPEndPoint _endPoint; + + // 閲嶈繛鐩稿叧 + private Timer _reconnectTimer; + private bool _isManualDisconnect = false; // 鏍囪瘑鏄惁涓烘墜鍔ㄦ柇寮 + private int _reconnectAttempts = 0; // 閲嶈繛灏濊瘯娆℃暟 + private const int MAX_RECONNECT_ATTEMPTS = 5; // 鏈澶ч噸杩炴鏁 + private const int RECONNECT_INTERVAL_MS = 3000; // 閲嶈繛闂撮殧锛堟绉掞級 + private readonly object _connectionLock = new object(); // 杩炴帴閿 + private bool _isConnecting = false; // 鏄惁姝e湪杩炴帴涓 + + // 杩炴帴鐘舵 + public bool IsConnected { get; private set; } = false; + public DateTime? LastConnectedTime { get; private set; } + public DateTime? LastDisconnectedTime { get; private set; } + + /// + /// 鍏抽棴褰撳墠TCP杩炴帴 + /// + public override void CloseCommunication() + { + lock (_connectionLock) + { + _isManualDisconnect = true; // 鏍囪涓烘墜鍔ㄦ柇寮锛屼笉瑙﹀彂鑷姩閲嶈繛 + + // 鍋滄閲嶈繛瀹氭椂鍣 + if (_reconnectTimer != null) + { + _reconnectTimer.Dispose(); + _reconnectTimer = null; + } + _isConnecting = false; + if (Client != null) + { + try + { + // 鍙栨秷璁㈤槄浜嬩欢锛岄伩鍏嶅唴瀛樻硠婕 + Client.PlaintextReceived -= OnPlaintextReceived; + Client.ServerConnected -= OnServerConnected; + Client.ServerDisconnected -= OnServerDisconnected; + + // 鍏抽棴杩炴帴 + Client.Close(); + Client = null; + + IsConnected = false; + LastDisconnectedTime = DateTime.Now; + + Diagnosis.Log($"FLChargeStation[{SiteId}] TCP connection closed manually"); + } + catch (Exception ex) + { + Diagnosis.Log($"ERR:FLChargeStation[{SiteId}] failed to close connection: {ex.Message}"); + } + } + } + } + + /// + /// 鍒涘缓TCP杩炴帴 + /// + public override void CreateCommunication(IPAddress ip, int port) + { + lock (_connectionLock) + { + // 鍏堝叧闂棫杩炴帴 + if (Client != null) + { + CloseCommunication(); + } + + _endPoint = new IPEndPoint(ip, port); + _isManualDisconnect = false; // 閲嶇疆鎵嬪姩鏂紑鏍囪 + _reconnectAttempts = 0; // 閲嶇疆閲嶈繛娆℃暟 + + ConnectInternal(); + } + } + + /// + /// 鍐呴儴杩炴帴鏂规硶 + /// + private void ConnectInternal() + { + if (_isConnecting) + { + Diagnosis.Log($"FLChargeStation[{SiteId}] is already connecting, skip"); + return; + } + + try + { + _isConnecting = true; + + // 鍒涘缓鏂扮殑TCP瀹㈡埛绔 + Client = new AsyncTcpClient(_endPoint.Address, _endPoint.Port); + Client.PlaintextReceived += OnPlaintextReceived; + Client.ServerConnected += OnServerConnected; + Client.ServerDisconnected += OnServerDisconnected; + + // 杩炴帴 + Client.Connect(); + + if (Client.Connected) + { + Diagnosis.Log($"FLChargeStation[{SiteId}] connecting to {_endPoint.Address}:{_endPoint.Port}..."); + } + else + { + _isConnecting = false; + Diagnosis.Log($"FLChargeStation[{SiteId}] connecting to {_endPoint.Address}:{_endPoint.Port}... 鏈缓绔嬭繛鎺"); + } + + } + catch (Exception ex) + { + Diagnosis.Log($"ERR:FLChargeStation[{SiteId}] failed to create connection: {ex.Message}"); + _isConnecting = false; + + // 瑙﹀彂閲嶈繛 + if (!_isManualDisconnect) + { + ScheduleReconnect(); + } + } + } + + /// + /// 鎵嬪姩瑙﹀彂閲嶈繛 + /// + public void Reconnect() + { + lock (_connectionLock) + { + Diagnosis.Log($"FLChargeStation[{SiteId}] manual reconnect triggered"); + + _isManualDisconnect = false; + _reconnectAttempts = 0; + + // 鍏抽棴鐜版湁杩炴帴 + if (Client != null) + { + try + { + Client.PlaintextReceived -= OnPlaintextReceived; + Client.ServerConnected -= OnServerConnected; + Client.ServerDisconnected -= OnServerDisconnected; + Client.Close(); + Client = null; + } + catch { } + } + + // 閲嶆柊杩炴帴 + ConnectInternal(); + } + } + + /// + /// 瀹夋帓鑷姩閲嶈繛 + /// + private void ScheduleReconnect() + { + if (_isManualDisconnect) + { + Diagnosis.Log($"FLChargeStation[{SiteId}] manual disconnect, skip auto reconnect"); + return; + } + + if (_reconnectAttempts >= MAX_RECONNECT_ATTEMPTS) + { + Diagnosis.Log($"ERR:FLChargeStation[{SiteId}] max reconnect attempts ({MAX_RECONNECT_ATTEMPTS}) reached, stop reconnecting"); + return; + } + + _reconnectAttempts++; + + // 鍋滄鐜版湁瀹氭椂鍣 + if (_reconnectTimer != null) + { + _reconnectTimer.Dispose(); + } + + Diagnosis.Log($"FLChargeStation[{SiteId}] scheduling reconnect attempt {_reconnectAttempts}/{MAX_RECONNECT_ATTEMPTS} in {RECONNECT_INTERVAL_MS}ms"); + + // 鍒涘缓鏂扮殑瀹氭椂鍣 + _reconnectTimer = new Timer( + callback: _ => AttemptReconnect(), + state: null, + dueTime: RECONNECT_INTERVAL_MS, + period: Timeout.Infinite + ); + } + + /// + /// 灏濊瘯閲嶈繛 + /// + private void AttemptReconnect() + { + lock (_connectionLock) + { + if (_isManualDisconnect || IsConnected) + { + return; + } + + Diagnosis.Log($"FLChargeStation[{SiteId}] attempting to reconnect (attempt {_reconnectAttempts}/{MAX_RECONNECT_ATTEMPTS})..."); + + try + { + // 娓呯悊鏃у鎴风 + if (Client != null) + { + try + { + Client.PlaintextReceived -= OnPlaintextReceived; + Client.ServerConnected -= OnServerConnected; + Client.ServerDisconnected -= OnServerDisconnected; + Client.Close(); + } + catch { } + Client = null; + } + + // 閲嶆柊杩炴帴 + ConnectInternal(); + } + catch (Exception ex) + { + Diagnosis.Log($"ERR:FLChargeStation[{SiteId}] reconnect failed: {ex.Message}"); + + // 缁х画瀹夋帓涓嬩竴娆¢噸杩 + if (!_isManualDisconnect && _reconnectAttempts < MAX_RECONNECT_ATTEMPTS) + { + ScheduleReconnect(); + } + } + } + } + + private void OnPlaintextReceived(object sender, TcpDatagramReceivedEventArgs e) + { + var messageService = CommunicationMessageService.Instance; + var recBytes = e.Datagram.Take(35).ToArray(); + string IP = ((AsyncTcpClient)sender).RemoteIPEndPoint.Address.ToString(); + Diagnosis.Post($"{string.Join(" ", recBytes.Select(p => $"{p:X2}"))}", $"{IP}", true); + IsSafe = recBytes[28] == 2; + messageService.AddReceiveMessage(_endPoint.Address.ToString(), _endPoint.Port, BitConverter.ToString(recBytes).Replace("-", " "), "FRLDTall"); + } + + /// + /// 鏈嶅姟鍣ㄨ繛鎺ユ垚鍔熶簨浠 + /// + private void OnServerConnected(object sender, TcpServerConnectedEventArgs e) + { + lock (_connectionLock) + { + _isConnecting = false; + IsConnected = true; + LastConnectedTime = DateTime.Now; + _reconnectAttempts = 0; // 閲嶇疆閲嶈繛娆℃暟 + + Diagnosis.Log($"FLChargeStation[{SiteId}] TCP connected successfully to {_endPoint.Address}:{_endPoint.Port}"); + + // 鍋滄閲嶈繛瀹氭椂鍣 + if (_reconnectTimer != null) + { + _reconnectTimer.Dispose(); + _reconnectTimer = null; + } + } + } + + /// + /// 鏈嶅姟鍣ㄦ柇寮杩炴帴浜嬩欢 + /// + private void OnServerDisconnected(object sender, TcpServerDisconnectedEventArgs e) + { + lock (_connectionLock) + { + _isConnecting = false; + IsConnected = false; + LastDisconnectedTime = DateTime.Now; + + Diagnosis.Log($"WARN:FLChargeStation[{SiteId}] TCP disconnected from {_endPoint.Address}:{_endPoint.Port}"); + } + } + + /// + /// 鍙戦佸厖鐢垫寚浠ゅ埌鍏呯數绔 + /// + public override void SendToChargeStation(int isCharge, Car car,Site site) + { + // 妫鏌ヨ繛鎺ョ姸鎬 + if (!IsConnected || Client == null) + { + Diagnosis.Log($"WARN:FLChargeStation[{SiteId}] not connected, cannot send charge command"); + return; + } + + try + { + var messageService = CommunicationMessageService.Instance; + float soc = 0f, voltage = 0, electricCurrent = 0; + int carId = 0; + float setVoltage = 55.0f; + float setElectricCurrent = 50.0f; + //Site site = null; + if (car != null) + { + soc = float.Parse(Commons.GetCarStatus(car, "Soc")); + voltage = float.Parse(Commons.GetCarStatus(car, "Voltage")); + electricCurrent = float.Parse(Commons.GetCarStatus(car, "ElectricCurrent")); + carId = car.id; + //site = SimpleLib.GetSite(car.status.holdingLocks.FirstOrDefault()); + + + + } + if (site != null && site.fields.ContainsKey("setVoltage") && site.fields.ContainsKey("setElectricCurrent")) + { + setVoltage = float.Parse(site.fields["setVoltage"]); + setElectricCurrent = float.Parse(site.fields["setElectricCurrent"]); + } + + var msg = GetSendBytes((byte)(isCharge), + setVoltage, setElectricCurrent, 1000, + Convert.ToSingle(carId), Convert.ToSingle(soc), Convert.ToSingle(electricCurrent), Convert.ToSingle(voltage)); + + messageService.AddSendMessage(_endPoint.Address.ToString(), _endPoint.Port, BitConverter.ToString(msg).Replace("-", " "), "FRLDTall", site?.name); + + // 鍙戦佹暟鎹紝甯﹀紓甯稿鐞 + try + { + Client.Send(msg); + Diagnosis.Post($"{string.Join(" ", msg.Select(d => $"{d:X2}"))}", + $"sendChargeSite: {SiteId}", true); + } + catch (ObjectDisposedException) + { + Diagnosis.Log($"ERR:FLChargeStation[{SiteId}] client disposed during send, triggering reconnect"); + IsConnected = false; + } + catch (SocketException ex) + { + Diagnosis.Log($"ERR:FLChargeStation[{SiteId}] socket error during send: {ex.SocketErrorCode}, triggering reconnect"); + IsConnected = false; + } + } + catch (Exception e) + { + Diagnosis.Post($"ERR:FLChargeStation[{SiteId}] SwitchCharge Fail: {ExceptionFormatter.FormatEx(e)}"); + } + } + + private byte[] GetSendBytes(byte startCharge, float chargeVoltage, float chargeElectricCurrent, + float chargeTimeSpan, float carId, + float carSoc, float carElectricCurrent, float carVoltage) + { + var sendByte = new byte[32]; + try + { + //BB 01 42 48 00 00 42 5C 00 00 00 00 00 02 00 5D 41 B4 00 00 41 28 00 00 00 00 00 00 00 00 00 EE + sendByte = new byte[2] { 0xBB, startCharge } + .Concat(BitConverter.GetBytes(chargeElectricCurrent).AsEnumerable().Reverse()) + .Concat(BitConverter.GetBytes(chargeVoltage).AsEnumerable().Reverse()) + .Concat(BitConverter.GetBytes((ushort)chargeTimeSpan).AsEnumerable().Reverse()) + .Concat(BitConverter.GetBytes((ushort)carId).AsEnumerable().Reverse()) + .Concat(BitConverter.GetBytes((ushort)carSoc).AsEnumerable().Reverse()) + .Concat(BitConverter.GetBytes(carElectricCurrent).AsEnumerable().Reverse()) + .Concat(BitConverter.GetBytes(carVoltage).AsEnumerable().Reverse()) + .Concat(new byte[] { 00, 00, 00, 00, 00, 00, 00, 0xEE }).ToArray(); + + } + catch (Exception ex) + { + Diagnosis.Post($"涓嬪彂鍏呯數鎺у埗{(startCharge == 1 ? "鍚姩" : "鍋滄")}寮傚父+ex:{ExceptionFormatter.FormatEx(ex)}", "error"); + } + + return sendByte; + } + } +} diff --git a/StandardScene.Devices/Charge/MuXingChargeStation.cs b/StandardScene.Devices/Charge/MuXingChargeStation.cs new file mode 100644 index 0000000..5c3628c --- /dev/null +++ b/StandardScene.Devices/Charge/MuXingChargeStation.cs @@ -0,0 +1,456 @@ +using SimpleLite.RCS; +using SimpleLite.RCS.CarTypes; +using SimpleCore; +using SimpleCore.Library; +using SimpleCore.PropType; +using StandardScene.Charge; +using StandardScene.TCP; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net; +using System.Net.Sockets; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using System.Timers; +using System.Windows.Forms; + +namespace StandardScene.ChargeStationType + +{ + public class MuXingChargeStation : AbstractChargeStation + { + public static TcpListener tcpListener; + public static Thread listenerThread; + public static NetworkStream stream; + public static TcpClient client; + //public Dictionary chargeClient = new Dictionary { }; + public System.Timers.Timer _Timer; + public bool reciveHeartBeat; + public bool running; + public byte[] IdBytes = new byte[] { 0x0f, 0x01 }; + private static IPEndPoint _endPoint; + + /// + /// 鍏抽棴褰撳墠TCP杩炴帴 + /// + public override void CloseCommunication() + { + try + { + running = false; + + // 鍋滄瀹氭椂鍣 + if (_Timer != null) + { + _Timer.Stop(); + _Timer.Dispose(); + _Timer = null; + } + + // 鍏抽棴娴 + if (stream != null) + { + stream.Close(); + stream = null; + } + + // 鍏抽棴瀹㈡埛绔 + if (client != null) + { + client.Close(); + client = null; + } + + // 鍋滄鐩戝惉鍣 + if (tcpListener != null) + { + tcpListener.Stop(); + tcpListener = null; + } + + Diagnosis.Log($"MuXingChargeStation[{SiteId}] TCP connection closed"); + } + catch (Exception ex) + { + Diagnosis.Log($"ERR:MuXingChargeStation[{SiteId}] failed to close connection: {ex.Message}"); + } + } + + public override void CreateCommunication(IPAddress ip, int port) + { + // 鍏堝叧闂棫杩炴帴 + CloseCommunication(); + + _endPoint = new IPEndPoint(ip, port); + reciveHeartBeat = false; + running = true; + int n = 0; + DateTime offlineTime = DateTime.Now; + try + { + tcpListener = new TcpListener(ip, port); + tcpListener.Start(); + Diagnosis.Post($"AGV涓庣墽鏄熷厖鐢电珯閫氫俊宸插缓绔嬶紝鐩戝惉 IP: {ip}, 绔彛: {port}"); + client = tcpListener.AcceptTcpClient(); + client.SendTimeout = 5000; + client.ReceiveTimeout = 5000; + stream = client.GetStream(); + } + catch (Exception ex) + { + Console.WriteLine($"涓庡厖鐢电珯閫氫俊寤虹珛澶辫触: {ExceptionFormatter.FormatEx(ex)}"); + throw; + } + + while (running) + { + try + { + if (client == null) + { + client = tcpListener.AcceptTcpClient(); + client.SendTimeout = 5000; + client.ReceiveTimeout = 5000; + stream = client.GetStream(); + } + + var type = ReceiveMesageType(); + //item1:byte0 甯уご + //item2:byte11 鍛戒护瀛 + //item3:byte12 鍔ㄤ綔鐮/鏁呴殰鐮/鐘舵佺爜 + //item4:byte5 璁惧ID 浣 + //item5:byte6 璁惧ID 楂 + + //0x10 鍏呯數妗╃櫥褰曞洖澶 1娆 + if (type.Item1 == 0xAA && type.Item2 == 0x10) + { + var timestampBytes = GetTimeStamp(); + byte[] dataByte = new byte[16] + { + 0, 0x0d, + 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0x10, + 0, 0, + 0x00, 0x00 + }; //闀垮害蹇呴』澶т簬10,涓庡抚闀垮害瀵瑰簲 + dataByte = InitSendBytes(dataByte, type, timestampBytes); + byte[] mesSendByte = + CombineDataAndCRC(dataByte, CalculateCRC16(dataByte.Skip(1).ToArray())); //鎷兼帴crc锛堝幓鎺夊寘澶达級 + Diagnosis.Post($"login => {string.Join(" ", mesSendByte.Select(d => $"{d:X2}"))}", + $"鍏呯數妗╃櫥褰曞洖澶"); + SendMessage(mesSendByte); + } + //0x12 鍏呯數妗╁鎺ュ畬鎴愬洖澶 1娆 + else if (type.Item1 == 0xAA && type.Item2 == 0x12) + { + var timestampBytes = GetTimeStamp(); + byte[] dataByte = new byte[12] + { + 0, 0x09, + 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0x12 + }; + ; + dataByte = InitSendBytes(dataByte, type, timestampBytes); + byte[] sendByte = + CombineDataAndCRC(dataByte, CalculateCRC16(dataByte.Skip(1).ToArray())); //鎷兼帴crc锛堝幓鎺夊寘澶达級 + Diagnosis.Post($"docking => {string.Join(" ", sendByte.Select(d => $"{d:X2}"))}", + $"鍏呯數妗╁鎺ュ畬鎴愬洖澶"); + SendMessage(sendByte); + } + //0x13 鏁呴殰涓婃姤鍥炲 + else if (type.Item1 == 0xAA && type.Item2 == 0x13) + { + var timestampBytes = GetTimeStamp(); + byte[] dataByte = new byte[13] + { + 0, 0x0A, + 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0x13, + type.Item3 + }; + dataByte = InitSendBytes(dataByte, type, timestampBytes); + byte[] sendByte = + CombineDataAndCRC(dataByte, CalculateCRC16(dataByte.Skip(1).ToArray())); //鎷兼帴crc锛堝幓鎺夊寘澶达級 + Diagnosis.Post($"Error => {string.Join(" ", sendByte.Select(d => $"{d:X2}"))}", + $"鏁呴殰涓婃姤鍥炲"); + SendMessage(sendByte); + } + + //鏀跺埌鑷冲皯1娆″厖鐢垫々蹇冭烦鍖呬笂浼 + if (type.Item1 == 0xAA && type.Item2 == 0xF0 && !reciveHeartBeat) + { + Diagnosis.Post($"鏀跺埌鍏呯數妗╁績璺冲寘涓婁紶", $"HeartBeatRecive"); + reciveHeartBeat = true; + StartSendingHeartBeat(); + } + + //寮傚父鎯呭喌澶勭悊 + //鏁呴殰涓婃姤 鏁呴殰鐮佷笉涓0 16 17 1 + if (type.Item1 == 0xAA && type.Item2 == 0x13 && + (type.Item3 != 0 && type.Item3 != 16 && type.Item3 != 17 && type.Item3 != 1)) + { + //鍏呯數妗╂帀绾 + if ((type.Item3 & (1 << 3)) != 0 && reciveHeartBeat) + { + if (n == 0) offlineTime = DateTime.Now; + //鍏呯數妗╂帀绾挎椂闂村ぇ浜60s 閲嶅惎WiFi妯″潡 + if ((DateTime.Now - offlineTime).TotalSeconds > 60) + { + var timestampBytes = GetTimeStamp(); + //涓嬪彂 鍛戒护鐮佹寚浠 + byte[] dataByte = new byte[17] + { + 0, 0x0E, + 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0x15, + 0x04, //閲嶅惎WiFi妯″潡 + 0x00, 0x00, 0x00, 0x00 + }; + dataByte = InitSendBytes(dataByte, type, timestampBytes); + byte[] sendChargeByte = + CombineDataAndCRC(dataByte, CalculateCRC16(dataByte.Skip(1).ToArray())); + Diagnosis.Post( + $"Restart => {string.Join(" ", sendChargeByte.Select(d => $"{d:X2}"))}", + $"閲嶅惎WiFi妯″潡"); + SendMessage(sendChargeByte); + n = 0; + offlineTime = DateTime.Now; + } + + n++; + } + else + { + n = 0; + offlineTime = DateTime.Now; + } + } + + //if (!client.Connected) + //{ + // Console.WriteLine("瀹㈡埛绔柇寮杩炴帴锛岄鍑哄惊鐜"); + // running = false; + //} + + } + catch (Exception ex) + { + Console.WriteLine($"鍏呯數妗╅氫俊寮傚父锛歿ExceptionFormatter.FormatEx(ex)}"); + running = false; + client.Close(); + tcpListener.Stop(); + stream.Close(); + } + + } + } + + public override void SendToChargeStation(int isCharge, Car car,Site site) + { + var messageService = CommunicationMessageService.Instance; + float setVoltage = 55.0f; + float setElectricCurrent = 50.0f; + float voltage = 430; + // Site site = null; + if (car != null) + { + // site = SimpleLib.GetSite(car.status.holdingLocks.FirstOrDefault()); + + + int carId = 0; + if (car != null && car.GetType() != typeof(DummyCar)) + { + voltage = float.Parse(Commons.GetCarStatus(car, "Voltage")) * 10; + voltage = voltage > 430 ? voltage : 430; + carId = car.id; + } + } + if (site != null && site.fields.ContainsKey("setVoltage") && site.fields.ContainsKey("setElectricCurrent")) + { + setVoltage = float.Parse(site.fields["setVoltage"]); + setElectricCurrent = float.Parse(site.fields["setElectricCurrent"]); + } + + var byte1 = BitConverter.GetBytes(voltage); + var type = ReceiveMesageType(); + var openChargePort = isCharge == 1 ? 2 : 3; + //涓嬪彂鎵撳紑鍏呯數鍙f寚浠 + var timestampBytes = GetTimeStamp(); + byte[] dataByte = new byte[18] + { + 0, 0x0F, + 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0x11, + (byte)openChargePort,//寮濮嬪厖鐢/缁撴潫鍏呯數 + byte1[0], byte1[1], + 0xc8, 0x00,//涓嬪彂鏈澶у厖鐢电數娴 + 0x02//鐢垫睜绉嶇被 + }; + dataByte = InitSendBytes(dataByte, type, timestampBytes); + byte[] openCharge = + CombineDataAndCRC(dataByte, CalculateCRC16(dataByte.Skip(1).ToArray())); + if (client.Connected) + { + stream.WriteAsync(openCharge, 0, openCharge.Length); + stream.FlushAsync(); + } + messageService.AddSendMessage(_endPoint.Address.ToString(), _endPoint.Port, BitConverter.ToString(openCharge).Replace("-", " "), site?.name, "MuXing"); + Diagnosis.Post($"openCharge => {string.Join(" ", openCharge.Select(d => $"{d:X2}"))}", + $"涓嬪彂寮濮嬪厖鐢"); + + } + + public void StartSendingHeartBeat() + { + if (client == null) + { + Console.WriteLine("瀹㈡埛绔负null锛屾棤娉曞惎鍔ㄥ績璺冲寘瀹氭椂鍣"); + return; + } + _Timer = new System.Timers.Timer(2000); + _Timer.Elapsed += SendHeartBeat; + _Timer.AutoReset = true; // 鍙嶅鎵ц + _Timer.Enabled = true; + + } + + private void SendHeartBeat(Object item, ElapsedEventArgs e) + { + var timestampBytes = GetTimeStamp(); + var type = ReceiveMesageType(); + //涓嬪彂蹇冭烦鍖呮姤鏂 + var rcs = new byte[] + { + 0xBB, 0x09, 0x00, 0x03, 0x00, + IdBytes[0], IdBytes[1], + timestampBytes[0], timestampBytes[1], timestampBytes[2], timestampBytes[3], + 0xF0 + }; + byte[] mesSendByte = CombineDataAndCRC(rcs, CalculateCRC16(rcs.Skip(1).ToArray())); + // 鏃ュ織璁板綍 + Diagnosis.Post($"heartBeat => {string.Join(" ", mesSendByte.Select(d => $"{d:X2}"))}", $"涓嬪彂蹇冭烦鍖"); + // 鍙戦佸績璺冲寘 + SendMessage(mesSendByte); + } + + private byte[] GetTimeStamp() + { + // 鑾峰彇褰撳墠鏃堕棿鎴筹紙绉掔骇鍒級 + int timestamp = (int)DateTimeOffset.UtcNow.ToUnixTimeSeconds(); + //灏嗘椂闂存埑杞崲涓哄瓧鑺傛暟缁勶紝浣庡瓧鑺傚湪鍓 + byte[] timestampBytes = BitConverter.GetBytes(timestamp); + return timestampBytes; + } + + private static Tuple ReceiveMesageType() + { + byte[] message = new byte[1024]; + int bytesRead = 0; + try + { + if (client != null && client.Connected && stream != null && stream.CanRead && client.Available > 0) + { + bytesRead = stream.Read(message, 0, 1024); + } + } + catch (Exception ex) + { + client = null; + stream = null; + Console.WriteLine($"Error reading message: {ex.Message}"); + } + + //message闀垮害鍒ゆ柇锛氳嚦灏戦渶瑕 13 瀛楄妭鎵嶈兘瀹夊叏璁块棶 message[12] + if (bytesRead >= 13) + { + + string receivedMessage = BitConverter.ToString(message, 0, bytesRead); + Diagnosis.Post($"Received <= {string.Join(" ", message.Take(bytesRead).ToArray().Select(d => $"{d:X2}"))}", "璇诲彇鎶ユ枃"); + Diagnosis.Post($"item1:{message[0]:x2},item2:{message[11]:x2},item3:{message[12]:x2},item4:{message[5]:x2},item5:{message[6]:x2},", "Tuple.Item"); + var messageService = CommunicationMessageService.Instance; + messageService.AddReceiveMessage(_endPoint.Address.ToString(), _endPoint.Port, string.Join(",", message), "MuXing"); + return Tuple.Create(message[0], message[11], message[12], message[5], message[6]); + } + return new Tuple(0, 0, 0, 0, 0); + } + + public static byte[] CombineDataAndCRC(byte[] data, byte[] crc) + { + //byte[] crcBytes = BitConverter.GetBytes(crc); + // 鍚堝苟鏁版嵁鍜孋RC + byte[] combined = new byte[data.Length + crc.Length]; + Array.Copy(data, combined, data.Length); + Array.Copy(crc, 0, combined, data.Length, crc.Length); + + return combined; + } + + private static byte[] CalculateCRC16(byte[] data) + { + byte b = byte.MaxValue; + byte b2 = byte.MaxValue; + byte b3 = 1; + byte b4 = 160; + for (int i = 0; i < data.Length; i++) + { + b = (byte)(b ^ data[i]); + for (int j = 0; j <= 7; j++) + { + byte b5 = b2; + byte b6 = b; + b2 = (byte)(b2 >> 1); + b = (byte)(b >> 1); + if ((b5 & 1) == 1) + { + b = (byte)(b | 0x80u); + } + if ((b6 & 1) == 1) + { + b2 = (byte)(b2 ^ b4); + b = (byte)(b ^ b3); + } + } + } + return new byte[2] + { + b,b2 + }; + } + + private void SendMessage(byte[] message) + { + try + { + if (client != null && stream != null) + { + stream.Write(message, 0, message.Length); + stream.Flush(); + } + } + catch (Exception ex) + { + client = null; + Console.WriteLine($"Error sending message: {ExceptionFormatter.FormatEx(ex)}"); + } + } + + private byte[] InitSendBytes(byte[] sendBytes, Tuple type, byte[] timestampBytes) + { + sendBytes[0] = 0xBB; //甯уご + sendBytes[2] = 0x00; //甯ч暱 楂 + sendBytes[3] = 0x03; //璁惧绫诲瀷 浣 + sendBytes[4] = 0x00; //璁惧绫诲瀷 楂 + sendBytes[5] = IdBytes[0]; //璁惧ID 浣 + sendBytes[6] = IdBytes[1]; //璁惧ID 楂 + sendBytes[7] = timestampBytes[0]; //鏃堕棿鎴 浣 + sendBytes[8] = timestampBytes[1]; + sendBytes[9] = timestampBytes[2]; + sendBytes[10] = timestampBytes[3]; //鏃堕棿鎴 楂 + return sendBytes; + } + } +} diff --git a/StandardScene.Devices/Charge/PCBChargeStation.cs b/StandardScene.Devices/Charge/PCBChargeStation.cs new file mode 100644 index 0000000..525db2b --- /dev/null +++ b/StandardScene.Devices/Charge/PCBChargeStation.cs @@ -0,0 +1,147 @@ +using SimpleLite.RCS; +using SimpleLite.RCS.CarTypes; +using SimpleCore; +using SimpleCore.Library; +using SimpleCore.PropType; +using StandardScene.Charge; +using System; +using System.Collections.Generic; +using System.Diagnostics.Contracts; +using System.Linq; +using System.Net; +using System.Net.Sockets; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using System.Windows.Forms; +using static SimpleCore.Traffic.TrafficControl; + +namespace StandardScene.ChargeStationType +{ + public class PCBChargeStation : AbstractChargeStation + { + private int _index = 0; + + public int IndexReceive; + private IPEndPoint _endPoint; + + public override void OnUdpMessage(byte[] message) + { + if (message != null && message.Length > 1) + IndexReceive = message[1]; + } + + public override void CreateCommunication(IPAddress ip, int port) + { + _endPoint = new IPEndPoint(ip, port); + } + public override void SendToChargeStation(int isCharge, Car car,Site site) + { + var messageService = CommunicationMessageService.Instance; + using (UdpClient udpClient = new UdpClient()) + { + //float soc = 0f, voltage = 0, electricCurrent = 0, chargeTimeSpan = 30f; + + float setVoltage = 29.2f; + float setElectricCurrent = 40.0f; + float soc = 0f, voltage = 0, electricCurrent = 0, chargeTimeSpan = 30f; + int carId = 0; + //Site site = null; + if (car != null) + { + carId = car.id; + soc = (float)Commons.CarValue(car, "Soc"); + voltage = (float)Commons.CarValue(car, "Voltage"); + electricCurrent = (float)Commons.CarValue(car, "ElectricCurrent"); + //site = SimpleLib.GetSite(car.status.holdingLocks.FirstOrDefault()); + + } + if (site != null && site.fields.ContainsKey("setVoltage") && site.fields.ContainsKey("setElectricCurrent")) + { + setVoltage = float.Parse(site.fields["setVoltage"]); + setElectricCurrent = float.Parse(site.fields["setElectricCurrent"]); + } + + + + var msg = GetSendBytes((byte)isCharge, setVoltage, setElectricCurrent, chargeTimeSpan, + carId, soc, electricCurrent, voltage); + messageService.AddSendMessage(_endPoint.Address.ToString(), _endPoint.Port, BitConverter.ToString(msg).Replace("-", " "), "FRLDShort", site?.name); + Diagnosis.Log($"ChargeStation ADD:[{BitConverter.ToString(msg).Replace("-", " ")}]", "UDP鍙戦佹姤鏂囦俊鎭", true); + udpClient.SendAsync(msg, msg.Length, _endPoint); + Thread.Sleep(100); + } + } + + private byte[] GetSendBytes(byte startCharge, float chargeVoltage, float chargeElectricCurrent, float chargeTimeSpan, int carId, float carSoc, float carElectricCurrent, float carVoltage) + { + var sendBytes = new byte[32]; + var indexNo = (byte)GetIndexNo(); + try + { + sendBytes = new byte[3] { 0xBB, indexNo, startCharge } + .Concat(BitConverter.GetBytes((int)chargeElectricCurrent * 10).AsEnumerable().Reverse()) + .Concat(BitConverter.GetBytes((int)chargeVoltage * 10).AsEnumerable().Reverse()) + .Concat(BitConverter.GetBytes((ushort)chargeTimeSpan).AsEnumerable().Reverse()) + .Concat(BitConverter.GetBytes((ushort)carId).AsEnumerable().Reverse()) + .Concat([(byte)carSoc]) + .Concat(BitConverter.GetBytes((int)(carElectricCurrent * 10)).AsEnumerable().Reverse()) + .Concat(BitConverter.GetBytes((int)(carVoltage * 10)).AsEnumerable().Reverse()) + .Concat(new byte[] { 00, 00, 00, 00, 00, 00, 00, 0xEE }).ToArray(); + var crcCode = GetCRC(sendBytes.Skip(1).Take(28).ToArray()).AsEnumerable().Reverse().ToArray(); + sendBytes[29] = crcCode[0]; + sendBytes[30] = crcCode[1]; + } + catch (Exception ex) + { + Diagnosis.Log($"鍏呯數鎶ユ枃缁勫寘寮傚父 ex => {ex.Message}", "鍏呯數", true); + } + return sendBytes; + } + private byte[] GetCRC(byte[] data) + { + byte b = byte.MaxValue; + byte b2 = byte.MaxValue; + byte b3 = 1; + byte b4 = 160; + for (int i = 0; i < data.Length; i++) + { + b = (byte)(b ^ data[i]); + for (int j = 0; j <= 7; j++) + { + byte b5 = b2; + byte b6 = b; + b2 = (byte)(b2 >> 1); + b = (byte)(b >> 1); + if ((b5 & 1) == 1) + { + b = (byte)(b | 0x80u); + } + if ((b6 & 1) == 1) + { + b2 = (byte)(b2 ^ b4); + b = (byte)(b ^ b3); + } + } + } + return new byte[2] + { + b,b2 + }; + } + + private int GetIndexNo() + { + if (_index < 255) + { + _index = _index + 1; + } + else + { + _index = 0; + } + return _index; + } + } + +} diff --git a/StandardScene.Devices/Door/ModbusDoorController.cs b/StandardScene.Devices/Door/ModbusDoorController.cs new file mode 100644 index 0000000..5f6d387 --- /dev/null +++ b/StandardScene.Devices/Door/ModbusDoorController.cs @@ -0,0 +1,412 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using StandardScene.Utils; +using SimpleCore.Library; + +namespace StandardScene.ExtendDevice.Door +{ + /// + /// Modbus 闂ㄦ帶鍒跺櫒瀹炵幇 + /// + [DoorType("ModbusDoorController")] + public class ModbusDoorController : BasicDoorController + { + /// + /// Modbus TCP 瀹㈡埛绔 + /// + private ModbusRtu _modbusClient; + + /// + /// 鍚屾閿 + /// + private readonly object _syncLock = new object(); + + /// + /// 鏄惁宸插惎鍔 + /// + private bool _isStarted = false; + + /// + /// 鏈杩戜竴娆″凡涓嬪彂鐨勯棬鎺у埗鐘舵侊紝閿负闂ㄧ储寮 + /// + private readonly Dictionary _lastSentControl = new Dictionary(); + + /// + /// 瀹氭椂璇诲彇浠诲姟鍙栨秷浠ょ墝 + /// + private CancellationTokenSource _cancellationTokenSource; + + /// + /// 瀹氭椂璇诲彇浠诲姟 + /// + private Task _readTask; + + /// + /// 璇诲彇闂撮殧锛堟绉掞級锛岄粯璁1000ms + /// + public int ReadInterval { get; set; } = 1000; + + /// + /// 閲嶈繛闂撮殧锛堟绉掞級锛岄粯璁3000ms + /// + public int ReconnectInterval { get; set; } = 3000; + + /// + /// 涓婃閲嶈繛灏濊瘯鏃堕棿 + /// + private DateTime _lastReconnectAttempt = DateTime.MinValue; + + /// + /// Modbus 浠庣珯鍦板潃锛岄粯璁1 + /// + public byte SlaveAddress { get; set; } = 1; + + /// + /// 璁剧疆闂ㄧ殑鐩爣鎺у埗鐘舵侊紙绾跨▼瀹夊叏瀹炵幇锛 + /// 浠呬慨鏀瑰唴瀛樺瓧娈碉紝涓嶇洿鎺ヨ繘琛岄氫俊锛屽疄闄呴氫俊鍦ㄥ唴閮ㄧ嚎绋嬩腑瀹屾垚 + /// + /// 闂ㄧ储寮 + /// true=鎵撳紑锛宖alse=鍏抽棴 + public override void SetDoorControlTarget(int doorIndex, bool open) + { + lock (_syncLock) + { + base.SetDoorControlTarget(doorIndex, open); + } + } + + /// + /// 杩炴帴闂ㄦ帶鍒跺櫒 + /// + public override void Connect() + { + lock (_syncLock) + { + if (_isStarted) + { + return; + } + + try + { + UpdateState(DoorControllerState.Connecting); + + // 鏍规嵁閰嶇疆鐨勯棬鍒濆鍖栭棬鐘舵 + var doorIndices = DoorConfigs.Keys.OrderBy(k => k).ToList(); + InitializeDoors(doorIndices); + + // 鍒濆鍖栨渶杩戜竴娆″凡涓嬪彂鐨勬帶鍒剁姸鎬 + _lastSentControl.Clear(); + foreach (var index in doorIndices) + { + _lastSentControl[index] = false; + if (!DoorControlTargets.ContainsKey(index)) + { + DoorControlTargets[index] = false; + } + } + + // 灏濊瘯杩炴帴 Modbus TCP 瀹㈡埛绔 + try + { + _modbusClient = new ModbusRtu(); + _modbusClient.StartTcpRtu(Ip, Port); + UpdateState(DoorControllerState.Online); + } + catch (Exception connectEx) + { + UpdateState(DoorControllerState.Connecting); + Diagnosis.Log($"ModbusDoorController[{Index}] 鍒濇杩炴帴澶辫触锛屽皢鍦ㄥ悗鍙版寔缁噸杩: {ExceptionFormatter.FormatEx(connectEx)}", "ModbusDoorController", true); + } + + // 鍚姩瀹氭椂璇诲彇浠诲姟 + _cancellationTokenSource = new CancellationTokenSource(); + _readTask = Task.Run(() => ReadDoorStatesLoop(_cancellationTokenSource.Token)); + + _isStarted = true; + } + catch (Exception ex) + { + UpdateState(DoorControllerState.Error, $"鍒濆鍖栧け璐: {ex.Message}"); + _isStarted = false; + Diagnosis.Log($"ModbusDoorController[{Index}] 鍒濆鍖栧け璐: {ExceptionFormatter.FormatEx(ex)}", "ModbusDoorController", true); + } + } + } + + /// + /// 鏂紑杩炴帴 + /// + public override void Disconnect() + { + lock (_syncLock) + { + if (!_isStarted) + { + return; + } + + try + { + // 鍋滄璇诲彇浠诲姟 + _cancellationTokenSource?.Cancel(); + _readTask?.Wait(1000); + + // 鍏抽棴 Modbus 杩炴帴 + _modbusClient?.Close(); + _modbusClient = null; + + _isStarted = false; + UpdateState(DoorControllerState.Offline); + } + catch (Exception ex) + { + UpdateState(DoorControllerState.Error, $"鏂紑杩炴帴澶辫触: {ex.Message}"); + Diagnosis.Log($"ModbusDoorController[{Index}] 鏂紑杩炴帴澶辫触: {ExceptionFormatter.FormatEx(ex)}", "ModbusDoorController", true); + } + } + } + + /// + /// 瀹氭椂璇诲彇闂ㄧ姸鎬佸惊鐜 + /// + private void ReadDoorStatesLoop(CancellationToken cancellationToken) + { + while (!cancellationToken.IsCancellationRequested) + { + try + { + if (!_isStarted) + { + break; + } + + // 妫鏌ヨ繛鎺ョ姸鎬 + bool isConnected = _modbusClient?.modbusRtu?.Connected ?? false; + if (_modbusClient == null || !isConnected) + { + UpdateState(DoorControllerState.Connecting); + + // 鎺у埗閲嶈繛棰戠巼 + var timeSinceLastReconnect = (DateTime.Now - _lastReconnectAttempt).TotalMilliseconds; + if (timeSinceLastReconnect >= ReconnectInterval) + { + _lastReconnectAttempt = DateTime.Now; + TryReconnect(); + } + + isConnected = _modbusClient?.modbusRtu?.Connected ?? false; + if (_modbusClient == null || !isConnected) + { + Thread.Sleep(ReadInterval); + continue; + } + } + + // 璇诲彇鎵鏈夐棬鐨勭姸鎬 + ReadAllDoorStates(); + + // 鏍规嵁鐩爣鎺у埗鐘舵佷笅鍙戞帶鍒舵寚浠 + ApplyDoorControlTargets(); + + // 鏇存柊鍦ㄧ嚎鐘舵 + UpdateState(DoorControllerState.Online); + } + catch (Exception ex) + { + Diagnosis.Log($"ModbusDoorController[{Index}] 璇诲彇鐘舵佸け璐: {ExceptionFormatter.FormatEx(ex)}", "ModbusDoorController", true); + UpdateState(DoorControllerState.Error, $"璇诲彇鐘舵佸け璐: {ex.Message}"); + + // 濡傛灉杩炴帴澶辫触锛屽皾璇曢噸杩 + TryReconnect(); + } + + // 绛夊緟鎸囧畾闂撮殧 + Thread.Sleep(ReadInterval); + } + } + + /// + /// 灏濊瘯閲嶈繛 Modbus TCP 杩炴帴 + /// + private void TryReconnect() + { + ModbusRtu oldClient = null; + try + { + // 瀹夊叏鍏抽棴鏃ц繛鎺 + if (_modbusClient != null) + { + oldClient = _modbusClient; + _modbusClient = null; + + try + { + oldClient.Close(); + } + catch + { + // 蹇界暐鍏抽棴鏃剁殑寮傚父 + } + finally + { + oldClient = null; + } + } + + // 鍒涘缓鏂拌繛鎺 + _modbusClient = new ModbusRtu(); + _modbusClient.StartTcpRtu(Ip, Port); + + Diagnosis.Log($"ModbusDoorController[{Index}] 閲嶈繛鎴愬姛", "ModbusDoorController", false); + } + catch (Exception ex) + { + // 閲嶈繛澶辫触锛岀‘淇濊祫婧愰噴鏀 + if (_modbusClient != null) + { + try + { + _modbusClient.Close(); + } + catch + { + // 蹇界暐鍏抽棴寮傚父 + } + _modbusClient = null; + } + + Diagnosis.Log($"ModbusDoorController[{Index}] 閲嶈繛澶辫触: {ExceptionFormatter.FormatEx(ex)}", "ModbusDoorController", false); + } + } + + /// + /// 璇诲彇鎵鏈夐棬鐨勭姸鎬 + /// + private void ReadAllDoorStates() + { + lock (_syncLock) + { + foreach (var doorConfig in DoorConfigs.Values) + { + try + { + var state = ReadDoorState(doorConfig.Index); + UpdateDoorState(doorConfig.Index, state ? DoorState.Open : DoorState.Closed); + } + catch (Exception ex) + { + Diagnosis.Log($"ModbusDoorController[{Index}] 璇诲彇闂▄doorConfig.Index}鐘舵佸け璐: {ExceptionFormatter.FormatEx(ex)}", "ModbusDoorController", true); + } + } + } + } + + /// + /// 鏍规嵁 DoorControlTargets 涓殑鐩爣鐘舵侊紝涓嬪彂闂ㄦ帶鍒舵寚浠 + /// + private void ApplyDoorControlTargets() + { + lock (_syncLock) + { + foreach (var doorConfig in DoorConfigs.Values) + { + var doorIndex = doorConfig.Index; + + // 鑾峰彇鐩爣鎺у埗鐘舵侊紝榛樿false + bool target = false; + DoorControlTargets.TryGetValue(doorIndex, out target); + + // 濡傛灉闂ㄩ厤缃负涓嶅厑璁稿彂閫佷换浣曟帶鍒舵寚浠わ紝鍒欒烦杩囷紙鏃笉鎵撳紑涔熶笉鍏抽棴锛 + if (doorConfig.NoControl) + { + continue; + } + + // 鑾峰彇涓婁竴娆″凡涓嬪彂鐨勭姸鎬 + bool last; + var hasLast = _lastSentControl.TryGetValue(doorIndex, out last); + + // 濡傛灉娌℃湁璁板綍鎴栫姸鎬佸彂鐢熷彉鍖栵紝鍒欎笅鍙戞帶鍒 + if (!hasLast || last != target) + { + try + { + WriteDoorControl(doorIndex, target); + _lastSentControl[doorIndex] = target; + } + catch (Exception ex) + { + Diagnosis.Log($"ModbusDoorController[{Index}] 涓嬪彂闂▄doorIndex}鎺у埗鎸囦护澶辫触: {ExceptionFormatter.FormatEx(ex)}", "ModbusDoorController", true); + } + } + } + } + } + + /// + /// 璇诲彇闂ㄧ姸鎬侊紙寮鍒颁綅淇″彿锛 + /// + /// 闂ㄧ储寮 + /// true=鎵撳紑锛宖alse=鍏抽棴 + public override bool ReadDoorState(int doorIndex) + { + lock (_syncLock) + { + if (!DoorConfigs.TryGetValue(doorIndex, out var doorConfig)) + { + throw new ArgumentException($"闂▄doorIndex}涓嶅瓨鍦"); + } + + if (_modbusClient == null || !_modbusClient.modbusRtu.Connected) + { + throw new InvalidOperationException("Modbus杩炴帴鏈缓绔"); + } + + // 璇诲彇寮鍒颁綅淇″彿 + var data = _modbusClient.ReadDiscreteInputs_02(SlaveAddress, doorConfig.OpenStatusAddress, 1); + return data != null && data.Length > 0 && data[0]; + } + } + + /// + /// 鍐欏叆闂ㄦ帶鍒朵俊鍙凤紙寮鍏虫帶鍒讹級 + /// + /// 闂ㄧ储寮 + /// true=鎵撳紑锛宖alse=鍏抽棴 + public override void WriteDoorControl(int doorIndex, bool open) + { + lock (_syncLock) + { + if (!DoorConfigs.TryGetValue(doorIndex, out var doorConfig)) + { + throw new ArgumentException($"闂▄doorIndex}涓嶅瓨鍦"); + } + + if (_modbusClient == null || !_modbusClient.modbusRtu.Connected) + { + // 灏濊瘯閲嶈繛 + TryReconnect(); + if (_modbusClient == null || !_modbusClient.modbusRtu.Connected) + { + throw new InvalidOperationException("Modbus杩炴帴鏈缓绔"); + } + } + + // 鍐欏叆寮鍏虫帶鍒朵俊鍙凤紙绾垮湀锛 + _modbusClient.WriteMultipleCoils_15(SlaveAddress, doorConfig.ControlAddress, [open]); + } + } + + /// + /// 鏋愭瀯鍑芥暟锛岀‘淇濊祫婧愰噴鏀 + /// + ~ModbusDoorController() + { + Disconnect(); + } + } +} diff --git a/StandardScene.Devices/StandardScene.Devices.csproj b/StandardScene.Devices/StandardScene.Devices.csproj new file mode 100644 index 0000000..f5f0b04 --- /dev/null +++ b/StandardScene.Devices/StandardScene.Devices.csproj @@ -0,0 +1,54 @@ + + + + net8.0-windows + Library + true + StandardScene + StandardScene.Devices + latest + true + AnyCPU;x64 + x64 + true + false + disable + disable + false + $(NoWarn);NU1701;CS0618;CS0612;MSB3277;CA1416 + {HintPathFromItem};{TargetFrameworkDirectory};{RawFileName};{GAC} + + + + + + + + + + + + + + + + E:\Work\Core\Simple-FR\Simple\SimpleLite\bin\Debug\SimpleLite.dll + + + E:\Work\Core\Simple-FR\Simple\SimpleCore\bin\Debug\netstandard2.0\SimpleCore.dll + + + D:\MDCS\Dependencies\Commons\CommonUsage.dll + + + E:\Work\Core\Simple-FR\Simple\tools\Topaz.dll + + + E:\Work\Core\Simple-FR\Simple\SimpleLite\bin\Debug\LessokajiWeaverUtilities.dll + + + ..\StandardScene.Core\Ref\leegKeys-sdk.dll + + + + diff --git a/StandardScene.Devices/StandardScene.Devices.scene.json b/StandardScene.Devices/StandardScene.Devices.scene.json new file mode 100644 index 0000000..43a1755 --- /dev/null +++ b/StandardScene.Devices/StandardScene.Devices.scene.json @@ -0,0 +1,12 @@ +{ + "id": "scene.device", + "displayName": "璁惧椹卞姩锛堥棬 / 鍏呯數妗 / 鎸夐挳鐩掞級", + "assembly": "StandardScene.Devices.dll", + "coreVersion": ">=1.0.0", + "requiresCore": "StandardScene.dll", + "provides": { + "doorControllers": [ "ModbusDoorController" ], + "chargeStations": [ "FLChargeStation", "PCBChargeStation", "MuXingChargeStation" ], + "buttonBoxes": [ "LeegButtonBox", "AzowieButtonBox" ] + } +} diff --git a/StandardScene.Magnetic/CarTypes/Kiva.cs b/StandardScene.Magnetic/CarTypes/Kiva.cs new file mode 100644 index 0000000..84d82ab --- /dev/null +++ b/StandardScene.Magnetic/CarTypes/Kiva.cs @@ -0,0 +1,692 @@ +锘縰sing LessokajiWeaverUtilities.Utilities; +using Newtonsoft.Json; +using SimpleLite; +using SimpleLite.RCS; +using SimpleLite.RCS.CarTypes; +using SimpleLite.CADTools; +using SimpleLite.Props; +using SimpleLite.UI; +using SimpleCore; +using SimpleCore.Compiler; +using SimpleCore.Extras; +using SimpleCore.Library; +using SimpleCore.PropType; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Drawing; +using System.Drawing.Drawing2D; +using System.Linq; +using System.Net.Http; +using System.Numerics; +using System.Reflection; +using System.Threading; +using System.Threading.Tasks; +using System.Windows.Forms; +using StandardScene.Model; +using StandardScene.Coders; +using StandardScene.Magnetic.Coders; +using Track = SimpleCore.PropType.Track; + +namespace StandardScene.CarTypes +{ + // MagneticTrackCoder 宸茬墿鐞嗚縼鍑鸿嚦 StandardScene.Magnetic.Coders锛坰cene.mag 骞冲彴锛夈 + // Kiva 绯诲瓧娈佃锛圞ivaCarFields/KivaSiteFields/KivaTrackFields/KivaPlanFields锛変笅娌夎嚦 + // 鍩哄骇 StandardScene.Core\CarTypes\KivaFields.cs锛圓rmCar 绛夎法骞冲彴杞﹀瀷缁ф壙瀹冧滑锛夈 + + //鏍囧畾杞噷绋嬭 + [TemplateTrackCoderSettings( + priority = 10, + useVerb = "track.CalibrateWheelEncoder && track.ReverseDst != dst.id", + blockVerb = "true", + templateString = "agv.CalibrateWheelEncoder(${src.x},${src.y},${src.id},${dst.x},${dst.y},${dst.id});", + siteFields = typeof(KivaSiteFields), + trackFields = typeof(KivaTrackFields))] + + // 閫氱敤閬块殰/IO/绾犲亸 coder 宸叉娊绂讳负 StandardScene.Coders.*锛堣绫诲墠 [ProgramTrackCoderSettings] 寮曠敤锛 + + // 閬块殰鍖哄昂瀵(2鍙)鍒囨崲 coder 宸叉娊绂讳负 StandardScene.Coders.AvoidanceParamLWCoder锛堟柟妗 B锛涜涓嬫柟 [ProgramTrackCoderSettings] 寮曠敤锛 + [ProgramTrackCoderSettings(priority = 20, program = typeof(AvoidanceParamLWCoder))] + + //plan.SegN 鏄寚 褰撳墠璺緞鎵鏈夌偣绾跨殑鏁伴噺 + //plan.SegN-2 灏辨槸缁堢偣鍊掓暟绗簩涓珯鐐 + //[TemplateTrackCoderSettings( + // priority = 1, + // templateString = "agv.BasicGo(${src.x},${src.y},${src.id},${dst.x},${dst.y},${dst.id}," + + // "${track.id},${track.Speed},${track.Reverse||(track.ReverseDst==dst.id)}," + + // "${track.CarDirectionBias},${track.EnableCarAbsoluteDirection},${track.CarAbsoluteDirection},${track.typeInfo});", + // siteFields = typeof(KivaSiteFields), + // trackFields = typeof(KivaTrackFields), + // planFields = typeof(KivaPlanFields))] + + [TemplateTrackCoderSettings( + priority = 30, + useVerb = "dst.tag>0 && src.tag>0", + templateString = "agv.QrGo(${src.x},${src.y},${src.id},${src.tag},${dst.x},${dst.y},${dst.id},${dst.tag},${track.id}," + + "${track.Speed},${track.Reverse || (track.ReverseDst == dst.id)}," + + "${track.CarDirectionBias},${track.EnableCarAbsoluteDirection},${track.CarAbsoluteDirection}," + + "${track.typeInfo});", + blockVerb = "true", + siteFields = typeof(KivaSiteFields), + trackFields = typeof(KivaTrackFields), + planFields = typeof(KivaPlanFields))] + //[TemplateTrackCoderSettings( + // priority = 2, + // useVerb = "track.ReverseDst==dst.id", + // templateString = "agv.BasicGo(${src.x},${src.y},${src.id},${dst.x},${dst.y},${dst.id},${track.id},${track.speed},0,true);", + // blockVerb = "true", + // siteFields = typeof(KivaSiteFields), + // trackFields = typeof(KivaTrackFields))] + + [TemplateTrackCoderSettings( + priority = 20, + useVerb = "plan.action=='fetch' && dst.Shelf&&plan.curSeg==plan.segN-2", + templateString = "agv.Wait();" + + "agv.Fetch(${src.x},${src.y},${src.id},${dst.x},${dst.y},${dst.id}," + + "${dst.FetchSpeed},${dst.FetchLidarArea},${dst.FetchIOArea},${dst.FetchReverse}," + + "${dst.FetchBlindMoveDist},${dst.FetchLiftDownTarget},${dst.FetchLiftUpTarget}," + + "${dst.FetchUseQr},${dst.FetchQrMode},${dst.FetchIsUpQr},${dst.FetchUseDetector},${dst.FetchDetector},${dst.FetchDetectWidth},${dst.FetchDetectDepth}," + + "${plan.CarLength},${plan.CarWidth},${dst.FetchLeaveSrcEarly},${dst.FetchShieldObstacleDist});" + + "agv.Wait();", + blockVerb = "true", + siteFields = typeof(KivaSiteFields), + trackFields = typeof(KivaTrackFields), + planFields = typeof(KivaPlanFields))] + + [TemplateTrackCoderSettings( + priority = 20, + useVerb = "plan.action=='put' && dst.Shelf&&plan.curSeg==plan.segN-2", + templateString = "agv.Wait();" + + "agv.Put(${src.x},${src.y},${src.id},${dst.x},${dst.y},${dst.id}," + + "${dst.PutSpeed},${dst.PutLidarArea},${dst.PutIOArea},${dst.PutReverse},${dst.PutSyncRotate}," + + "${dst.PutBlindMoveDist},${dst.PutLiftDownTarget},${dst.PutLiftUpTarget}," + + "${dst.PutUseQr},${dst.PutQrMode},${dst.PutIsUpQr},${dst.PutUseDetector},${dst.PutDetector}," + + "${dst.PutDetectWidth},${dst.PutDetectDepth},${dst.PutLeaveSrcEarly},${dst.PutShieldObstacleDist});" + + "agv.Wait();", + blockVerb = "true", + siteFields = typeof(KivaSiteFields), + trackFields = typeof(KivaTrackFields), + planFields = typeof(KivaPlanFields))] + + [TemplateTrackCoderSettings( + priority = 22, + useVerb = "src.Shelf&&plan.curSeg==1", + templateString = "agv.LeaveShelf(${src.x},${src.y},${src.id},${dst.x},${dst.y},${dst.id}," + + "${src.LeaveShelfSpeed},${src.LeaveShelfLidarArea},${src.LeaveShelfIOArea},${src.LeaveShelfReverse},${src.LeaveShelfSyncRotate}," + + "${src.LeaveShelfBlindMoveDist},${src.LeaveShelfLiftDownTarget},${src.LeaveShelfRecoveryObstacleDist},);", + blockVerb = "true", + siteFields = typeof(KivaSiteFields), + trackFields = typeof(KivaTrackFields), + planFields = typeof(KivaPlanFields))] + + [TemplateSiteCoderSettings( + priority = 25, + useVerb = "plan.action=='fetch'&& plan.segN == 1 &&dst.Shelf", + templateString = + "agv.FetchInPlace(${dst.FetchUseQr},${dst.FetchQrMode},${dst.FetchIsUpQr},${dst.FetchLiftUpTarget},${plan.CarLength},${plan.CarWidth});" + + "agv.Wait();", + blockVerb = "true", + siteFields = typeof(KivaSiteFields), + trackFields = typeof(KivaTrackFields), + planFields = typeof(KivaPlanFields))] + //[TemplateSiteCoderSettings( + // priority = 100, + // useVerb = "plan.curSeg == 0", + // templateString = "agv.StartingMission();agv.Wait();", + // blockVerb = "false", + // siteFields = typeof(KivaSiteFields), + // trackFields = typeof(KivaTrackFields), + // planFields = typeof(KivaPlanFields))] + + + + [ProgramTrackCoderSettings(priority = 5, program = typeof(KivaCarTrackCoder))] + [ProgramTrackCoderSettings(priority = 19, program = typeof(MagneticTrackCoder))] + [ProgramTrackCoderSettings(priority = 17, program = typeof(LidarAreaSwitchCoder))] + [ProgramTrackCoderSettings(priority = 20, program = typeof(AvoidanceDistanceCoder))] + [ProgramTrackCoderSettings(priority = 20, program = typeof(IoAreaSwitchCoder))] + [ProgramTrackCoderSettings(priority = 20, program = typeof(TrackingErrThreshCoder))] + [CarType(Name = "Kiva", editor = typeof(Kiva))] + [I18N.DocumentTranslation(Name = "Kiva", locale = "en")] + [EnvelopConfig(centerX = 0, centerY = 0, lengthX = 950, lengthY = 650)] + public class Kiva : GhostCar, IScriptErrorRecoverable + { + /// 鑴氭湰寮傚父鑷仮澶嶏紙AbstractLoopMission 缁 IScriptErrorRecoverable 璋冪敤锛夛紝杞皟 newReset銆 + public void RecoverReset(int resetSiteId = 0) => newReset(resetSiteId); + + [FieldMember] public float CarLength = 2000; + [FieldMember] public float CarWidth = 1400; + public class RotateSiteEnvelope : SiteEnvelopeDefinition + { + public override bool Use() => true; + + public override void Prompt() + { + if (curSeg == 0) + { + return; + } + + if (plan.codeArr == null) + { + return; + } + + var carConfig = usingCar.GetType().GetCustomAttribute(); + var carLength = carConfig.lengthX; + var carWidth = carConfig.lengthY; + var code = plan.codeArr[curSeg - 1]; + if (code.Contains("Fetch")) + { + var lengthX = plan.fields.TryGetValue("MaterialLength", out var materialLength) + ? (int.TryParse(materialLength, out var lengthResult) ? lengthResult > 0 ? lengthResult : carLength : carLength) + : carLength; + var lengthY = plan.fields.TryGetValue("MaterialWidth", out var materialWidth) + ? (int.TryParse(materialWidth, out var widthResult) ? widthResult > 0 ? widthResult : carWidth : carWidth) + : carWidth; + reshape(lengthX, lengthY, 0, 0); + } + else if (code.Contains("Put")) + { + reshape(carLength, carWidth, 0, 0); + } + } + + public override bool Block() => false; + + public override int priority => 1; + } + + public static async Task Create() + { + var kiva = new Kiva() + { + lstatus = "杩炴帴涓", + address = "127.0.0.1", + name = $"Kiva", + haveCoordination = true, + speed = 1, + }; + return kiva; + } + + private const float DrawWidth = 950, DrawLength = 650; + private readonly Pen orientPen = new Pen(Color.White, 3); + private bool twinkle = false; + + protected override void draw(Graphics eGraphics) + { + try + { + var DrawWidth = CarLength; + var DrawLength = CarWidth; + var lineCap = new AdjustableArrowCap(5, 5, true); + orientPen.CustomEndCap = lineCap; + orientPen.StartCap = LineCap.RoundAnchor; + var alarmLevel = Commons.GetCarStatus(this, "AlarmLevel"); + var soc = Commons.CarValue((Car)this, "Soc"); + var electricCurrent = Commons.CarValue((Car)this, "ElectricCurrent"); + var thresSpeed = Commons.CarValue(this, "ThresSpeed"); + LadderLogic.FlipFlop(ref twinkle, 500, true, false); + Rectangle destRect = new Rectangle( + (int)((int)(-DrawWidth / 2)), + (int)((int)(-DrawLength / 2)), + (int)((int)DrawWidth), + (int)((int)DrawLength) + ); + if (int.TryParse(alarmLevel, out var alarm) && alarm > 0) + { + eGraphics.FillRectangle(twinkle ? Brushes.DarkRed : Brushes.Green, destRect); + } + else if (alarmLevel == "-1" || this.siteID == -1) + eGraphics.FillRectangle(Brushes.LightSeaGreen, destRect); + else if (electricCurrent > 1) + eGraphics.FillRectangle(Brushes.YellowGreen, destRect); + else if (thresSpeed <= 0) + { + eGraphics.FillRectangle(twinkle ? Brushes.YellowGreen : Brushes.Green, destRect); + } + else if (soc < 25) + { + eGraphics.FillRectangle(twinkle ? Brushes.DarkRed : Brushes.LightSeaGreen, destRect); + } + else + eGraphics.FillRectangle(Brushes.Green, destRect); + + eGraphics.DrawEllipse(Pens.Yellow, -160, -160, 320, 320); + eGraphics.DrawLine(orientPen, 0, 0, 480, 0); + + } + catch (Exception e) + { + Diagnosis.Post("缁樺埗灏忚溅寮傚父" + ExceptionFormatter.FormatEx(e), "缁樺埗灏忚溅寮傚父", true); + } + } + + private bool LoopTestRunning = false; + [MethodMember(Name = "寰幆娴嬭瘯")] + [I18N.DocumentTranslation(Name = "Loop Test", locale = "en")] + public async void LoopTest() + { + LoopTestRunning = true; + + var a = SimpleLib.GetAllSites().First(s => s.name == "A"); + var b = SimpleLib.GetAllSites().First(s => s.name == "B"); + + var plan1 = new SegmentPlan() { usingCar = this }; + plan1.fields["action"] = "fetch"; + plan1.FindRoute(SimpleLib.GetSite(GetLastSite()), a); + await plan1.Compile("fetch").Queue(); + + var plan2 = new SegmentPlan() { usingCar = this }; + plan2.fields["action"] = "go"; + plan2.FindRoute(SimpleLib.GetSite(GetLastSite()), b); + await plan2.Compile("go").Queue(); + + var plan3 = new SegmentPlan() { usingCar = this }; + plan3.fields["action"] = "put"; + plan3.FindRoute(SimpleLib.GetSite(GetLastSite()), a); + await plan3.Compile("put").Queue(); + + var plan4 = new SegmentPlan() { usingCar = this }; + plan4.fields["action"] = "go"; + plan4.FindRoute(SimpleLib.GetSite(GetLastSite()), b); + await plan4.Compile("go").Queue(); + + } + + [MethodMember(Name = "寰幆鍋滄娴嬭瘯")] + [I18N.DocumentTranslation(Name = "Stop Loop Test", locale = "en")] + public void LoopTestStop() + { + LoopTestRunning = false; + } + public override string SetDisplayInfo() + { + try + { + var str = $"{name}({id})\n"; + status.enums.TryGetValue("ChassisMode", out var manual); + var ms = manual == "0" ? "鑷姩" : "鎵嬪姩"; + status.enums.TryGetValue("Soc", out var soc); + str = $"{str}{ms}|soc:{soc}"; + var alarmStr = Commons.GetCarStatus(this, "AlarmInfo"); + if (alarmStr != "") + str = $"{str}|{alarmStr}"; + //if (status.enums.TryGetValue("mAlarm", out var mAlarm) && mAlarm != "") + // str = $"{str}|M:{mAlarm}"; + //var hasC = status.enums.TryGetValue("cAlarm", out var cAlarm) && cAlarm != ""; + //if (mAlarm != "" && hasC) + // str = $"{str}|"; + //if (hasC) + // str = $"{str}C:{cAlarm}"; + //var L_step = status.enums.TryGetValue("ls", out var ls) && ls != ""; + //if (L_step) + //{ + // str = $"{str}|ls:{ls}"; + //} + if (status.enums.TryGetValue("ElectricCurrent", out var electricCurrent) && + !string.IsNullOrEmpty(electricCurrent)) + { + var charging = float.Parse(electricCurrent) * 0.01f > 0 ? "鍏呯數涓" : "鏈厖鐢"; + str = $"{str}|charge:{charging}"; + } + + var thresSpeed = Commons.CarValue(this, "ThresSpeed"); + str = $"{str}{((thresSpeed <= 0) ? "|閬块殰瑙﹀彂" : "")}"; + return str; + } + catch (Exception e) + { + Console.WriteLine(e); + return "bad car"; + } + } + + [MethodMember(Name = "杩涘叆灏忚溅杩滅▼", Description = "杩涘叆灏忚溅杩滅▼妗岄潰")] + public void Mstsc() + { + var ip = this.address; + // 鍚姩mstsc骞朵紶閫扞P鍦板潃 + Process.Start( + new ProcessStartInfo + { + FileName = "mstsc", + Arguments = $"/v:{ip}", + UseShellExecute = false, + CreateNoWindow = true + } + ); + } + + [MethodMember(Name = "鏄剧ず杞﹁締鐩戞帶", Description = "鎵撳紑杞﹁締鐘舵佺洃鎺х獥鍙")] + public void ShowVehicleMonitor() + { + VehicleMonitor.ShowMonitor(); + } + + [MethodMember(Name = "妯℃嫙鐢甸噺", Description = "/")] + public void SimarSoc() + { + if (InputBox.ShowDialog("妯℃嫙鐢甸噺", "妯℃嫙鐢甸噺Soc", "0", InputBox.Buttons.OkCancel) == SimpleLite.DialogResult.Cancel) return; + string value = InputBox.ResultValue; + try + { + status.enums["Soc"] = (60 + int.Parse(value)).ToString(); + status.enums["Voltage"] = (50 + int.Parse(value)).ToString(); + status.enums["ElectricCurrent"] = (40 + int.Parse(value)).ToString(); + + } + catch (Exception) + { + + + } + + + } + + + + [MethodMember(Name = "绔嬪嵆寮哄埗缁撴潫new", Description = "绔嬪埢缁堟灏忚溅杩愯锛岄噸鍚疌锛屽苟璁╁皬杞︿笅绾")] + [I18N.DocumentTranslation(Name = "Force Stop new", Description = "Stop car & reset clumsy", locale = "en")] + public new async void ForceStop() + { + try + { + NoSchedule(true); + siteID = -1; + Commons.ClearTags(tags); + new Thread(() => + { + try + { + AppendDebug("restarting clumsy"); + Get("reset"); + AppendDebug("wait for any pending task to flush."); + try + { + status.programs.task.Wait(); + } + catch + { + } + + AppendDebug("Clumsy Restarted"); + NoSchedule(true); + siteID = -1; + Commons.DeleteTag(tags, "occupied"); + } + catch (Exception ex) + { + MessageBox.Show( + $@"鏈兘绔嬪嵆寮哄埗缁撴潫灏忚溅{name}({id})锛屽師鍥狅細{ExceptionFormatter.FormatEx(ex)}" + ); + } + }) + { + Name = $"ForceStop:{name}({id})" + }.Start(); + } + catch (Exception e) + { + Console.WriteLine(e); + throw; + } + } + + [MethodMember(Name = "杩斿巶妫淇畁ew", Description = "涓嶅啀鍒锋柊灏忚溅鐘舵侊紝褰撶劧涔熶笉鍐嶈璋冨害")] + [I18N.DocumentTranslation(Name = "Blown new", Description = "Car no longer refresh status and scheduling", locale = "en")] + public new void Blown() + { + AppendDebug("ui-blown"); + Diagnosis.Post($"Car {name}({id}) blown"); + NoSchedule(); + siteID = -1; + tags.Clear(); + status.usage.AddUsage( + "base", + new CarUsage.CarUsageInfo { scheduling = false, refreshing = false } + ); + lstatus = "杩斿巶妫淇"; + } + + [MethodMember(Name = "鐜板満妫淇畁ew ", Description = "璁╁皬杞︿笉鍐嶈璋冨害锛屼絾浠嶇劧鍒锋柊鐘舵侊紝骞朵笖褰撳墠鐐逛笉鍐嶄娇鐢")] + [I18N.DocumentTranslation(Name = "Repair new", Description = "Car no longer scheduling but still refresh status", locale = "en")] + public new void Repair() + { + AppendDebug("ui-repair"); + Diagnosis.Post($"Repair Car {name}({id})"); + NoSchedule(); + siteID = -1; + Commons.DeleteTag(tags, "occupied"); + + lstatus = "鐜板満妫淇"; + } + + public void newReset(int resetId = 0) + { + //if (!status.programs.task.IsCompleted && + // MessageBox.Show($"灏忚溅{name}({id})褰撳墠鏈変换鍔★細{status.programs.printStatus()}锛岀‘璁ゅ垵濮嬪寲锛", "纭", + // MessageBoxButtons.YesNo) == DialogResult.No) return; + try + { + if (GetLastSite() != -1 && Commons.GetVehicleStatus((Car)this)!= VehicleStatus.NeedInit) + { + Diagnosis.Post($"Car{id}鍒濆鍖栧け璐ワ細宸插垵濮嬪寲"); + return; + } + var debug = "ui invoke reset. "; + + // todo: 鎵剧偣鍜岀嚎娈电殑鏈杩戠偣銆傘傘 + // lock + UISite site1 = null; + float dist = float.MaxValue; + List<(float, Site, Site)> trackList = new List<(float, Site, Site)>(); + if (resetId == 0) + { + + (float bias, Vector2 hPnt, float fd) Project2DLine( + Vector2 pnt, + Vector2 segSt, + Vector2 segEnd + ) + { + var dir = Vector2.Normalize(segEnd - segSt); + var fd = Vector2.Dot(pnt - segSt, dir); + var hPnt = segSt + fd * dir; + var bias = dir.X * (pnt.Y - segSt.Y) - (pnt.X - segSt.X) * dir.Y; + return (bias, hPnt, fd); + } + + foreach (var allTrack in SimpleLib.GetAllTracks()) + { + var s = SimpleLib.GetSite(allTrack.siteA); + var e = SimpleLib.GetSite(allTrack.siteB); + var (b, h, fd) = Project2DLine( + new Vector2(x, y), + new Vector2(s.x, s.y), + new Vector2(e.x, e.y) + ); + b = Math.Abs(b); + if (fd < 0 || fd > 1) + continue; + if (dist > b) + { + UISite ss; + if (fd < 0.5) + ss = (UISite)s; + else + ss = (UISite)e; + + foreach (var ccar in SimpleLib.GetAllCars()) + { + if (ccar == this) + continue; + if (ccar.status.holdingLocks.Contains(s.id)) + goto next; + } + + dist = b; + site1 = ss; + next: + ; + } + } + float radius = 5000; + //鏍规嵁灏忚溅涓哄渾蹇冩壘鍒板崐寰剅adius鍐呯殑绔欑偣 + var surroundSites = SimpleLib.GetAllSites().ToList().FindAll(p => LessMath.dist(x, y, p.x, p.y) <= radius); + foreach (var allTrack in SimpleLib.GetAllTracks()) + { + var s = SimpleLib.GetSite(allTrack.siteA); + var e = SimpleLib.GetSite(allTrack.siteB); + if (!surroundSites.Contains(s) && !surroundSites.Contains(e) && surroundSites.Count != 0) continue; + + var (b, h, fd) = Project2DLine(new Vector2(x, y), new Vector2(s.x, s.y), new Vector2(e.x, e.y)); + b = Math.Abs(b); + double pathAngle = Double.NaN; + if (allTrack.direction == 1) + pathAngle = Math.Atan2((e.y - s.y), (e.x - s.x)) / Math.PI * 180; + else if (allTrack.direction == 2) + pathAngle = Math.Atan2((e.y - s.y), (e.x - s.x)) / Math.PI * 180 + 180; + else + trackList.Add((b, s, e)); + if (Math.Abs(this.th - pathAngle) < 30) + trackList.Add((b, s, e)); + foreach (var ccar in SimpleLib.GetAllCars()) + { + if (ccar == this) continue; + if (ccar.status.holdingLocks.Contains(s.id)) goto next; + } + next:; + + } + //var sites = trackList.OrderBy(p => p.Item1).ToArray(); + List<(double, Site)> distList = new List<(double, Site)>(); + foreach (var site in trackList) + { + if (site.Item2.fields.ContainsKey("no_reset") || site.Item3.fields.ContainsKey("no_reset")) + continue; + foreach (var ccar in SimpleLib.GetAllCars()) + { + if (ccar == this) continue; + if (ccar.status.holdingLocks.Contains(site.Item2.id) || ccar.status.holdingLocks.Contains(site.Item3.id)) goto next; + } + distList.Add((LessMath.dist(site.Item2.x, site.Item2.y, x, y), site.Item2)); + distList.Add((LessMath.dist(site.Item3.x, site.Item3.y, x, y), site.Item3)); + next:; + } + + site1 = (UISite)distList.OrderBy(p => p.Item1).First().Item2; + + //foreach (var site in SimpleLib.GetAllSites()) + //{ + // if (site.fields.ContainsKey("no_reset")) + // continue; + // foreach (var ccar in SimpleLib.GetAllCars()) + // { + // if (ccar == this) + // continue; + // if (ccar.status.holdingLocks.Contains(site.id)) + // goto next; + // } + + // var d = LessMath.dist(site.x, site.y, x, y); + // if (d < dist) + // { + // dist = (float)d; + // site1 = (UISite)site; + // } + // next: + // ; + //} + TrafficReset(site1, true, strict: false); + } + else + { + TrafficReset(SimpleLib.GetSite(resetId), true, strict: false); + } + + + siteID = site1.id; + + lstatus = "涓婄嚎"; + debug += $"site={site1.id}"; + AppendDebug(debug); + G.pushStatus($"Car {name}({id}) reset to Site {site1.id}"); + } + catch (Exception e) + { + Diagnosis.Post($"{this.name}---鍒濆鍖栧け璐--{e.Message}", $"{this.name}", true); + } + } + + HttpClient hc = new HttpClient() { Timeout = TimeSpan.FromMilliseconds(300) }; + // [MethodMember(name = "灏忚溅鏆傚仠", desc = "灏忚溅鏆傚仠")] + public void EmergencyStop(string reason) + { + Diagnosis.Post($"car{name}:EmergencyStop"); + hc.GetStringAsync($"http://{address}:8008/setValue?FieldName=ISRelease&Value=1"); + } + + //[MethodMember(name = "灏忚溅鏆傚仠鎭㈠", desc = "灏忚溅鏆傚仠鎭㈠")] + public void EmergencyRelease() + { + Diagnosis.Post($"car{name}:EmergencyStop release"); + hc.GetStringAsync($"http://{address}:8008/setValue?FieldName=ISRelease&Value=0"); + } + //[MethodMember(name = "灏忚溅閲嶅惎C", desc = "灏忚溅閲嶅惎Clumsy")] + public void ResetClumsy() + { + Diagnosis.Post($"car{name}-{address}:ResetClumsy"); + hc.GetStringAsync($"http://{address}:8008/reset"); + } + } + + /// + /// 涓撻棬鐢ㄤ簬鎺у埗turn鐨勯棶棰 + /// + public class KivaCarTrackCoder : ITrackCoder + { + public bool toBlock() + { + return false; + } + /// + /// 鐢ㄤ簬璁板綍涓婁竴璺緞鐨勮搴 + /// + public static double lastTh = -1; + + public bool Code(SegmentPlan plan, Track track, Site src, Site dst, int i) + { + var trackInfo = StringDictConvert.Convert(track.fields); + var srcInfo = StringDictConvert.Convert(src.fields); + Console.WriteLine($@"srcInfo.Turn:{srcInfo.Turn},i={1}"); + if (!srcInfo.Turn) //涓嶅寘鍚棆杞寚浠わ紝鐩存帴璺宠繃 + { + return true; + } + if (i == 1) + { + return true; + } + var lastSite = plan.segments[i - 3] as Site; + if (lastSite == null) + { + return true; + } + var lastPathDir = Math.Round(Math.Atan2(src.y - lastSite.y, src.x - lastSite.x) / Math.PI * 2) * 90; + var curPathDir = Math.Round(Math.Atan2(dst.y - src.y, dst.x - src.x) / Math.PI * 2) * 90; + if (Math.Abs(curPathDir - lastPathDir) < 45) + { + return true; + } + var angleTarget = trackInfo.ReverseDst == dst.id || trackInfo.Reverse ? curPathDir + 180 : curPathDir; + + plan.codeArr[i] += $"agv.Wait(); agv.SyncRotate({angleTarget}); agv.Wait(); "; + + return true; + } + } + +} diff --git a/StandardScene.Magnetic/CarTypes/MultiWheelLifterCar.cs b/StandardScene.Magnetic/CarTypes/MultiWheelLifterCar.cs new file mode 100644 index 0000000..013ebf3 --- /dev/null +++ b/StandardScene.Magnetic/CarTypes/MultiWheelLifterCar.cs @@ -0,0 +1,396 @@ +锘縰sing CommonUsage.Mathematics; +using LessokajiWeaverUtilities.Utilities; +using Nancy.Helpers; +using Newtonsoft.Json; +using SimpleLite; +using SimpleLite.RCS; +using SimpleLite.RCS.CarTypes; +using SimpleLite.CADTools; +using SimpleLite.Props; +using SimpleLite.UI; +using SimpleCore; +using SimpleCore.Compiler; +using SimpleCore.Library; +using SimpleCore.PropType; +using StandardScene.Model; +using StandardScene.Coders; +using StandardScene.Magnetic.Coders; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Drawing; +using System.Linq; +using System.Net.Http; +using System.Numerics; +using System.Runtime; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using System.Windows.Forms; +using Track = SimpleCore.PropType.Track; + +namespace StandardScene.CarTypes +{ + // 纾佸惊杩瑰櫒宸蹭笌 Kiva 鐗堝幓閲嶅悎骞朵负鍞竴 StandardScene.Magnetic.Coders.MagneticTrackCoder銆 + // 鍘 MWL 鐗 NaiveMagGo 缂哄皯 ${track.Speed}锛屽悎骞跺悗缁熶竴涓哄惈 Speed 鐨勭増鏈紙鐢ㄦ埛纭锛夈 + // MultiWheelLifter 绯诲瓧娈佃涓嬫矇鑷冲熀搴 StandardScene.Core\CarTypes\MultiWheelLifterFields.cs + // 锛圡ultiVehicleCar 绛夎法骞冲彴杞﹀瀷寮曠敤瀹冧滑锛夈 + + [TemplateTrackCoderSettings( + priority = 30, + useVerb = "track.SleepTime!=0", + templateString = "agv.Wait();agv.Sleep(${track.SleepTime});agv.Wait();", + blockVerb = "false", + siteFields = typeof(MultiWheelLifterSiteFields), + trackFields = typeof(MultiWheelLifterTrackFields), + planFields = typeof(MultiWheelLifterPlanFields))] + + [TemplateTrackCoderSettings( + priority = 30, + useVerb = "track.TrayTarget!=0", + templateString = "agv.Wait();agv.TrayControl(${track.TrayTarget});agv.Wait();", + blockVerb = "false", + siteFields = typeof(MultiWheelLifterSiteFields), + trackFields = typeof(MultiWheelLifterTrackFields), + planFields = typeof(MultiWheelLifterPlanFields))] + + //璋冩暣鏂瑰悜+閽昏溅 + [TemplateTrackCoderSettings( + priority = 19, + useVerb = "dst.name=='FetchSite'&&plan.action=='fetch'", + templateString = "agv.Wait();agv.RotateToTarget(${plan.AngleTarget});agv.Wait();agv.TireFollowing(${plan.TireNum},${plan.FrontLidarDetect},${plan.FirstTire});agv.Wait();", + blockVerb = "false", + siteFields = typeof(MultiWheelLifterSiteFields), + trackFields = typeof(MultiWheelLifterTrackFields), + planFields = typeof(MultiWheelLifterPlanFields))] + //[TemplateTrackCoderSettings( + // priority = 19, + // useVerb = "dst.FetchPlace && plan.action=='fetch'", + // templateString = "agv.Wait();agv.RotateToTarget(${plan.AngleTarget});agv.Wait();", + // blockVerb = "false", + // siteFields = typeof(MultiWheelLifterSiteFields), + // trackFields = typeof(MultiWheelLifterTrackFields), + // planFields = typeof(MultiWheelLifterPlanFields))] + + //涓嬩娇鑳+澶规姳+涓婁娇鑳 + [TemplateTrackCoderSettings( + priority = 18, + useVerb = "dst.name=='FetchSite'&&plan.action=='fetch'", + templateString = "agv.DriverDisable();agv.Wait();agv.ClamptoTarget(${dst.ClampClose});agv.Wait();agv.DriverAble();agv.Wait();", + blockVerb = "true", + siteFields = typeof(MultiWheelLifterSiteFields), + trackFields = typeof(MultiWheelLifterTrackFields), + planFields = typeof(MultiWheelLifterPlanFields))] + + [TemplateTrackCoderSettings( + priority = 19, + useVerb = "dst.tag>0 && src.tag>0", + templateString = "agv.QrGo(${src.x},${src.y},${src.id},${src.tag},${dst.x},${dst.y},${dst.id},${dst.tag},${track.id}," + + "${track.Speed},${track.Reverse || (track.ReverseDst == dst.id)}," + + "${track.CarDirectionBias},${track.EnableCarAbsoluteDirection},${track.CarAbsoluteDirection}," + + "${track.typeInfo});", + blockVerb = "true", + siteFields = typeof(MultiWheelLifterSiteFields), + trackFields = typeof(MultiWheelLifterTrackFields), + planFields = typeof(MultiWheelLifterPlanFields))] + + // 閫氱敤閬块殰/IO coder 宸叉娊绂讳负 StandardScene.Coders.*锛堣绫诲墠 [ProgramTrackCoderSettings] 寮曠敤锛 + + // 閬块殰鍖哄昂瀵(4鍙,鍚腑蹇冪偣)鍒囨崲 coder 宸叉娊绂讳负 StandardScene.Coders.AvoidanceParamCoder锛堣绫诲墠 [ProgramTrackCoderSettings] 寮曠敤锛 + + // 绾犲亸闃堝 coder 宸叉娊绂讳负 StandardScene.Coders.TrackingErrThreshCoder + + [ProgramTrackCoderSettings(priority = 19, program = typeof(MagneticTrackCoder))] + [ProgramTrackCoderSettings(priority = 27, program = typeof(LidarAreaSwitchCoder))] + [ProgramTrackCoderSettings(priority = 20, program = typeof(AvoidanceDistanceCoder))] + [ProgramTrackCoderSettings(priority = 20, program = typeof(IoAreaSwitchCoder))] + [ProgramTrackCoderSettings(priority = 20, program = typeof(TrackingErrThreshCoder))] + [ProgramTrackCoderSettings(priority = 20, program = typeof(AvoidanceParamCoder))] + + [EnvelopConfig(lengthX = 3000, lengthY = 1300, centerX = 0, centerY = 0)] + + [CarType(Name = "澶氳埖杞《鍗囪溅")] + [I18N.DocumentTranslation(Name = "MultiWheel Lifter Car", locale = "en")] + public class MultiWheelLifterCar : GhostCar + { + [FieldMember]public float CarLength = 2000; + [FieldMember] public float CarWidth = 1400; + public static async Task Create() + { + var car = new MultiWheelLifterCar() + { + lstatus = "杩炴帴涓", + address = "127.0.0.1", + name = "澶氳埖杞《鍗囪溅" + }; + return car; + } + + [MethodMember(Name = "杩涘叆灏忚溅杩滅▼", Description = "杩涘叆灏忚溅杩滅▼妗岄潰")] + [I18N.DocumentTranslation(Name = "Open remote desktop",Description = "Open the car's remote desktop", locale = "en")] + public void Mstsc() + { + var ip = this.address; + // 鍚姩mstsc骞朵紶閫扞P鍦板潃 + Process.Start( + new ProcessStartInfo + { + FileName = "mstsc", + Arguments = $"/v:{ip}", + UseShellExecute = false, + CreateNoWindow = true + } + ); + } + + public void newTrafficReset(int resetId = 0) + { + //if (!status.programs.task.IsCompleted && + // MessageBox.Show($"灏忚溅{name}({id})褰撳墠鏈変换鍔★細{status.programs.printStatus()}锛岀‘璁ゅ垵濮嬪寲锛", "纭", + // MessageBoxButtons.YesNo) == DialogResult.No) return; + try + { + if (GetLastSite() != -1 && Commons.GetVehicleStatus((Car)this) != VehicleStatus.NeedInit) + { + Diagnosis.Post($"Car{id}鍒濆鍖栧け璐ワ細宸插垵濮嬪寲"); + return; + } + var debug = "ui invoke reset. "; + + // todo: 鎵剧偣鍜岀嚎娈电殑鏈杩戠偣銆傘傘 + // lock + UISite site1 = null; + float dist = float.MaxValue; + List<(float, Site, Site)> trackList = new List<(float, Site, Site)>(); + if (resetId == 0) + { + + (float bias, Vector2 hPnt, float fd) Project2DLine( + Vector2 pnt, + Vector2 segSt, + Vector2 segEnd + ) + { + var dir = Vector2.Normalize(segEnd - segSt); + var fd = Vector2.Dot(pnt - segSt, dir); + var hPnt = segSt + fd * dir; + var bias = dir.X * (pnt.Y - segSt.Y) - (pnt.X - segSt.X) * dir.Y; + return (bias, hPnt, fd); + } + + foreach (var allTrack in SimpleLib.GetAllTracks()) + { + var s = SimpleLib.GetSite(allTrack.siteA); + var e = SimpleLib.GetSite(allTrack.siteB); + var (b, h, fd) = Project2DLine( + new Vector2(x, y), + new Vector2(s.x, s.y), + new Vector2(e.x, e.y) + ); + b = Math.Abs(b); + if (fd < 0 || fd > 1) + continue; + if (dist > b) + { + UISite ss; + if (fd < 0.5) + ss = (UISite)s; + else + ss = (UISite)e; + + foreach (var ccar in SimpleLib.GetAllCars()) + { + if (ccar == this) + continue; + if (ccar.status.holdingLocks.Contains(s.id)) + goto next; + } + + dist = b; + site1 = ss; + next: + ; + } + } + float radius = 5000; + //鏍规嵁灏忚溅涓哄渾蹇冩壘鍒板崐寰剅adius鍐呯殑绔欑偣 + var surroundSites = SimpleLib.GetAllSites().ToList().FindAll(p => LessMath.dist(x, y, p.x, p.y) <= radius); + foreach (var allTrack in SimpleLib.GetAllTracks()) + { + var s = SimpleLib.GetSite(allTrack.siteA); + var e = SimpleLib.GetSite(allTrack.siteB); + if (!surroundSites.Contains(s) && !surroundSites.Contains(e) && surroundSites.Count != 0) continue; + + var (b, h, fd) = Project2DLine(new Vector2(x, y), new Vector2(s.x, s.y), new Vector2(e.x, e.y)); + b = Math.Abs(b); + double pathAngle = Double.NaN; + if (allTrack.direction == 1) + pathAngle = Math.Atan2((e.y - s.y), (e.x - s.x)) / Math.PI * 180; + else if (allTrack.direction == 2) + pathAngle = Math.Atan2((e.y - s.y), (e.x - s.x)) / Math.PI * 180 + 180; + else + trackList.Add((b, s, e)); + if (Math.Abs(this.th - pathAngle) < 30) + trackList.Add((b, s, e)); + foreach (var ccar in SimpleLib.GetAllCars()) + { + if (ccar == this) continue; + if (ccar.status.holdingLocks.Contains(s.id)) goto next; + } + next:; + + } + //var sites = trackList.OrderBy(p => p.Item1).ToArray(); + List<(double, Site)> distList = new List<(double, Site)>(); + foreach (var site in trackList) + { + if (site.Item2.fields.ContainsKey("no_reset") || site.Item3.fields.ContainsKey("no_reset")) + continue; + foreach (var ccar in SimpleLib.GetAllCars()) + { + if (ccar == this) continue; + if (ccar.status.holdingLocks.Contains(site.Item2.id) || ccar.status.holdingLocks.Contains(site.Item3.id)) goto next; + } + distList.Add((LessMath.dist(site.Item2.x, site.Item2.y, x, y), site.Item2)); + distList.Add((LessMath.dist(site.Item3.x, site.Item3.y, x, y), site.Item3)); + next:; + } + + site1 = (UISite)distList.OrderBy(p => p.Item1).First().Item2; + + //foreach (var site in SimpleLib.GetAllSites()) + //{ + // if (site.fields.ContainsKey("no_reset")) + // continue; + // foreach (var ccar in SimpleLib.GetAllCars()) + // { + // if (ccar == this) + // continue; + // if (ccar.status.holdingLocks.Contains(site.id)) + // goto next; + // } + + // var d = LessMath.dist(site.x, site.y, x, y); + // if (d < dist) + // { + // dist = (float)d; + // site1 = (UISite)site; + // } + // next: + // ; + //} + TrafficReset(site1, true, strict: false); + } + else + { + TrafficReset(SimpleLib.GetSite(resetId), true, strict: false); + } + + + siteID = site1.id; + + lstatus = "涓婄嚎"; + debug += $"site={site1.id}"; + AppendDebug(debug); + G.pushStatus($"Car {name}({id}) reset to Site {site1.id}"); + } + catch (Exception e) + { + Diagnosis.Post($"{this.name}---鍒濆鍖栧け璐--{e.Message}", $"{this.name}", true); + } + } + + + + protected override void draw(Graphics eGraphics) + { + var HalfCarLength = CarLength / 2; + var HalfCarWidth = CarWidth / 2; + eGraphics.FillRectangle(Brushes.Gray, -HalfCarLength, -HalfCarWidth, HalfCarLength * 2, HalfCarWidth * 2); + eGraphics.DrawRectangle(Pens.White, -HalfCarLength, -HalfCarWidth, HalfCarLength * 2, HalfCarWidth * 2); + eGraphics.DrawLine(Pens.White, HalfCarLength - HalfCarWidth, -HalfCarWidth, HalfCarLength, 0); + eGraphics.DrawLine(Pens.White, HalfCarLength - HalfCarWidth, HalfCarWidth, HalfCarLength, 0); + } + + public override string SetDisplayInfo() + { + try + { + var str = $"{name}({id})\n"; + status.enums.TryGetValue("ChassisMode", out var manual); + var ms = manual == "0" ? "鑷姩" : "鎵嬪姩"; + status.enums.TryGetValue("Soc", out var soc); + str = $"{str}{ms}|soc:{soc}"; + var alarmStr = Commons.GetCarStatus(this, "AlarmInfo"); + if (alarmStr != "") + str = $"{str}|{alarmStr}"; + if (status.enums.TryGetValue("ElectricCurrent", out var electricCurrent) && + !string.IsNullOrEmpty(electricCurrent)) + { + var charging = float.Parse(electricCurrent) * 0.01f > 0 ? "鍏呯數涓" : "鏈厖鐢"; + str = $"{str}|charge:{charging}"; + } + + var thresSpeed = Commons.CarValue(this, "ThresSpeed"); + str = $"{str}{((thresSpeed <= 0) ? "|閬块殰瑙﹀彂" : "")}"; + return str; + } + catch (Exception e) + { + Console.WriteLine(e); + return "bad car"; + } + } + + private bool _running = false; + + [MethodMember(Name = "娴嬭瘯", Description = "寰幆")] + [I18N.DocumentTranslation(Name = "Test", Description = "Looping", locale = "en")] + public async void Test() + { + var siteA = SimpleLib.GetAllSites().First(s => s.name == "A"); + var siteB = SimpleLib.GetAllSites().First(s => s.name == "B"); + _running = true; + while (_running) + { + var plan = new SegmentPlan() { usingCar = this }; + plan.FindRoute(SimpleLib.GetSite(this.GetLastSite()), siteA); + await plan.Compile("GoA").Queue(); + var planB = new SegmentPlan() { usingCar = this }; + planB.FindRoute(SimpleLib.GetSite(this.GetLastSite()), siteB); + await planB.Compile("GoB").Queue(); + await Task.Delay(1000); + } + } + + [MethodMember(Name = "鍋滄娴嬭瘯")] + [I18N.DocumentTranslation(Name = "Stop Test", Description = "Stop Looping", locale = "en")] + public void Stop() + { + _running = false; + } + HttpClient hc = new HttpClient() { Timeout = TimeSpan.FromMilliseconds(300) }; + [MethodMember(Name = "灏忚溅鏆傚仠", Description = "灏忚溅鏆傚仠")] + [I18N.DocumentTranslation(Name = "Emergency Stop",Description = "Emergency Stop the car", locale = "en")] + public void EmergencyStop(string reason) + { + Diagnosis.Post($"car{name}:EmergencyStop"); + hc.GetStringAsync($"http://{address}:8008/setValue?FieldName=ISRelease&Value=1"); + } + + [MethodMember(Name = "灏忚溅鏆傚仠鎭㈠", Description = "灏忚溅鏆傚仠鎭㈠")] + [I18N.DocumentTranslation(Name = "Emergency Release",Description = "Release Emergency Stop of the car", locale = "en")] + public void EmergencyRelease() + { + Diagnosis.Post($"car{name}:EmergencyStop release"); + hc.GetStringAsync($"http://{address}:8008/setValue?FieldName=ISRelease&Value=0"); + } + [MethodMember(Name = "灏忚溅閲嶅惎C", Description = "灏忚溅閲嶅惎Clumsy")] + [I18N.DocumentTranslation(Name = "Reset Clumsy",Description = "Reset Clumsy of the car", locale = "en")] + public void ResetClumsy() + { + Diagnosis.Post($"car{name}-{address}:ResetClumsy"); + hc.GetStringAsync($"http://{address}:8008/reset"); + } + } +} diff --git a/StandardScene.Magnetic/Coders/MagneticTrackCoder.cs b/StandardScene.Magnetic/Coders/MagneticTrackCoder.cs new file mode 100644 index 0000000..273980e --- /dev/null +++ b/StandardScene.Magnetic/Coders/MagneticTrackCoder.cs @@ -0,0 +1,168 @@ +using CommonUsage.Mathematics; +using LessokajiWeaverUtilities.Utilities; +using SimpleCore; +using SimpleCore.Compiler; +using SimpleCore.Library; +using SimpleCore.PropType; +using SimpleLite.Props; +using StandardScene.CarTypes; +using System; +using System.Linq; +using System.Numerics; +using Track = SimpleCore.PropType.Track; + +namespace StandardScene.Magnetic.Coders +{ + /// + /// 鍞竴纾佸惊杩瑰櫒锛堜細璇1锛欿iva 鐗堜笌 MWL 鐗堝幓閲嶏紱NaiveMagGo 閲囩敤鍚 ${track.Speed} 鐗堟湰锛岀敤鎴风‘璁わ級銆 + /// C2 鐗╃悊杩佸嚭锛氬師鏆傜疆 Kiva.cs锛圫tandardScene.CarTypes锛夛紝鐜板綊 scene.mag 骞冲彴鎻掍欢銆 + /// 瑙﹀彂鏉′欢锛歵rack.Magnet=true 涓旈潪鍊掕溅娈碉紱鍒嗗弶/姹囨祦璺彛鎸夐『鏃堕拡搴忚绠 magSelect銆 + /// + public class MagneticTrackCoder : ITrackCoder + { + public bool Code(SegmentPlan plan, Track track, Site src, Site dst, int i) + { + Diagnosis.Post("enter code", "MagTrackCoder", true); + if (!track.fields.TryGetValue("Magnet", out var mStr) || !bool.TryParse(mStr, out var mBool) || + !mBool) return false; + + // do not use magnet navigation for reversing + if (track.fields.TryGetValue("ReverseDst", out var reverseStr) && int.TryParse(reverseStr, out var rId) && + rId == dst.id) return false; + + Diagnosis.Post($">>>> checking {track.id}", "MagTrackCoder", true); + var magSelect = -1; + // choose forking/converging path in clockwise order. + // i.e. 0 is the left-most forking. -1 means no choosing + + float CalculateTangent(Track myTrack, bool checkSrc, int segId) + { + var anchorSite = (Site)(checkSrc ? plan.segments[segId - 1] : plan.segments[segId + 1]); + + if (myTrack is UICircularArcTrack arcTrack) + { + var angleStart = arcTrack.Arc.AngleStart; + var direction = 1; + if (Vector2.Distance(anchorSite.v2, arcTrack.Arc.PointEnd) < + Vector2.Distance(anchorSite.v2, arcTrack.Arc.PointStart)) + { + angleStart = arcTrack.Arc.AngleEnd; + direction = -1; + } + + angleStart = CommonMath.RoundTh(angleStart); + var myAngle = angleStart + direction * 5; + var arcTh = CommonMath.RoundTh(myAngle + 90 * direction); + Console.WriteLine($">>>> {checkSrc} id: {myTrack.id} th: {arcTh:0.0}"); + return arcTh; + } + + if (myTrack is UIBezierTrack bTrack) + { + var add180 = 0f; + var controlPoints = bTrack.BezierCurve.ControlPoints; + if (Vector2.Distance(anchorSite.v2, controlPoints[0]) > + Vector2.Distance(anchorSite.v2, controlPoints.Last())) add180 = 180; + var TangentPoint = bTrack.BezierCurve.QueryTangentPoint(anchorSite.v2); + Console.WriteLine($">>>>Bezier {checkSrc} id: {myTrack.id} th: {TangentPoint.Angle:0.0} add:{add180} queryid:{anchorSite.id}"); + return TangentPoint.Angle + add180; + } + + if (myTrack is UINurbsTrack nTrack) + { + + } + + // line track + var myDst = SimpleLib.GetSite(myTrack.GetOther(anchorSite.id)); + var lineTh = (float)(Math.Atan2(myDst.y - anchorSite.y, myDst.x - anchorSite.x) / Math.PI * 180); + Console.WriteLine($">>>> {checkSrc} id: {myTrack.id} th: {lineTh:0.0}"); + return lineTh; + } + + bool TestMagSelect(bool checkSrc) + { + var anchorSite = checkSrc ? src : dst; + var myTh = CalculateTangent(track, checkSrc, i); + var allTracks = anchorSite.relatedTracks.Select(ii => SimpleLib.GetTrack(ii)).ToList(); + if (allTracks.Count <= 2) return false; + + var tracksAndThs = allTracks.Select(myTrack => (myTrack, CalculateTangent(myTrack, checkSrc, i))).ToList(); + var sameHalfTracks = tracksAndThs.Where(item => Math.Abs(LessMath.thDiff(item.Item2, myTh)) < 90) + .ToList(); + if (sameHalfTracks.Count > 1) + { + sameHalfTracks = checkSrc + ? sameHalfTracks.OrderByDescending(item => LessMath.thDiff(item.Item2, myTh)).ToList() + : sameHalfTracks.OrderBy(item => LessMath.thDiff(item.Item2, myTh)).ToList(); + Console.WriteLine( + $">>>> {checkSrc} {track.id} ({string.Join(", ", sameHalfTracks.Select(tt => tt.myTrack.id))})"); + magSelect = sameHalfTracks.FindIndex(tt => tt.myTrack.id == track.id); + return true; + } + + // check if adjacent to forking/converging path + Track adjacentTrack; + int adjacentSegId; + if (checkSrc) + { + if (i <= 1) return false; + adjacentTrack = (Track)plan.segments[i - 2]; + adjacentSegId = i - 2; + } + else + { + if (i >= plan.segments.Count - 2) return false; + adjacentTrack = (Track)plan.segments[i + 2]; + adjacentSegId = i + 2; + } + var otherHalfTracks = tracksAndThs.Where(item => Math.Abs(LessMath.thDiff(item.Item2, myTh)) >= 90) + .ToList(); + var adjacentTh = CalculateTangent(adjacentTrack, !checkSrc, adjacentSegId); + otherHalfTracks = checkSrc + ? otherHalfTracks.OrderBy(item => LessMath.thDiff(item.Item2, adjacentTh)).ToList() + : otherHalfTracks.OrderByDescending(item => LessMath.thDiff(item.Item2, adjacentTh)).ToList(); + Console.WriteLine($">>>> use adjacentTrack {checkSrc} {adjacentTrack.id} ({string.Join(", ", otherHalfTracks.Select(tt => tt.myTrack.id))})"); + magSelect = otherHalfTracks.FindIndex(tt => tt.myTrack.id == adjacentTrack.id); + return true; + } + + if (!TestMagSelect(true)) TestMagSelect(false); + + Console.WriteLine($">>>> {track.id} select {magSelect}"); + var engine = ProgramCoderHelper.PrepareTrackEngine(plan, track, src, dst, i, + carFields: typeof(BasicCarFields), + siteFields: typeof(BasicSiteFields), + trackFields: typeof(BasicTrackFields), + planFields: typeof(BasicPlanFields)); + var tStr = "agv.MagGo(${src.x},${src.y},${src.id},${dst.x},${dst.y},${dst.id},${track.id}," + + $"{magSelect}," + + "${track.Speed},${track.Reverse || (track.ReverseDst == dst.id)}," + + "${track.CarDirectionBias},${track.EnableCarAbsoluteDirection},${track.CarAbsoluteDirection});"; + + if (track.fields.TryGetValue("NaiveMagnet", out var naiveStr) && bool.TryParse(naiveStr, out var naive) && + naive) + { + var slowDown = i == plan.segments.Count - 2; + if (slowDown && (!dst.fields.TryGetValue("TagValue", out var tagValStr) || + !int.TryParse(tagValStr, out _))) + throw new Exception($"Destination site {dst.id} must have valid TagValue field!"); + Console.WriteLine($">>>> Naive magnet!"); + tStr = "agv.NaiveMagGo(${src.id},${dst.id},${track.id}," + + $"{magSelect}," + "${track.Speed}," + + $"{slowDown.ToString().ToLower()}," + + "${dst.TagValue}," + + "${src.x},${src.y},${dst.x},${dst.y},${track.typeInfo});"; + } + + plan.codeArr[i] += (string)engine.ExecuteExpression($"`{tStr}`", default); + + return true; + } + + public bool toBlock() + { + return true; + } + } +} diff --git a/StandardScene.Magnetic/MagneticSceneProfile.cs b/StandardScene.Magnetic/MagneticSceneProfile.cs new file mode 100644 index 0000000..b642c9d --- /dev/null +++ b/StandardScene.Magnetic/MagneticSceneProfile.cs @@ -0,0 +1,31 @@ +using System; +using System.Collections.Generic; +using SimpleCore.Navigation; +using StandardScene.CarTypes; + +namespace StandardScene.Magnetic +{ + /// + /// scene.mag 骞冲彴鐢诲儚锛氱瀵艰埅鍦烘櫙鎻掍欢锛堢鏉″惊杩逛负涓伙紝鍏煎浜岀淮鐮佸湴鏍囨锛夈 + /// 瀹夸富锛圫impleLite锛夊姞杞芥湰 dll 鍚庡弽灏勫疄渚嬪寲骞 OnActivate / 娉ㄥ唽銆 + /// + public sealed class MagneticSceneProfile : NavigationProfileBase + { + public override NavKind Kind => NavKind.Magnetic; + + public override string SceneId => "scene.mag"; + + public override string DisplayName => "纾佸鑸钩鍙"; + + public override IReadOnlyList CarTypes => new[] + { + typeof(Kiva), + typeof(MultiWheelLifterCar), + }; + + public override void OnActivate(ISceneContext context) + { + context.Log($"{DisplayName} 宸叉縺娲伙紙杞﹀瀷锛欿iva / 澶氳埖杞《鍗囪溅锛"); + } + } +} diff --git a/StandardScene.Magnetic/StandardScene.Magnetic.csproj b/StandardScene.Magnetic/StandardScene.Magnetic.csproj new file mode 100644 index 0000000..c4726ee --- /dev/null +++ b/StandardScene.Magnetic/StandardScene.Magnetic.csproj @@ -0,0 +1,54 @@ + + + + net8.0-windows + Library + true + StandardScene.Magnetic + StandardScene.Magnetic + latest + true + AnyCPU;x64 + x64 + true + false + disable + disable + false + $(NoWarn);NU1701;CS0618;CS0612;MSB3277;CA1416 + {HintPathFromItem};{TargetFrameworkDirectory};{RawFileName};{GAC} + + + + + + + + + + + + + + + E:\Work\Core\Simple-FR\Simple\SimpleLite\bin\Debug\SimpleLite.dll + + + E:\Work\Core\Simple-FR\Simple\SimpleCore\bin\Debug\netstandard2.0\SimpleCore.dll + + + D:\MDCS\Dependencies\Commons\CommonUsage.dll + + + E:\Work\Core\Simple-FR\Simple\tools\Topaz.dll + + + E:\Work\Core\Simple-FR\Simple\SimpleLite\bin\Debug\LessokajiWeaverUtilities.dll + + + + + + + + diff --git a/StandardScene.Magnetic/StandardScene.Magnetic.scene.json b/StandardScene.Magnetic/StandardScene.Magnetic.scene.json new file mode 100644 index 0000000..5161cd9 --- /dev/null +++ b/StandardScene.Magnetic/StandardScene.Magnetic.scene.json @@ -0,0 +1,12 @@ +{ + "id": "scene.mag", + "displayName": "纾佸鑸钩鍙", + "navKind": "magnetic", + "assembly": "StandardScene.Magnetic.dll", + "coreVersion": ">=1.0.0", + "requiresCore": "StandardScene.dll", + "provides": { + "carTypes": [ "Kiva", "MultiWheelLifterCar" ], + "missionTypes": [] + } +} diff --git a/StandardScene.Protocol.VDA5050/StandardScene.Protocol.VDA5050.csproj b/StandardScene.Protocol.VDA5050/StandardScene.Protocol.VDA5050.csproj new file mode 100644 index 0000000..d73788d --- /dev/null +++ b/StandardScene.Protocol.VDA5050/StandardScene.Protocol.VDA5050.csproj @@ -0,0 +1,60 @@ + + + + net8.0-windows + Library + true + StandardScene + StandardScene.Protocol.VDA5050 + latest + true + AnyCPU;x64 + x64 + true + false + disable + disable + false + $(NoWarn);NU1701;CS0618;CS0612;MSB3277;CA1416 + {HintPathFromItem};{TargetFrameworkDirectory};{RawFileName};{GAC} + + + + + + + + + + + + + + + + E:\Work\Core\Simple-FR\Simple\SimpleLite\bin\Debug\SimpleLite.dll + + + E:\Work\Core\Simple-FR\Simple\SimpleCore\bin\Debug\netstandard2.0\SimpleCore.dll + + + D:\MDCS\Dependencies\Commons\CommonUsage.dll + + + E:\Work\Core\Simple-FR\Simple\tools\Topaz.dll + + + E:\Work\Core\Simple-FR\Simple\SimpleLite\bin\Debug\LessokajiWeaverUtilities.dll + + + + + + + + + + + + + diff --git a/StandardScene.Protocol.VDA5050/StandardScene.Protocol.VDA5050.scene.json b/StandardScene.Protocol.VDA5050/StandardScene.Protocol.VDA5050.scene.json new file mode 100644 index 0000000..c57f0b6 --- /dev/null +++ b/StandardScene.Protocol.VDA5050/StandardScene.Protocol.VDA5050.scene.json @@ -0,0 +1,10 @@ +{ + "id": "scene.vda5050", + "displayName": "VDA5050 鍗忚杞﹀瀷", + "assembly": "StandardScene.Protocol.VDA5050.dll", + "coreVersion": ">=1.0.0", + "requiresCore": "StandardScene.dll", + "provides": { + "carTypes": [ "VDA5050Car" ] + } +} diff --git a/StandardScene.Protocol.VDA5050/VDACar/MasterMQTTCommunication.cs b/StandardScene.Protocol.VDA5050/VDACar/MasterMQTTCommunication.cs new file mode 100644 index 0000000..10f952d --- /dev/null +++ b/StandardScene.Protocol.VDA5050/VDACar/MasterMQTTCommunication.cs @@ -0,0 +1,297 @@ +锘縰sing System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using CommonUsage.Protocols.VDA5050.Messages; +using MQTTnet; +using MQTTnet.Client; +using MQTTnet.Extensions.ManagedClient; +using MQTTnet.Protocol; +using MQTTnet.Server; +using Newtonsoft.Json; +using SimpleCore; +using SimpleCore.Library; + +namespace StandardScene.CarTypes +{ + public class MasterMQTTCommunication + { + private IManagedMqttClient _client; + private IManagedMqttClient _visualizationClient; // Separate client for visualization + + private const string OrderTopic = "vda5050/frldAGV/order"; + private const string StateTopic = "vda5050/frldAGV/state"; + + private const string VisualizationTopic = "vda5050/frldAGV/visualization"; + private readonly string _connectionTopic = "vda5050/frldAGV/connection"; + private readonly string _instantAction = "vda5050/frldAGV/instantActions"; + private readonly string _factsheet = "vda5050/frldAGV/factsheet"; + private readonly string _carFields = "vda5050/frldAGV/carFields"; + + + + //192.168.123.5 + public MasterMQTTCommunication() + { + // Initialize MQTT client and connect to broker + var mqttFactory = new MqttFactory(); + _client = mqttFactory.CreateManagedMqttClient(); + + var mqttClientOptions = new MqttClientOptionsBuilder() + .WithTcpServer("localhost", 1883) // Connect to the local broker + .WithCleanSession(true) + .WithCleanStart(true) + .Build(); + + var managedOptions = new ManagedMqttClientOptionsBuilder() + .WithClientOptions(mqttClientOptions) + .WithMaxPendingMessages(20) + .WithPendingMessagesOverflowStrategy(MqttPendingMessagesOverflowStrategy.DropOldestQueuedMessage) + .Build(); + + _client.StartAsync(managedOptions).GetAwaiter().GetResult(); + Console.WriteLine(" >>> Master-Control MQTT client connected to broker."); + + // Initialize separate client for visualization + _visualizationClient = mqttFactory.CreateManagedMqttClient(); + var visualizationOptions = new MqttClientOptionsBuilder() + .WithTcpServer("localhost", 1883) + .WithClientId("Master-Visualization") + .Build(); + + var visualizationManagedOptions = new ManagedMqttClientOptionsBuilder() + .WithClientOptions(visualizationOptions) + .WithMaxPendingMessages(10) // Lower queue size for visualization + .WithPendingMessagesOverflowStrategy(MqttPendingMessagesOverflowStrategy.DropOldestQueuedMessage) + .Build(); + + _visualizationClient.StartAsync(visualizationManagedOptions).GetAwaiter().GetResult(); + Console.WriteLine(" >>> Master-Control Visualization MQTT client initialized."); + + } + + public async Task RestartClient() + { + Console.WriteLine(" >>> Restarting MQTT client..."); + + var stopTask = _client.StopAsync(); + if (await Task.WhenAny(stopTask, Task.Delay(5000)) == stopTask) + { + Console.WriteLine(" >>> MQTT client stopped successfully."); + } + else + { + Console.WriteLine(" >>> Timeout while stopping MQTT client."); + } + + // Dispose and reinitialize the client + //_client.Dispose(); + + var mqttFactory = new MqttFactory(); + var newClient = mqttFactory.CreateManagedMqttClient(); + + var mqttClientOptions = new MqttClientOptionsBuilder() + .WithTcpServer("localhost", 1883) + .Build(); + + var managedOptions = new ManagedMqttClientOptionsBuilder() + .WithClientOptions(mqttClientOptions) + .Build(); + + await newClient.StartAsync(managedOptions); + Console.WriteLine(" >>> New MQTT client started."); + } + + + + public void SubsribeToConnectionTopic() + { + _client.SubscribeAsync(_connectionTopic, MqttQualityOfServiceLevel.AtLeastOnce).GetAwaiter().GetResult(); + + _client.ApplicationMessageReceivedAsync += async e => + { + if (e.ApplicationMessage.Topic == _connectionTopic) + { + Console.WriteLine($" >>>>>>> Subscribed to topic: {e.ApplicationMessage.Topic}"); + var payload = Encoding.UTF8.GetString(e.ApplicationMessage.Payload); + //LogMessage("RECEIVED", e.ApplicationMessage.Topic, payload); + var connectionStatusMessage = JsonConvert.DeserializeObject(payload); + var car = (VDA5050Car)SimpleLib.GetAllCars() + .FirstOrDefault(cc => cc is VDA5050Car vdaCar); + if (car == null) + { + Diagnosis.Post($"no car of serialNumber {connectionStatusMessage.serialNumber}"); + + } + + car.ConnectionStatus = connectionStatusMessage.connectionState; + Console.WriteLine($">>>> Connection status: {connectionStatusMessage.connectionState}"); + } + }; + } + + public void SubscribeToCarFields() + { + _client.SubscribeAsync(_carFields,MqttQualityOfServiceLevel.AtLeastOnce).GetAwaiter().GetResult(); + _client.ApplicationMessageReceivedAsync += async e => + { + if (e.ApplicationMessage.Topic == _carFields) + { + var payload = Encoding.UTF8.GetString(e.ApplicationMessage.Payload); + Console.WriteLine(payload); + //TODO:澶勭悊瀛楃涓诧紝鎷挎兂瑕佺殑鍊 + } + }; + } + + //public void SubsribeToFactSheetTopic() + //{ + // _client.SubscribeAsync(_factsheet, MqttQualityOfServiceLevel.AtMostOnce).GetAwaiter().GetResult(); + + // _client.ApplicationMessageReceivedAsync += async e => + // { + // if (e.ApplicationMessage.Topic == _factsheet) + // { + // //Console.WriteLine($" >>>>>>> Subscribed to topic: {e.ApplicationMessage.Topic}"); + // var payload = Encoding.UTF8.GetString(e.ApplicationMessage.Payload); + // //LogMessage("RECEIVED FactSheet", e.ApplicationMessage.Topic, payload); + // // Console.WriteLine($">>>> Connection status: {connectionStatusMessage.connectionState}"); + // } + // }; + //} + + public void SubscribeToVisualization() + { + _visualizationClient.SubscribeAsync(VisualizationTopic, MqttQualityOfServiceLevel.AtMostOnce).GetAwaiter().GetResult(); + Console.WriteLine($" >>> Subscribed to {VisualizationTopic}"); + + _visualizationClient.ApplicationMessageReceivedAsync += async e => + { + if(_visualizationClient.PendingApplicationMessagesCount > 5) + { + Console.WriteLine($"[WARNING] Dropping old visualization message. Pending: {_visualizationClient.PendingApplicationMessagesCount}"); + return; // Skip processing to catch up + } + if (e.ApplicationMessage.Topic == VisualizationTopic) + { + var payload = Encoding.UTF8.GetString(e.ApplicationMessage.Payload); + //Console.WriteLine($"[DEBUG] Received Visualization Message: {payload}"); + + var state = JsonConvert.DeserializeObject(payload); + var car = (VDA5050Car)SimpleLib.GetAllCars() + .FirstOrDefault(cc => cc is VDA5050Car vdaCar); + + if (car != null) + { + car.UpdatePosition(state); + } + } + await Task.CompletedTask; + }; + } + + public void SubscribeToState() + { + _client.SubscribeAsync(StateTopic, MqttQualityOfServiceLevel.AtMostOnce).GetAwaiter().GetResult(); + Console.WriteLine($"Subscribed to topic: {StateTopic}"); + _client.ApplicationMessageReceivedAsync += async e => + { + Console.WriteLine("e.ApplicationMessage.Topic: " + e.ApplicationMessage.Topic + " StateTopic: " + StateTopic); + if (e.ApplicationMessage.Topic == StateTopic) + { + Console.WriteLine($" >>>>>>> Subscribed to topic: {e.ApplicationMessage.Topic}"); + var payload = Encoding.UTF8.GetString(e.ApplicationMessage.Payload); + //LogMessage("RECEIVED", e.ApplicationMessage.Topic, payload); + // Console.WriteLine($" >> Topic: [{e.ApplicationMessage.Topic}] -- {payload}"); + var state = JsonConvert.DeserializeObject(payload); + var car = (VDA5050Car)SimpleLib.GetAllCars() + .FirstOrDefault(cc => cc is VDA5050Car vdaCar); + if (car == null) + { + Diagnosis.Post($"no car of serialNumber {state.serialNumber}"); + + } + car.IsPaused = state.paused; + car.UpdateState(state); + //car.UpdatePosition(state); + //Console.WriteLine($">> Received message on topic '{StateTopic}': {payload}"); + // onTopicReceived(state); + } + await Task.CompletedTask; + }; + } + + //public void SubscribeToState() + //{ + // _client.SubscribeAsync(StateTopic, MqttQualityOfServiceLevel.AtMostOnce).GetAwaiter().GetResult(); + // _client.ApplicationMessageReceivedAsync += async e => + // { + // if (e.ApplicationMessage.Topic == StateTopic) + // { + // var payload = Encoding.UTF8.GetString(e.ApplicationMessage.Payload); + // var state = JsonConvert.DeserializeObject(payload); + // var car = (VDA5050Car)SimpleLib.GetAllCars().FirstOrDefault(cc => cc is VDA5050Car); + // if (car == null) + // { + // Diagnosis.Post($"no car of serialNumber {state.serialNumber}"); + // } + // else + // { + // // Instead of calling UpdateState/UpdatePosition directly, + // // simply store the latest state message. + // car.SetLatestState(state); + // } + // } + // await Task.CompletedTask; + // }; + //} + + + + public async Task PublishTo(string order) + { + var message = new MqttApplicationMessageBuilder() + .WithTopic(OrderTopic) + .WithPayload(order) + .WithQualityOfServiceLevel(MqttQualityOfServiceLevel.AtMostOnce) + .Build(); + //Console.WriteLine($"[MQTT-Master] Pending Messages before sending: {_client.PendingApplicationMessagesCount}"); + + await _client.EnqueueAsync(message); + //Console.WriteLine($"[MQTT-Master] Sent order message. Pending Messages after sending: {_client.PendingApplicationMessagesCount}"); + + //LogMessage("Sent Order", OrderTopic, order); + } + + public async Task PublishInstantActions(string actions) + { + var message = new MqttApplicationMessageBuilder() + .WithTopic(_instantAction) + .WithPayload(actions) + .Build(); + + await _client.EnqueueAsync(message); + + } + + public void LogMessage(string direction, string topic, string payload) + { + string formattedPayload = payload; + + // Try to parse the payload as JSON and pretty-print it + try + { + var jsonObject = JsonConvert.DeserializeObject(payload); + formattedPayload = JsonConvert.SerializeObject(jsonObject, Formatting.Indented); + } + catch (JsonReaderException) + { + // If the payload is not valid JSON, just leave it as is + formattedPayload = payload; + } + + Console.WriteLine($"[{DateTime.Now:HH:mm:ss}] [{direction}] Topic: {topic}, Payload: {formattedPayload}"); + } + } +} \ No newline at end of file diff --git a/StandardScene.Protocol.VDA5050/VDACar/TextViewer.Designer.cs b/StandardScene.Protocol.VDA5050/VDACar/TextViewer.Designer.cs new file mode 100644 index 0000000..db7443d --- /dev/null +++ b/StandardScene.Protocol.VDA5050/VDACar/TextViewer.Designer.cs @@ -0,0 +1,58 @@ +锘縩amespace StandardScene.CarTypes +{ + partial class TextViewer + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.richTextBox1 = new System.Windows.Forms.RichTextBox(); + this.SuspendLayout(); + // + // richTextBox1 + // + this.richTextBox1.Location = new System.Drawing.Point(12, 12); + this.richTextBox1.Name = "richTextBox1"; + this.richTextBox1.Size = new System.Drawing.Size(776, 1032); + this.richTextBox1.TabIndex = 0; + this.richTextBox1.Text = ""; + // + // TextViewer + // + this.AutoScaleDimensions = new System.Drawing.SizeF(9F, 18F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(800, 1056); + this.Controls.Add(this.richTextBox1); + this.Name = "TextViewer"; + this.Text = "TextViewer"; + this.ResumeLayout(false); + + } + + #endregion + + private System.Windows.Forms.RichTextBox richTextBox1; + } +} \ No newline at end of file diff --git a/StandardScene.Protocol.VDA5050/VDACar/TextViewer.cs b/StandardScene.Protocol.VDA5050/VDACar/TextViewer.cs new file mode 100644 index 0000000..0a6c00d --- /dev/null +++ b/StandardScene.Protocol.VDA5050/VDACar/TextViewer.cs @@ -0,0 +1,28 @@ +锘縰sing System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Data; +using System.Drawing; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Forms; + +namespace StandardScene.CarTypes +{ + public partial class TextViewer : Form + { + public TextViewer() + { + InitializeComponent(); + } + + public void UpdateText(string str) + { + richTextBox1.Invoke((Action)delegate + { + richTextBox1.Text = str; + }); + } + } +} diff --git a/StandardScene.Protocol.VDA5050/VDACar/TextViewer.resx b/StandardScene.Protocol.VDA5050/VDACar/TextViewer.resx new file mode 100644 index 0000000..1af7de1 --- /dev/null +++ b/StandardScene.Protocol.VDA5050/VDACar/TextViewer.resx @@ -0,0 +1,120 @@ +锘 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/StandardScene.Protocol.VDA5050/VDACar/VDA5050Car.cs b/StandardScene.Protocol.VDA5050/VDACar/VDA5050Car.cs new file mode 100644 index 0000000..bf28d9c --- /dev/null +++ b/StandardScene.Protocol.VDA5050/VDACar/VDA5050Car.cs @@ -0,0 +1,1069 @@ +using Acornima.Ast; +using CommonUsage.Protocols.VDA5050.Messages; +using CommonUsage.Protocols.VDA5050.Objects; +using LessokajiWeaverUtilities.Utilities; +using MQTTnet; +using MQTTnet.Client; +using MQTTnet.Extensions.ManagedClient; +using MQTTnet.Protocol; +using Nancy.Routing; +using Newtonsoft.Json; +using SimpleLite; +using SimpleLite.RCS; +using SimpleLite.RCS.CarTypes; +using SimpleLite.RCS.Signal; +using SimpleLite.CADTools; +using SimpleLite.Props; +using SimpleLite.UI; +using SimpleCore; +using SimpleCore.Compiler; +using SimpleCore.Library; +using SimpleCore.PropType; +using StandardScene.Chained; +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Drawing; +using System.Linq; +using System.Net.Http; +using System.Security.Policy; +using System.Threading; +using System.Threading.Tasks; +using System.Windows.Forms; +using static SimpleLite.RCS.CarTypes.ClumsyCar; +using static SimpleLite.RCS.CarTypes.DummyCar; +using static System.Windows.Forms.VisualStyles.VisualStyleElement.TaskbarClock; +using Site = SimpleCore.PropType.Site; + +namespace StandardScene.CarTypes +{ + class VDA5050TrackField + { + public float Speed = -1; + public bool Reverse = false; + public int ReverseDst = -1; + public float[] typeInfo = []; + } + + // VDA5050SiteField 宸蹭笅娌夎嚦 Core锛圓rmCar.cs锛夛細ArmCar 寮曠敤瀹冭 ArmCar 鐣 Core锛 + // 鏁呮媶 VDA dll 鏃惰瀛楁绫讳繚鐣欎簬 Core锛屾湰鏂囦欢涓嶅啀瀹氫箟锛堜粎娉ㄩ噴涓浘寮曠敤锛夈 + + + [TemplateTrackCoderSettings( + priority = 0, + templateString = "agv.Go(${src.id},${dst.id},${track.id},${track.Speed},${track.Reverse || track.ReverseDst == dst.id},[${track.typeInfo}]);", + trackFields = typeof(VDA5050TrackField))] + + //[TemplateSiteCoderSettings( + // priority = 1, + // useVerb = "dst.DI41", + // templateString = "agv.Wait();agv.ChangeDI41Signal();agv.WaitAO3Signal();agv.Wait();agv.Sleep(${dst.SleepTime});", + // siteFields = typeof(VDA5050SiteField))] + + [CarType(Name = "VDA5050鏍囧噯杞")] + [I18N.DocumentTranslation(Name = "VDA5050 Car", locale = "en")] + public class VDA5050Car : Car + { + private static readonly MasterMQTTCommunication _mqttCommunication = new MasterMQTTCommunication(); + + [FieldMember] public string conf = ""; + + [FieldMember] public string SerialNumber = ""; + + /// + /// Total length of base routes sent from master control to AGV. + /// + [FieldMember] public float BaseLength = 5000; + + /// + /// Total length of horizon routes sent from mater control to AGV. + /// + [FieldMember] public float HorizonLength = 5000; + + private string _connectionStatus = "Offline"; // Default to "Offline" + private const string OrderTopic = "vda5050/orders"; + private VDA5050Interface currentAgv; + HttpClient hc = new HttpClient(); + + public bool IsPaused = false; + public int AO3 = 0; + + // Property for connection status + public string ConnectionStatus + { + get => _connectionStatus; + set + { + _connectionStatus = value; + + // Update lstatus based on the connection status + if (_connectionStatus == "ONLINE") + { + lstatus = "Online"; + } + else if (_connectionStatus == "OFFLINE") + { + lstatus = "Offline"; + } + else if (_connectionStatus == "CONNECTIONBROKEN") + { + lstatus = "Connection Broken"; + } + } + } + + public static async Task Create() + { + return new VDA5050Car() + { + lstatus = "Not Connected", + address = "127.0.0.1", + name = $"VDA5050鏍囧噯杞", + haveCoordination = true, + }; + } + + [JsonIgnore]public bool Listened = false; + public override void keepAlive() + { + // MasterMQTTCommunication _mqttCommunication = new MasterMQTTCommunication(); + + // _mqttCommunication.SubscribeTo(UpdateState,"vda5050/state").GetAwaiter().GetResult(); + // _mqttCommunication.SubscribeTo(UpdatePosition, "vda5050/visualization").GetAwaiter().GetResult(); + // var test = new MasterControlMQTTTest(); + // _mqttCommunication.TestPublishTo(); + // if(ConnectionStatus == "Online") + // _mqttCommunication.SubscribeToVisualizationTopic(); + if (!Listened) + { + SetupListeners(); + Listened = true; + } + // StartStateProcessing(); + // SetupInstActionListeners(); + lock(RouteCache) ProcessCacheAndSendOrderMessage(); + + //GetVDA5050StateFromC(); + // else Diagnosis.Post(" WARNING!! AGV is offline. Orders will not be sent"); + } + + public async Task GetVDA5050StateFromC() + { + try + { + //string jsonResponse1 = await hc.GetStringAsync($"http://{this.address}:8008/getStat"); + //var data1 = JsonConvert.DeserializeObject>(jsonResponse1); + string jsonResponse1 = await hc.GetStringAsync($"http://{this.address}:8008/getStat"); + var data1 = JsonConvert.DeserializeObject>(jsonResponse1); + if (data1.ContainsKey("AO3")) + { + string AO3Str = data1["AO3"]; + AO3 = int.Parse(AO3Str); + } + else Console.WriteLine("AO3 key not found in the response."); + } + catch (Exception ex) + { + Diagnosis.Log($"GetVDA5050StateFromC 澶辫触: {ex.Message}", "VDA5050", true); + } + } + + // public override Task actualSendScript(string script) + // { + // var tsk = new Task(() => + // { + // SelfEvaluating(new VDA5050Interface(id), script); + // return null; + // }); + // tsk.Start(); + // return tsk; + // } + public bool running = false; + public override async Task actualSendScript(string script) + { + script += "\nagv.Wait(1);\n"; + Diagnosis.Log($"{script}", "script", true); + if (running) + throw new Exception($"dummy car {id} already running script"); + running = true; + + try + { + route = new int[0]; + AppendDebug($"VDA5050 car {id} use jint for simulation"); + lock(RouteCache)currentAgv = new VDA5050Interface(id); + var finish = false; + var tcs = new TaskCompletionSource(); + new Thread(() => { + try + { + Diagnosis.Log($"start evaluate","script",true); + SelfEvaluating(currentAgv, script); + Diagnosis.Log($"end evaluate", "script", true); + tcs.SetResult(1); + finish = true; + } + catch (Exception ex) + { + tcs.SetException(ex); + } + } + ) + { Name = $"eva_{name}({id}):{status.programs.now.name}" }.Start(); + async void monitor() + { + await Task.Run(() => + { + while (true) + { + if (status.holdingLocks.Length > 0 && status.holdingLocks.Last() == GetLastSite() || finish) + { + Diagnosis.Log($"{script} {status.holdingLocks.Last()} {GetLastSite()} {finish}","script",true); + tcs.SetResult(1); + break; + } + Thread.Sleep(50); + } + }); + } + + // monitor(); + await tcs.Task; + // await currentAgv.WaitAsync(); + + Console.WriteLine($"{name}({id}) self evaluating script completed"); + } + catch (Exception ex) + { + AppendDebug($"simulation error:{ExceptionFormatter.FormatEx(ex)}"); + Console.WriteLine($"* {id} evaluating script failed, ex:{ExceptionFormatter.FormatEx(ex)}"); + running = false; + throw; + } + + running = false; + } + // public bool Restarting = false; + private void ProcessCacheAndSendOrderMessage() + { + try + { + int currentBase; + bool generateNewOrder = false; + + lock (RouteCache) + { + Console.WriteLine("RouteCache.Count: " + RouteCache.Count); + if (RouteCache.Count==0) + { + // Diagnosis.Post($"RouteCache.Count :{0}"); + return; + }; + lock (_receivedLastConfirmedRoute) + { + Console.WriteLine("_receivedOrderId: " + _receivedOrderId + " OrderId: " + OrderId); + Console.WriteLine(_receivedOrderId == OrderId); + if (_receivedOrderId == OrderId) + { + if (_receivedLastConfirmedRoute.Count == 0) + { + if (RouteCache.Count>0 && _receivedLastNodeSequenceId == RouteCache.Last().Item.sequenceId) + _toTraverseSequenceId = RouteCache.Count; + else + { + _toTraverseSequenceId = _receivedLastNodeSequenceId; + // _toTraverseSequenceId = 0; + } + } + else _toTraverseSequenceId = (int)_receivedLastConfirmedRoute[0].sequenceId; + + Console.WriteLine("_receivedLastConfirmedRoute.Count: " + _receivedLastConfirmedRoute.Count); + Console.WriteLine("_toTraverseSequenceId: " + _toTraverseSequenceId); + for (var i = 0; i < _toTraverseSequenceId; ++i)//瀹屾垚鐨勮矾寰勶紝鑺傜偣 + RouteCache[i].State = VDA5050Segment.SegState.Executed; + + foreach (var seg in RouteCache.Where(seg => + seg.State == VDA5050Segment.SegState.Executed)) + { + var shouldTriggerFinish = false; + + if (seg.Item.sequenceId == RouteCache.Count - 1) shouldTriggerFinish = true; + else if (seg.IsNode() && !seg.FinishTriggered && seg.Item.sequenceId + 1 + rr.State == VDA5050Segment.SegState.BaseSending || + rr.State == VDA5050Segment.SegState.BaseAcknowledged||rr.State == VDA5050Segment.SegState.Executed); + + var accumulatedHorizon = 0f; + for (var currentHorizon = currentBase + 1; currentHorizon < RouteCache.Count; ++currentHorizon) + { + RouteCache[currentHorizon].State = VDA5050Segment.SegState.HorizonSending; + + if (RouteCache[currentHorizon].IsNode()) continue; + accumulatedHorizon += RouteCache[currentHorizon].GetTrackLength(); + if (accumulatedHorizon >= HorizonLength) break; + } + + foreach (var item in _receivedLastConfirmedRoute) + { + if (item.released) continue; + + if (RouteCache[(int)item.sequenceId].State == + VDA5050Segment.SegState.HorizonSending) + RouteCache[(int)item.sequenceId].State = + VDA5050Segment.SegState.HorizonAcknowledged; + } + } + + if (currentBase > _lastBaseNodeSequenceId) + { + Diagnosis.Log($"\n{RouteCache.Display()}", "Interface", true); + Diagnosis.Log($"lastBase:{_lastBaseNodeSequenceId} curBase:{currentBase}","Interface",true); + _taskUpdateId++; + generateNewOrder = true; + } + } + + RouteCacheViewer?.UpdateText($"{DateTime.Now:HH:mm:ss-fff}\n\n{RouteCache.Display()}"); + + void PrintOrder(orderMessage om) + { + Diagnosis.Post($"order{om.orderId}-{om.orderUpdateId}:\n" + + $"nodes: {string.Join(" ", om.nodes.Select(nn => $"{nn.nodeId}({nn.released})"))}\n" + + $"edges: {string.Join(" ", om.edges.Select(ee => $"{ee.edgeId}({ee.released})"))}", + "orders", true); + } + var startTime = DateTime.Now; + if (generateNewOrder) + { + var activeItems = RouteCache.Skip(_lastBaseNodeSequenceId) + .Where(rr => rr.State != VDA5050Segment.SegState.Waiting).Select(rr => rr.Item).ToList(); + + var newOrder = new orderMessage() + { + orderId = OrderId, + orderUpdateId = (uint)_taskUpdateId, + nodes = activeItems.OfType().ToArray(), + edges = activeItems.OfType().ToArray() + }; + var newContent = JsonConvert.SerializeObject(newOrder, Formatting.Indented); + // Diagnosis.Post($"add order:", "orders", true); + // PrintOrder(newOrder); + _orderList.Add((newContent, DateTime.MinValue)); + startTime = DateTime.Now; + } + + _lastBaseNodeSequenceId = currentBase; + + Console.WriteLine("_orderList.Count: " + _orderList.Count); + if (_orderList.Count == 0) return; + + var pendingOrder = _orderList[0]; + if ((DateTime.Now - pendingOrder.SendTime).TotalSeconds < 0.5) return; + var (content, _) = pendingOrder; + var order = JsonConvert.DeserializeObject(content); + + var acknowledged = true; + lock (RouteCache) + { + // if (generateNewOrder) Console.WriteLine($"~~~鍝堝搱 get Lock ,interval:{(DateTime.Now - startTime).TotalSeconds}s"); + foreach (var item in order.nodes) + { + var state = RouteCache[(int)item.sequenceId].State; + Diagnosis.Post($"node: {item.nodeId}({item.sequenceId}), {state}"); + if (item.released && state != VDA5050Segment.SegState.BaseAcknowledged && + state != VDA5050Segment.SegState.Executed) acknowledged = false; + if (!item.released && state != VDA5050Segment.SegState.HorizonAcknowledged && + state != VDA5050Segment.SegState.BaseSending) acknowledged = false; + } + foreach (var item in order.edges) + { + var state = RouteCache[(int)item.sequenceId].State; + Diagnosis.Post($"edge: {item.edgeId}({item.sequenceId}), {state}"); + if (item.released && state != VDA5050Segment.SegState.BaseAcknowledged && + state != VDA5050Segment.SegState.Executed) acknowledged = false; + if (!item.released && state != VDA5050Segment.SegState.HorizonAcknowledged && + state != VDA5050Segment.SegState.BaseSending) acknowledged = false; + } + } + + if (acknowledged) _orderList.RemoveAt(0); + else + { + //Diagnosis.Post($"order-{order.orderUpdateId} resend"); + + Diagnosis.Post("-------Sending order to AGV"); + PrintOrder(order); + _mqttCommunication.PublishTo(content).GetAwaiter().GetResult(); + // Post("order", content); + + _orderList[0] = (content, DateTime.Now);//todo 杩欓噷鍙兘浼氭湁鏃堕棿宸紵杞︾杩樻病鍙嶅簲濂斤紙鎴栬呭鐞嗗涓嶄笂浜嗭級锛屽湪鍙戜簡涓娆★紵 + } + } + catch (Exception ex) + { + // 鎵撳嵃寮傚父淇℃伅鍜屽爢鏍堣窡韪紝甯姪瀹氫綅鎶ラ敊浣嶇疆 + Console.WriteLine("ProcessCacheAndSendOrderMessage 鍙戠敓寮傚父锛"); + Console.WriteLine("寮傚父淇℃伅锛" + ex.Message); + Console.WriteLine("鍫嗘爤璺熻釜锛" + ex.StackTrace); + // throw; + } + + + } + + private int _lastBaseNodeSequenceId = -1; + + private List<(string Content, DateTime SendTime)> _orderList = new(); + + /// + /// Route state stored by master control. + /// + internal VDA5050SegmentsCache RouteCache = new(); + + public void VDAReset() + { + lock (RouteCache) + lock (_receivedLastConfirmedRoute) + { + RouteCache = new(); + _receivedLastNodeSequenceId = -1; + _toTraverseSequenceId = -1; + _receivedLastConfirmedRoute = []; + _lastBaseNodeSequenceId = -1; + TaskId = 0; + running = false; + _orderList = new(); + } + } + + internal class VDA5050SegmentsCache + { + public void Add(VDA5050Segment segment) + { + segment.Item.sequenceId = _sequenceId++; + _segments.Add(segment); + } + + public VDA5050Segment this[int index] + { + get => _segments[index]; + set => _segments[index] = value; + } + + public VDA5050Segment Last() + { + return _segments.Last(); + } + + public int FindLastIndex(Predicate func) + { + return _segments.FindLastIndex(func); + } + + public bool Any(Func func) + { + return _segments.Any(func); + } + + public IEnumerable Where(Func func) + { + return _segments.Where(func); + } + + public IEnumerable Skip(int count) + { + return _segments.Skip(count); + } + + public int Count => _segments.Count; + + public string Display() + { + return string.Join("\n", + _segments.Select(seg => + { + var id = ""; + if (seg.Item is node nn) id = nn.nodeId; + else if (seg.Item is edge ee) id = ee.edgeId; + return $"{seg.Item.sequenceId}\t{id}\t{seg.Item.GetType().Name}\t{seg.State}"; + })); + } + + private uint _sequenceId = 0; + + private List _segments = new(); + } + + private int _taskId; + + private int _taskUpdateId = -1; + + internal int TaskId + { + get => _taskId; + set + { + _taskUpdateId = -1; + _lastBaseNodeSequenceId = -1; + _taskId = value; + } + } + + internal string OrderId => $"order-{_taskId}"; + + internal uint OrderUpdateId => (uint)_taskUpdateId; + + /// + /// Route state stored by AGV. + /// + private List _receivedLastConfirmedRoute = []; + + private string _receivedOrderId = ""; + // Add these fields at the top of the VDA5050Car class (with your other field declarations) + private volatile stateMessage _latestStateMessage = null; + private CancellationTokenSource _stateProcessingCts; + + + private uint _receivedOrderUpdateId = 0; + + private int _receivedLastNodeSequenceId = -1; + + public void ReseData() + { + RouteCache = new(); + _receivedLastNodeSequenceId = -1; + _toTraverseSequenceId = -1; + _receivedLastConfirmedRoute = []; + _lastBaseNodeSequenceId = -1; + // TaskId = 0; + _orderList = new(); + } + + + private int _toTraverseSequenceId = -1; + + public int TotalStateMessageNumber = 0; + + public void UpdateState(stateMessage msg) + { + + // lock (RouteCache) + lock (_receivedLastConfirmedRoute) + { + var segments = new List(); + + int ii = 0, jj = 0; + while (ii < msg.edgeStates.Length && jj < msg.nodeStates.Length) + { + var edge = msg.edgeStates[ii]; + var node = msg.nodeStates[jj]; + var takeEdge = edge.sequenceId < node.sequenceId; + + if (takeEdge) + { + segments.Add(edge); + ii++; + } + else + { + segments.Add(node); + jj++; + } + } + for (var i = ii; i < msg.edgeStates.Length; ++i) segments.Add(msg.edgeStates[i]); + for (var j = jj; j < msg.nodeStates.Length; ++j) segments.Add(msg.nodeStates[j]); + + if (segments.Count > 1) + for (var i = 1; i < segments.Count; i++) + if (segments[i - 1].sequenceId + 1 != segments[i].sequenceId) + throw new Exception("stateMessage not continuous!"); + + _receivedOrderId = msg.orderId; + _receivedOrderUpdateId = msg.orderUpdatedId; + _receivedLastNodeSequenceId = (int)msg.lastNodeSequenceId; + _receivedLastConfirmedRoute = segments; + var s = $"{DateTime.Now:HH:mm:ss-fff}\n\n" + + $"{string.Join("\n", _receivedLastConfirmedRoute.Select(item => $"{(item is nodeState ? "*" + ((nodeState)item).nodeId : ((edgeState)item).edgeId)}\t{item.sequenceId}\t{item.released}"))}" + + $"\n_receivedLastNodeSequenceId:{_receivedLastNodeSequenceId}\n" + + $"{string.Join("\n",msg.errors.Select(e=>$"{e.errorType}锛歿e.errorLevel}"))}" + + $"{string.Join("\n",msg.actionStates.Select(a=>$"{a.actionDescription}锛歿a.actionStatus} {a.actionId}"))}"; + StateMessageViewer?.UpdateText(s); + // Diagnosis.Log(s,"stateMsg",true); + } + } + + public void UpdatePosition(visualizationMessage msg) + { + haveCoordination = true; + x = (float)msg.agvPosition.x; + y = (float)msg.agvPosition.y; + th = (float)(msg.agvPosition.theta / Math.PI * 180); + } + + /// + /// Called from the MQTT callback to store the latest state message. + /// + public void SetLatestState(stateMessage msg) + { + _latestStateMessage = msg; + } + + public void SetupListeners() + { + _mqttCommunication.SubsribeToConnectionTopic(); + //_mqttCommunication.SubsribeToFactSheetTopic(); + //_mqttCommunication.SubscribeToRegularState(); + _mqttCommunication.SubscribeToState(); + _mqttCommunication.SubscribeToVisualization(); + //_mqttCommunication.SubscribeToVisualization(); + } + + // public void SetupInstActionListeners() + // { + // InstanceActionPause(); + // InstanceActionResume(); + // InstanceActionCancel(); + // } + // todo: should not inherit form ClumsyCarStatus + public class VDA5050CarStatus : ClumsyCar.ClumsyCarStatus + { + + } + public override CarStatus status { get; set; } = new VDA5050CarStatus(); + + [MethodMember(Name = "Request fact sheet")] + public void InstantActionFactSheet() + { + var instantMsg = VDA5050Commons.CreateInstanceAction( + actionId: Guid.NewGuid().ToString(), + actionType: "factSheetRequest"); + _mqttCommunication.PublishInstantActions(JsonConvert.SerializeObject(instantMsg)); + } + + [MethodMember(Name = "Pause")] + public void InstanceActionPause() + { + var instantMsg = VDA5050Commons.CreateInstanceAction( + actionId: Guid.NewGuid().ToString(), + actionType: "startPause" + ); + + _mqttCommunication.PublishInstantActions(JsonConvert.SerializeObject(instantMsg)); + + + // Post("instanceAction", JsonConvert.SerializeObject(instanceMsg)); + } + [MethodMember(Name = "Resume")] + public void InstanceActionResume() + { + + var instantMsg = VDA5050Commons.CreateInstanceAction( + actionId: Guid.NewGuid().ToString(), + actionType: "stopPause" + ); + + _mqttCommunication.PublishInstantActions(JsonConvert.SerializeObject(instantMsg)); + + // Post("instanceAction", JsonConvert.SerializeObject(instanceMsg)); + + } + [MethodMember(Name = "Cancel")] + public void InstanceActionCancel() + { + + var instantMsg = VDA5050Commons.CreateInstanceAction( + actionId: Guid.NewGuid().ToString(), + actionType: "cancelOrder" + ); + _mqttCommunication.PublishInstantActions(JsonConvert.SerializeObject(instantMsg)); + + //_mqttCommunication.RestartClient(); + + //ForceStop(); + + + // Post("instanceAction", JsonConvert.SerializeObject(instanceMsg)); + } + + [MethodMember(Name = "鏄剧ずRouteCache")] + public void DisplayRouteCache() + { + RouteCacheViewer = new TextViewer(); + RouteCacheViewer.Show(); + } + + [MethodMember(Name = "绔嬪嵆寮哄埗缁撴潫")] + public void ForceStop() + { + AppendDebug("Clumsy Restarted"); + NoSchedule(true); + siteID = -1; + VDAReset(); + Get("reset"); + Commons.DeleteTag(tags, "occupied"); + AppendDebug("restarting clumsy"); + AppendDebug( + "wait for any pending task to flush." + ); + status.programs.task = Task.CompletedTask; + + AppendDebug("Clumsy Restarted"); + NoSchedule(true); + siteID = -1; + Commons.DeleteTag(tags, "occupied"); + // Reset(); + } + + [MethodMember(Name = "鏂扮幇鍦烘淇")] + public new void Repair() + { + AppendDebug("ui-repair"); + Diagnosis.Post($"Repair Car {this.name}({this.id})"); + NoSchedule(makeUnavailable: false); + siteID = -1; + base.tags.Clear(); + lstatus = "鐜板満妫淇"; + } + [MethodMember(Name = "鏄剧ず涓婃姤娑堟伅")] + public void DisplayLatestMessage() + { + StateMessageViewer = new TextViewer(); + StateMessageViewer.Show(); + } + + public TextViewer StateMessageViewer; + + public TextViewer RouteCacheViewer; + + public override void rightClickAction(float mouseX, float mouseY) + { + try + { + Site site1 = null; + float dist = float.MaxValue; + foreach (var site in SimpleLib.GetAllSites()) + { + var d = LessMath.dist(site.x, site.y, mouseX, mouseY); + if (d < dist) + { + dist = (float)d; + site1 = site; + } + } + + Console.WriteLine($"cart {id} goto {site1.id}"); + if (dist < 20) + { + _ = Task.Factory.StartNew(() => + { + var siteDst = site1; + if (siteID == -1) + { + dist = float.MaxValue; + foreach (var site in SimpleLib.GetAllSites()) + { + var d = LessMath.dist(site.x, site.y, x, y); + if (d < dist) + { + dist = (float)d; + site1 = site; + } + } + } + //else site1 = SimpleLib.GetSite(siteID); + else site1 = SimpleLib.GetSite(GetLastSite()); + + try + { + var plan = new SegmentPlan() { usingCar = this }; + plan.FindRoute(site1, siteDst); + // var code = $"{plan.Code()}; agv.Wait();"; + G.pushStatus($"鍚憑name}({id})涓嬪彂琛岃蛋浠诲姟"); + Console.WriteLine($"right click, directs AGV{name}({id}) from {site1.id} to {siteDst.id}"); + tags.Add("occupied", "SimplyMove"); + tags.Add("dest", siteID.ToString()); + var tsk = plan.Compile("walk").Queue(false,false);//鎺ョ画浠诲姟 + // var tsk = plan.Compile("walk").Queue(); + G.pushStatus($"AGV:{name}({id})寮濮嬫墽琛屼换鍔"); + tsk.Wait(); + G.pushStatus($"AGV:{name}({id})鎵ц浠诲姟瀹屾瘯, siteID={siteDst.id}"); + siteID = siteDst.id; + tags.Remove("occupied"); + tags.Remove("dest"); + } + catch (Exception ex) + { + Console.WriteLine($"Go route exception:{ExceptionFormatter.FormatEx(ex)}"); + } + }); + } + } + catch + { + //... ignored + } + } + + private bool _running; + [MethodMember(Name = "娴嬭瘯")] + public async void Test() + { + _running = true; + var siteA = SimpleLib.GetAllSites().First(s => s.name == "A"); + var siteB = SimpleLib.GetAllSites().First(s => s.name == "B"); + while (_running) + { + var planA = new SegmentPlan() { usingCar = this }; + planA.FindRoute(SimpleLib.GetSite(GetLastSite()), siteA); + await planA.Compile("A").Queue(); + var planB = new SegmentPlan() { usingCar = this }; + planB.FindRoute(siteA, siteB); + await planB.Compile("B").Queue(); + Thread.Sleep(1000); + } + } + + [MethodMember(Name = "澶氱偣浠诲姟娴嬭瘯")] + public async void MultiPointTest() + { + _running = true; + var siteNumResult = InputBox.ShowDialog("璇锋寜椤哄簭杈撳叆褰撳墠澶氱偣浠诲姟鐨勭珯鐐筰d:"); + string sitesIDStr = InputBox.ResultValue; + Console.WriteLine("sitesIDStr: " + sitesIDStr); + int[] sitesID = Array.ConvertAll(sitesIDStr.Split(' '), int.Parse); + for (var i = 0; i < sitesID.Length; i++) + { + Console.WriteLine(sitesID[i]); + } + Site[] sites = new Site[sitesID.Length]; + SimpleLite.Point[] pts = new SimpleLite.Point[sitesID.Length]; + + //for (var i = 0; i < sitesID.Length; i++) + //{ + + //} + + for (var i = 0; i < sitesID.Length; i++) + { + Console.WriteLine("-------------------褰撳墠鐐逛负锛" + sitesID[i]); + sites[i] = SimpleLib.GetSite(sitesID[i]); + Commons.AddOrUpdateSiteField(sites[i], "codeArrive", "agv.Wait();agv.ChangeDI41Signal();agv.WaitAO3Signal();agv.Wait();"); + var plan = new SegmentPlan() { usingCar = this }; + plan.FindRoute(SimpleLib.GetSite(GetLastSite()), sites[i]); + G.pushStatus($"涓嬪彂琛岃蛋浠诲姟from {GetLastSite()} to {sites[i]}"); + await plan.Compile($"plan").Queue(); + //Thread.Sleep(200); + Commons.DeleteSiteField(sites[i], "codeArrive"); + Console.WriteLine("-----------------------寮濮嬩笅涓娈典换鍔"); + } + + //瀹屾垚浠ヤ笂浠诲姟鍚庤繑鍥炲緟鍛界偣 + G.pushStatus($"杩斿洖寰呭懡鐐"); + var targetPlan = Commons.GetNearestPlan( + this, + site => + site.fields.ContainsKey("standby") + && site.fields["standby"] == "true" + ); + if (targetPlan != null) + { + Commons.GoSite(this, targetPlan.Destination, 1); + } + + } + + + [MethodMember(Name = "鍋滄娴嬭瘯")] + public void Stop() + { + _running = false; + } + + + [MethodMember(Name = "娴嬭瘯淇敼DI41淇″彿")] + public void ChangeDI41SignalTest() + { + try + { + Console.WriteLine("car address: " + this.address); + hc.GetStringAsync($"http://{this.address}:8008/setValue?FieldName=DI41&Value=True"); + Console.WriteLine("灏咲I41缃负True"); + Thread.Sleep(1000); + hc.GetStringAsync($"http://{this.address}:8008/setValue?FieldName=DI41&Value=False"); + Console.WriteLine("1绉掑悗灏咲I41缃负False"); + } + catch + { + Console.WriteLine("閿欒"); + }; + + } + + [MethodMember(Name = "娴嬭瘯淇敼AO3淇″彿")] + public void ChangeAO3SignalTest() + { + try + { + hc.GetStringAsync($"http://{this.address}:8008/setValue?FieldName=AO3&Value=1"); + //hc.GetStringAsync($"http://192.168.2.1:8008/setValue?FieldName=AO3&Value=1"); + Console.WriteLine("灏咥O3缃负1"); + Thread.Sleep(1000); + hc.GetStringAsync($"http://{this.address}:8008/setValue?FieldName=AO3&Value=0"); + //hc.GetStringAsync($"http://192.168.2.1:8008/setValue?FieldName=AO3&Value=0"); + Console.WriteLine("1绉掑悗灏咥O3缃负0"); + } + catch + { + Console.WriteLine("閿欒"); + }; + } + + [MethodMember(Name = "杩斿洖寰呭懡鐐")] + public void GoStandBySite() + { + try + { + if (this != null) + { + var carGroup = this.fields.TryGetValue("group", out var value) + ? value + : "all"; + var targetPlan = Commons.GetNearestPlan( + this, + site => + site.fields.ContainsKey("standby") + && carGroup.Equals( + site.fields.TryGetValue("group", out var value) ? value : "all" + ) + && site.fields["standby"] == "true" + ); + if (targetPlan != null) + { + Commons.GoSite(this, targetPlan.Destination, 1); + } + } + } + catch (Exception) + { + Console.WriteLine($"璋冪敤灏忚溅鍘诲緟鍛界偣澶辫触"); + } + } + + protected override void draw(Graphics eGraphics) + { + eGraphics.FillRectangle(Brushes.Gray, -160, -120, 320, 240); + eGraphics.DrawRectangle(Pens.White, -160, -120, 320, 240); + eGraphics.DrawLine(Pens.White, 0, -120, 160, 0); + eGraphics.DrawLine(Pens.White, 0, 120, 160, 0); + } + + + public void Post1(string api, string content, int port = 0) + { + } + + public void Post(string api, string content, int port = 0) + { + // Console.WriteLine($"Content of the order message in http: " + content); + var addresses = address.Split(',').OrderBy(p => ((VDA5050CarStatus)status).apiStat.Contains(p) ? 0 : 1); + foreach (var addr in addresses) + { + var call = $"{addr}:{(port != 0 ? port : GetConf("cport", 8008))}/{api}"; + try + { + GetHC().Post($"http://{call}", content); + } + catch + { + + } + } + } + public string Get(string api, int port = 0, bool outputIfFail = false) + { + var addrls = address.Split(','); + var exs = new string[addrls.Length]; + int j = 0; + foreach (var addr in addrls.OrderBy(p => ((ClumsyCarStatus)status).apiStat.Contains(p) ? 0 : 1)) + { + for (int i = 0; i < 8; ++i) + { + var call = $"{addr}:{(port != 0 ? port : GetConf("cport", 8008))}/{api}"; + try + { + ((ClumsyCarStatus)status).apiStat = $"[try{i}]{call}"; + var str = GetHC().GetString($"http://{call}"); + ((ClumsyCarStatus)status).apiStat = $"[fin{i}]{call}"; + // seqAPIFail = 0; + return str; + } + catch (Exception ex) + { + ((ClumsyCarStatus)status).apiStat = $"[bad{i}]{call}, ex={ex.Message}"; + exs[j] = ex.Message; + // seqAPIFail += 1; + } + } + + j += 1; + } + + var exstr = $"Get API {api} failed, addresses={address} all not available({string.Join(",", exs)})"; + AppendDebug(exstr); + throw new Exception(exstr); + } + private HttpClient2 GetHC() + { + lock (this) + return _carHc ??= new HttpClient2(TimeSpan.FromSeconds(GetTimeout()), this); + } + + private int GetTimeout() + { + var timeout = 5; + if (conf.Contains("timeout")) + timeout = Convert.ToInt32(conf.Substring(conf.IndexOf("timeout") + 8).Split(',')[0]); + return timeout; + } + + private T GetConf(string name, T defVal) + { + var timeout = defVal; + if (conf.Contains(name)) + { + var ll = conf.Split(',').Where(ss => ss.Contains(name)).ToList(); + if (ll.Count > 0) + { + var str = ll[0]; + return (T)TypeDescriptor.GetConverter(typeof(T)).ConvertFromString(str.Split(':')[1]); + } + } + return timeout; + } + + // mqtt controller + + + private HttpClient2 _carHc; + } +} diff --git a/StandardScene.Protocol.VDA5050/VDACar/VDA5050Commons.cs b/StandardScene.Protocol.VDA5050/VDACar/VDA5050Commons.cs new file mode 100644 index 0000000..ef0703e --- /dev/null +++ b/StandardScene.Protocol.VDA5050/VDACar/VDA5050Commons.cs @@ -0,0 +1,39 @@ +锘縰sing System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using CommonUsage.Protocols.VDA5050.Messages; +using CommonUsage.Protocols.VDA5050.Objects; + +namespace StandardScene.CarTypes +{ + public class VDA5050Commons + { + private static uint _headerId = 0; // Tracks the current header ID. + private static readonly string _protocolVersion = "1.0"; // Example protocol version. + private static readonly string _manufacturer = "YourManufacturer"; // Replace with your manufacturer. + private static readonly string _serialNumber = "test-01"; // Replace with your AGV's serial number. + + public static instanceAction CreateInstanceAction(string actionId, string actionType) + { + return new instanceAction + { + headerId = ++_headerId, // Increment header ID for each message. + timestamp = DateTime.UtcNow.ToString("o"), // ISO 8601 timestamp. + version = _protocolVersion, + manufacturer = _manufacturer, + serialNumber = _serialNumber, + actions = new List + { + new actionState + { + actionId = actionId, + actionType = actionType, + } + } + }; + } + + } +} diff --git a/StandardScene.Protocol.VDA5050/VDACar/VDA5050Helper.cs b/StandardScene.Protocol.VDA5050/VDACar/VDA5050Helper.cs new file mode 100644 index 0000000..9f847bf --- /dev/null +++ b/StandardScene.Protocol.VDA5050/VDACar/VDA5050Helper.cs @@ -0,0 +1,56 @@ +锘縰sing System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using SimpleCore; +using SimpleCore.PropType; + +namespace StandardScene.CarTypes +{ + internal class VDA5050Helper + { + /// + /// If true, use Simple "id" field as VDA5050 id. + /// If false, use Simple "name" field as VDA5050 id. + /// + public static bool UseSimpleId = true; + + public static T FindByVDA5050Id(string vda5050Id) where T : Prop + { + Func testFunc = UseSimpleId ? prop => $"{prop.id}" == vda5050Id : prop => prop.name == vda5050Id; + + var matches = SimpleLib.Things().OfType().Where(prop => testFunc(prop)).ToList(); + if (matches.Count == 0) return null; + if (matches.Count > 1) + throw new Exception( + $"multiple Props have same name {vda5050Id}, matches: {string.Join(", ", matches.Select(pp => $"{pp.id}({pp.GetType()})"))}"); + return matches[0]; + } + + public static string GetVDA5050Id(int propId) + { + return UseSimpleId ? $"{propId}" : SimpleLib.Things().Where(pp => pp.id == propId).FirstOrDefault().name; + } + + public static string GetVDA5050Id(Prop prop) + { + return UseSimpleId ? $"{prop.id}" : prop.name; + } + + public static Site FindSiteByVDA5050Id(string vda5050Id) + { + return FindByVDA5050Id(vda5050Id); + } + + public static Track FindTrackByVDA5050Id(string vda5050Id) + { + return FindByVDA5050Id(vda5050Id); + } + + public static AbstractCar FindCarByVDA5050Id(string vda5050Id) + { + return FindByVDA5050Id(vda5050Id); + } + } +} diff --git a/StandardScene.Protocol.VDA5050/VDACar/VDA5050Interface.cs b/StandardScene.Protocol.VDA5050/VDACar/VDA5050Interface.cs new file mode 100644 index 0000000..20bb83e --- /dev/null +++ b/StandardScene.Protocol.VDA5050/VDACar/VDA5050Interface.cs @@ -0,0 +1,177 @@ +using SimpleCore.BasicProps; +using SimpleCore.Library; +using SimpleCore.Traffic; +using SimpleCore; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using CommonUsage.Protocols.VDA5050.Objects; +using System.Security.Cryptography.X509Certificates; +using System.Numerics; +using System.Threading; +using System.Net; +using System.Net.Http; +using Nancy.Routing; +using Newtonsoft.Json; +using SimpleLite; +using System.Windows.Forms; + +namespace StandardScene.CarTypes +{ + internal class VDA5050Interface : AGVInterface + { + public static int TaskId = 0; + HttpClient hc = new HttpClient(); + + public override bool TryLock(int siteId) + { + if (!_car.status.usage.Get().scheduling) throw new Exception("abandoned"); + var siteId_temp = siteId; + var pendinglocks0 = _car.status.pendingLocks[0]; + if (siteId_temp != pendinglocks0) + { + throw new Exception("lock not according to sequence"); + } + return TrafficControl.TryLock(_car, siteId); + } + + public override void Leave(int siteId) + { + TrafficControl.Leave(_car, siteId); + } + + public VDA5050Interface(int id) + { + _car = (VDA5050Car)SimpleLib.GetCar(id); + // _car.RouteCache = new(); + // _car.TaskId = TaskId++; + if (_car.RouteCache.Count ==0||_car.RouteCache.Count>0&&_car.RouteCache.Last().State==VDA5050Segment.SegState.Executed)//鎺ョ画浠诲姟 + { + // _car.RouteCache = new(); + // _car.ResetLastNodeSequenceId(); + _car.ReseData(); + _car.TaskId = TaskId; + TaskId += 1; + } + } + + public void Go(int srcId, int dstId, int trackId, int speed = -1, bool reverse = false, float[] trackTypeInfo = null) + { + VDA5050Segment src, dst, track; + lock (_car.RouteCache) + { + //Console.WriteLine("_car.RouteCache.Count: " + _car.RouteCache.Count); + if (_car.RouteCache.Count == 0) + { + src = new(new node() + { + nodeId = VDA5050Helper.GetVDA5050Id(srcId), + nodePosition = new() { x = SimpleLib.GetSite(srcId).x, y = SimpleLib.GetSite(srcId).y } + }, VDA5050Segment.SegState.Waiting); + _car.RouteCache.Add(src); + } + else src = _car.RouteCache.Last(); + + if (trackTypeInfo.Length != 0 && trackTypeInfo[0] == 3) + { + var controlPointNum = (int)trackTypeInfo[1]; + var controlPoints = new controlPoint[controlPointNum]; + for (int pt = 2; pt < controlPointNum * 2 + 2; pt += 2) + { + controlPoints[pt/2-1] = new controlPoint(trackTypeInfo[pt], trackTypeInfo[pt + 1], trackTypeInfo[1 + controlPointNum * 2 + pt / 2]); + } + var knotVector = new float[controlPointNum*2]; + for (int i = 0; i < controlPointNum; i++) + { + knotVector[i] = 0f; + knotVector[i + controlPointNum] = 1f; + } + var trajectory = new trajectory(){controlPoints = controlPoints,degree = controlPointNum,knotVector = knotVector }; + track = new(new edge() + { + edgeId = VDA5050Helper.GetVDA5050Id(trackId), + startNodeId = VDA5050Helper.GetVDA5050Id(srcId), + endNodeId = VDA5050Helper.GetVDA5050Id(dstId), + trajectory = trajectory, + orientation = reverse ? Math.PI : 0.0, + action = [new action() { actionDescription = $"{VDA5050Helper.GetVDA5050Id(trackId)}", actionId = Guid.NewGuid().ToString() }] + }, VDA5050Segment.SegState.Waiting); + } + else + { + track = new(new edge() + { + edgeId = VDA5050Helper.GetVDA5050Id(trackId), + startNodeId = VDA5050Helper.GetVDA5050Id(srcId), + endNodeId = VDA5050Helper.GetVDA5050Id(dstId), + trackTypeInfo = trackTypeInfo, + orientation = reverse ? Math.PI : 0.0, + action = [new action() { actionDescription = $"{VDA5050Helper.GetVDA5050Id(trackId)}", actionId = Guid.NewGuid().ToString() }] + }, VDA5050Segment.SegState.Waiting); + } + + dst = new(new node() + { + nodeId = VDA5050Helper.GetVDA5050Id(dstId), + nodePosition = new() { x = SimpleLib.GetSite(dstId).x, y = SimpleLib.GetSite(dstId).y }, + actions = [new action() { actionDescription = $"{VDA5050Helper.GetVDA5050Id(dstId)}", actionId = Guid.NewGuid().ToString() }] + }, VDA5050Segment.SegState.Waiting); + + _car.RouteCache.Add(track); + _car.RouteCache.Add(dst); + } + + Queue(async () => + { + while (!TryLock(dstId)) + await Task.Delay(10); + lock (_car.RouteCache) + { + if (src.Item.sequenceId == 0) + { + src.State = VDA5050Segment.SegState.BaseSending; + } + track.State = VDA5050Segment.SegState.BaseSending; + dst.State = VDA5050Segment.SegState.BaseSending; + src.Item.released = true; + track.Item.released = true; + dst.Item.released = true; + } + }, async () => + { + await src.FinishToken.Task; + Leave(srcId); + }); + } + + public void ChangeDI41Signal() + { + hc.GetStringAsync($"http://{_car.address}:8008/setValue?FieldName=DI41&Value=True"); + Diagnosis.Log("灏咲I41缃负True", "VDA5050", true); + Thread.Sleep(1000); + hc.GetStringAsync($"http://{_car.address}:8008/setValue?FieldName=DI41&Value=False"); + Diagnosis.Log("1绉掑悗灏咲I41缃负False", "VDA5050", true); + } + + public void Sleep(int SleepTime) + { + Diagnosis.Log("灏忚溅寮濮婼leep" + SleepTime + "ms", "VDA5050", true); + Thread.Sleep(SleepTime); + Diagnosis.Log("灏忚溅缁撴潫Sleep", "VDA5050", true); + } + + public void WaitAO3Signal() + { + while(_car.AO3 == 0) + { + Thread.Sleep(200); + } + Diagnosis.Log($"AO3: " + _car.AO3, "VDA5050", true); + Diagnosis.Log("鑾峰彇鍒癆O3淇″彿涓1锛岀户缁笅涓换鍔", "VDA5050", true); + } + + private VDA5050Car _car; + } +} diff --git a/StandardScene.Protocol.VDA5050/VDACar/VDA5050Segment.cs b/StandardScene.Protocol.VDA5050/VDACar/VDA5050Segment.cs new file mode 100644 index 0000000..6f8705b --- /dev/null +++ b/StandardScene.Protocol.VDA5050/VDACar/VDA5050Segment.cs @@ -0,0 +1,77 @@ +using System; +using System.Threading.Tasks; +using CommonUsage.Protocols.VDA5050.Objects; +using SimpleLite.CADTools; +using SimpleLite.Props; +using SimpleLite.UI; +using SimpleCore; +using SimpleCore.Library; +using SimpleCore.PropType; + +namespace StandardScene.CarTypes +{ + /// + /// Intermediate data structure for linking VDA5050 nodes & edges with MDCS sites & tracks. + /// + internal class VDA5050Segment + { + public VDA5050Segment(sequenceItem item, SegState state) + { + Item = item; + State = state; + if (IsNode()) FinishToken = new TaskCompletionSource(); + } + + public enum SegState + { + Waiting, + BaseSending, + BaseAcknowledged, + HorizonSending, + HorizonAcknowledged, + Executed, + } + + public bool IsNode() + { + return Item is node || Item is nodeState; + } + + public float GetTrackLength() + { + var track = VDA5050Helper.FindTrackByVDA5050Id(((edge)Item).edgeId); + if (track is UITrack lineTrack) + { + var sa = SimpleLib.GetSite(track.siteA); + var sb = SimpleLib.GetSite(track.siteB); + return (float)LessMath.dist(sa.x, sa.y, sb.x, sb.y); + } + else if (track is UICircularArcTrack arcTrack) + { + throw new Exception("not implemented yet"); + } + else if (track is UIBezierTrack bezierTrack) + { + var sa = SimpleLib.GetSite(track.siteA); + var sb = SimpleLib.GetSite(track.siteB); + return (float)LessMath.dist(sa.x, sa.y, sb.x, sb.y); + + } + else if(track is UINurbsTrack) + { + var sa = SimpleLib.GetSite(track.siteA); + var sb = SimpleLib.GetSite(track.siteB); + return (float)LessMath.dist(sa.x, sa.y, sb.x, sb.y); + } + else throw new Exception("not implemented yet"); + } + + public sequenceItem Item = null; + + public SegState State; + + public bool FinishTriggered = false; + + public TaskCompletionSource FinishToken = null; + } +} diff --git a/StandardScene.Protocol.VDA5050/VDACar/VDA5050WebApi.cs b/StandardScene.Protocol.VDA5050/VDACar/VDA5050WebApi.cs new file mode 100644 index 0000000..78b3980 --- /dev/null +++ b/StandardScene.Protocol.VDA5050/VDACar/VDA5050WebApi.cs @@ -0,0 +1,130 @@ +锘//using System; +//using Nancy; +//using System.Linq; +//using CommonUsage.Protocols.VDA5050.Messages; +//using Nancy.Extensions; +//using Newtonsoft.Json; +//using SimpleCore; +//using SimpleCore.Library; +//using System.Collections.Concurrent; +//using System.Timers; + +//namespace StandardScene.CarTypes +//{ +// public class VDA5050WebApi : NancyModule +// { +// private static ConcurrentDictionary agvLastRequestTime = +// new ConcurrentDictionary(); + +// private static TimeSpan offlineThreshold = TimeSpan.FromSeconds(4); +// private static Timer agvStatusCheckTimer; + +// public VDA5050WebApi() +// { + +// Post["uagv/v2/manufacturer/SN/connection"] = param => +// { +// var bodyStr = Request.Body.AsString(); +// var msg = JsonConvert.DeserializeObject(bodyStr); +// var car = (VDA5050Car)SimpleLib.GetAllCars() +// .FirstOrDefault(cc => cc is VDA5050Car vdaCar && vdaCar.SerialNumber == msg.serialNumber); +// if (car == null) +// { +// Diagnosis.Post($"no car of serialNumber {msg.serialNumber}"); +// return ""; +// } +// Console.WriteLine($"Connection status---------------------{msg.connectionState}"); +// car.ConnectionStatus = msg.connectionState; +// return ""; +// }; + +// Post["/registration"] = param => +// { +// var bodyStr = Request.Body.AsString(); +// var msg = JsonConvert.DeserializeObject(bodyStr); + +// var car = (VDA5050Car)SimpleLib.GetAllCars() +// .FirstOrDefault(cc => cc is VDA5050Car vdaCar && vdaCar.SerialNumber == msg.serialNumber); +// if (car == null) +// { +// Diagnosis.Post($"no car of serialNumber {msg.serialNumber}"); +// return ""; +// } + +// return ""; +// }; + +// Post["/vda5050/state"] = param => +// { + +// var bodyStr = Request.Body.AsString(); +// var msg = JsonConvert.DeserializeObject(bodyStr); + +// var car = (VDA5050Car)SimpleLib.GetAllCars() +// .FirstOrDefault(cc => cc is VDA5050Car vdaCar && vdaCar.SerialNumber == msg.serialNumber); +// if (car == null) +// { +// Diagnosis.Post($"no car of serialNumber {msg.serialNumber}"); +// return ""; +// } + +// Console.WriteLine($"Is the car Paused: {msg.paused} ------------------"); +// if (msg.orderUpdatedId == car.OrderUpdateId) car.UpdateState(msg); +// else Diagnosis.Post($"stateMessage orderUpdateId does not match!"); +// car.UpdateState(msg); // todo: what if orderUpdateId does not arrive in order? + +// return ""; +// }; + +// Post["/vda5050/visualization"] = param => +// { + +// var bodyStr = Request.Body.AsString(); +// var msg = JsonConvert.DeserializeObject(bodyStr); + +// var car = (VDA5050Car)SimpleLib.GetAllCars() +// .FirstOrDefault(cc => cc is VDA5050Car vdaCar && vdaCar.SerialNumber == msg.serialNumber); +// if (car == null) +// { +// Diagnosis.Post($"no car of serialNumber {msg.serialNumber}"); +// return ""; +// } + +// car.UpdatePosition(msg); + +// return ""; +// }; +// } + +// private static void CheckAgvStatus(object sender, ElapsedEventArgs e) +// { +// var cars = SimpleLib.GetAllCars() +// .OfType(); // Assuming SimpleLib provides a way to get all cars + +// foreach (var car in cars) +// { +// if (agvLastRequestTime.TryGetValue(car.SerialNumber, out DateTime lastRequestTime)) +// { +// // Check if the time since the last request exceeds the offline threshold +// if ((DateTime.Now - lastRequestTime) > offlineThreshold) +// { +// if (car.ConnectionStatus == "Online") +// { +// car.ConnectionStatus = "Connection Broken"; +// Diagnosis.Post($"AGV {car.SerialNumber} is now offline (no requests received)."); +// } +// } +// } +// else +// { +// // If there鈥檚 no record of the last request, the AGV is considered offline +// if (car.ConnectionStatus == "Online") +// { +// car.Online = false; +// Diagnosis.Post($"AGV {car.SerialNumber} is disconnected unexpectedly."); +// } +// } +// } +// } +// } +//} diff --git a/StandardScene.QrLidar/Cad/SyncQrMap.cs b/StandardScene.QrLidar/Cad/SyncQrMap.cs new file mode 100644 index 0000000..2e378ba --- /dev/null +++ b/StandardScene.QrLidar/Cad/SyncQrMap.cs @@ -0,0 +1,49 @@ +using System; +using System.Linq; +using System.Windows.Forms; +using Newtonsoft.Json; +using SimpleCore; +using SimpleLite.CADTools; +using SimpleLite.RCS.CarTypes; +using StandardScene; +using StandardScene.Model; + +namespace StandardScene.QrLidar.Cad +{ + /// + /// 鎵弿鍏ㄥ浘甯 tag 鐨勭珯鐐圭敓鎴愪簩缁寸爜鍦板浘锛堢爜鍊 鈫 鍧愭爣/瑙掑害锛夛紝鍐欏叆 + /// 渚 /api/QrMap 涓嬪彂锛屽苟閫氱煡甯 QrCar 鏍囪鐨勫湪绾胯溅杈嗗埛鏂般 + /// 鑷 Core StandardCADTool.cs 杩佸叆锛坰cene.qrlidar 骞冲彴涓撳睘鑳藉姏锛夈 + /// + [CADToolDescriptor(name = "鍚屾浜岀淮鐮佸湴鍥惧埌灏忚溅")] + public class SyncQrMap : CADTool + { + public override async void Invoke() + { + var tagSites = SimpleLib.GetAllSites().Where(p => p.fields.ContainsKey("tag")); + ApiController.QrMap.Clear(); + foreach (var tagSite in tagSites) + { + var tag = int.Parse(tagSite.fields["tag"]); + var tagX = tagSite.x; + var tagY = tagSite.y; + var tagTh = tagSite.fields.TryGetValue("th", out var field) ? float.Parse(field) : 0f; + if (ApiController.QrMap.ContainsKey(tag)) + { + MessageBox.Show(($@"瀛樺湪閲嶅鐮佸:{tag} 绔欑偣id涓:{tagSite.id}")); + return; + } + + ApiController.QrMap[tag] = (tagX, tagY, tagTh); + } + + ApiController.QrMapJson = JsonConvert.SerializeObject(ApiController.QrMap); + var qrCars = SimpleLib.GetAllCars().Where(p => p.fields.ContainsKey("QrCar")); + foreach (var qrCar in qrCars) + { + if(Commons.GetVehicleStatus((Car)qrCar) is VehicleStatus.Normal or VehicleStatus.NeedInit) + ((ClumsyCar)qrCar).ImmediateCommand("pilot.GetUpdateQrMap()"); + } + } + } +} diff --git a/StandardScene.QrLidar/CarTypes/ArmCar.cs b/StandardScene.QrLidar/CarTypes/ArmCar.cs new file mode 100644 index 0000000..8d3c0dd --- /dev/null +++ b/StandardScene.QrLidar/CarTypes/ArmCar.cs @@ -0,0 +1,261 @@ +using LessokajiWeaverUtilities.Utilities; +using SimpleLite.RCS; +using SimpleLite.RCS.CarTypes; +using SimpleLite.CADTools; +using SimpleLite.Props; +using SimpleLite.UI; +using SimpleCore; +using SimpleCore.Compiler; +using SimpleCore.Library; +using SimpleCore.PropType; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Drawing; +using System.Drawing.Drawing2D; +using System.Linq; +using System.Text; +using System.Threading; +using System.Threading.Tasks; + +namespace StandardScene.CarTypes +{ + class ArmCarSiteFields:KivaSiteFields + { + public bool putNeedTurn = false; + } + + class ArmCarTrackFields:KivaTrackFields + { + + } + + class ArmCarPlanFields:KivaPlanFields + { + public bool charge = false; + } + + // 鑷 VDA5050Car.cs 涓嬫矇鑷 Core锛欰rmCar 鐨 siteFields 寮曠敤瀹冿紙瑙佷笅鏂 [TemplateTrackCoderSettings]锛夈 + // VDA5050 鎷嗕负鐙珛 dll 鍚 ArmCar 鐣 Core锛屼笉鑳藉弽渚濊禆 VDA dll锛屾晠璇ュ瓧娈电被缃簬 Core銆 + class VDA5050SiteField : BasicSiteFields + { + public bool DI41 = false; + public int SleepTime = 0; + public bool workPlace = false; + } + //[TemplateTrackCoderSettings( + // priority = 1, + // templateString = "agv.BasicGo(${src.x},${src.y},${src.id},${dst.x},${dst.y},${dst.id}," + + // "${track.id},${track.Speed},${track.Reverse||(track.ReverseDst==dst.id)}," + + // "${track.CarDirectionBias},${track.EnableCarAbsoluteDirection},${track.CarAbsoluteDirection},${track.typeInfo});", + // siteFields = typeof(KivaSiteFields), + // trackFields = typeof(KivaTrackFields), + // planFields = typeof(KivaPlanFields))] + //[TemplateSiteCoderSettings( + // priority = 5, + // useVerb = "plan.action=='fetch'&&plan.curSeg==plan.segN-1", + // blockVerb = "true", + // templateString = "agv.Wait();agv.Fetch(\"${dst.name}\");agv.Wait();", + // siteFields = typeof(ArmCarSiteFields), + // trackFields = typeof(ArmCarTrackFields), + // planFields = typeof(ArmCarPlanFields))] + //[TemplateSiteCoderSettings( + // priority = 5, + // useVerb = "plan.action=='put'&&plan.curSeg==plan.segN-1", + // blockVerb = "true", + // templateString = "agv.Wait();agv.Put(${dst.putNeedTurn},\"${dst.name}\");agv.Wait();", + // siteFields = typeof(ArmCarSiteFields), + // trackFields = typeof(ArmCarTrackFields), + // planFields = typeof(ArmCarPlanFields))] + //[TemplateSiteCoderSettings( + // priority = 5, + // useVerb = "plan.action=='putEmpty'&&plan.curSeg==plan.segN-1", + // blockVerb = "true", + // templateString = "agv.Wait();agv.PutEmpty(\"${dst.name}\");agv.Wait();", + // siteFields = typeof(ArmCarSiteFields), + // trackFields = typeof(ArmCarTrackFields), + // planFields = typeof(ArmCarPlanFields))] + [TemplateSiteCoderSettings( + priority = 5, + useVerb = "plan.action=='pickFull'&&plan.curSeg==plan.segN-1", + blockVerb = "true", + templateString = "agv.Wait();agv.PickFull(\"${dst.name}\");agv.Wait();", + siteFields = typeof(ArmCarSiteFields), + trackFields = typeof(ArmCarTrackFields), + planFields = typeof(ArmCarPlanFields))] + [TemplateSiteCoderSettings( + priority = 5, + useVerb = "plan.action=='pickEmpty'&&plan.curSeg==plan.segN-1", + blockVerb = "true", + templateString = "agv.Wait();agv.PickEmpty(\"${dst.name}\");agv.Wait();", + siteFields = typeof(ArmCarSiteFields), + trackFields = typeof(ArmCarTrackFields), + planFields = typeof(ArmCarPlanFields))] + [TemplateSiteCoderSettings( + priority = 5, + useVerb = "plan.action=='putFull'&&plan.curSeg==plan.segN-1", + blockVerb = "true", + templateString = "agv.Wait();agv.PutFull(\"${dst.name}\");agv.Wait();", + siteFields = typeof(ArmCarSiteFields), + trackFields = typeof(ArmCarTrackFields), + planFields = typeof(ArmCarPlanFields))] + [TemplateSiteCoderSettings( + priority = 5, + useVerb = "plan.action=='putEmpty'&&plan.curSeg==plan.segN-1", + blockVerb = "true", + templateString = "agv.Wait();agv.PutEmpty(\"${dst.name}\");agv.Wait();", + siteFields = typeof(ArmCarSiteFields), + trackFields = typeof(ArmCarTrackFields), + planFields = typeof(ArmCarPlanFields))] + [TemplateSiteCoderSettings( + priority = 5, + useVerb = "plan.action=='MoveArm'&&plan.curSeg==plan.segN-1", + blockVerb = "true", + templateString = "agv.Wait();agv.MoveArm(${dst.MoveDirection});agv.Wait();", + siteFields = typeof(ArmCarSiteFields), + trackFields = typeof(ArmCarTrackFields), + planFields = typeof(ArmCarPlanFields))] + [TemplateSiteCoderSettings( + priority = 5, + useVerb = "dst.name=='charge'", + templateString = "agv.Wait();agv.ControlChargePort(true);agv.Wait();", + siteFields = typeof(ArmCarSiteFields), + trackFields = typeof(ArmCarTrackFields), + planFields = typeof(ArmCarPlanFields))] + + [TemplateSiteCoderSettings( + priority = 5, + useVerb = "dst.name=='charge'&&plan.curSeg==0", + templateString = "agv.Wait();agv.ControlChargePort(false);", + siteFields = typeof(VDA5050SiteField))] + + [CarType(Name = "ArmCar")] + [I18N.DocumentTranslation(Name = "ArmCar", locale = "en")] + public class ArmCar : GhostCar + { + public static async Task Create() + { + var car = new ArmCar() + { + lstatus = "杩炴帴涓", + address = "127.0.0.1", + name = $"Kiva", + haveCoordination = true, + speed = 0.3f, + }; + return car; + } + private const float DrawWidth = 950, DrawLength = 650; + private readonly Pen orientPen = new Pen(Color.White, 3); + private bool twinkle = false; + protected override void draw(Graphics eGraphics) + { + try + { + float scale = 1; + var lineCap = new AdjustableArrowCap(5, 5, true); + orientPen.CustomEndCap = lineCap; + orientPen.StartCap = LineCap.RoundAnchor; + var alarmLevel = Commons.GetCarStatus(this, "AlarmLevel"); + var soc = Commons.CarValue((Car)this, "Soc"); + var electricCurrent = Commons.CarValue((Car)this, "ElectricCurrent"); + var thresSpeed = Commons.CarValue(this, "ThresSpeed"); + LadderLogic.FlipFlop(ref twinkle, 500, true, false); + Rectangle destRect = new Rectangle( + (int)((int)(-DrawWidth / 2) * scale), + (int)((int)(-DrawLength / 2) * scale), + (int)((int)DrawWidth * scale), + (int)((int)DrawLength * scale) + ); + if (int.TryParse(alarmLevel, out var alarm) && alarm > 0) + { + eGraphics.FillRectangle(twinkle ? Brushes.DarkRed : Brushes.Green, destRect); + } + else if (alarmLevel == "-1" || this.siteID == -1) + eGraphics.FillRectangle(Brushes.LightSeaGreen, destRect); + else if (electricCurrent > 1) + eGraphics.FillRectangle(Brushes.YellowGreen, destRect); + else if (thresSpeed <= 0) + { + eGraphics.FillRectangle(twinkle ? Brushes.YellowGreen : Brushes.Green, destRect); + } + else if (soc < 25) + { + eGraphics.FillRectangle(twinkle ? Brushes.DarkRed : Brushes.LightSeaGreen, destRect); + } + else + eGraphics.FillRectangle(Brushes.Green, destRect); + + eGraphics.DrawEllipse(Pens.Yellow, -160, -160, 320, 320); + eGraphics.DrawLine(orientPen, 0, 0, 480, 0); + + } + catch (Exception e) + { + Diagnosis.Post("缁樺埗灏忚溅寮傚父" + ExceptionFormatter.FormatEx(e), "缁樺埗灏忚溅寮傚父", true); + } + } + + + public override string SetDisplayInfo() + { + try + { + var str = $"{name}({id})\n"; + status.enums.TryGetValue("ChassisMode", out var manual); + var ms = manual == "0" ? "鑷姩" : "鎵嬪姩"; + status.enums.TryGetValue("Soc", out var soc); + str = $"{str}{ms}|soc:{soc}"; + var alarmStr = Commons.GetCarStatus(this, "AlarmInfo"); + if (alarmStr != "") + str = $"{str}|{alarmStr}"; + //if (status.enums.TryGetValue("mAlarm", out var mAlarm) && mAlarm != "") + // str = $"{str}|M:{mAlarm}"; + //var hasC = status.enums.TryGetValue("cAlarm", out var cAlarm) && cAlarm != ""; + //if (mAlarm != "" && hasC) + // str = $"{str}|"; + //if (hasC) + // str = $"{str}C:{cAlarm}"; + //var L_step = status.enums.TryGetValue("ls", out var ls) && ls != ""; + //if (L_step) + //{ + // str = $"{str}|ls:{ls}"; + //} + if (status.enums.TryGetValue("ElectricCurrent", out var electricCurrent) && + !string.IsNullOrEmpty(electricCurrent)) + { + var charging = float.Parse(electricCurrent) * 0.01f > 0 ? "鍏呯數涓" : "鏈厖鐢"; + str = $"{str}|charge:{charging}"; + } + + var thresSpeed = Commons.CarValue(this, "ThresSpeed"); + str = $"{str}{((thresSpeed <= 0) ? "|閬块殰瑙﹀彂" : "")}"; + return str; + } + catch (Exception e) + { + Console.WriteLine(e); + return "bad car"; + } + } + + [MethodMember(Name = "杩涘叆灏忚溅杩滅▼", Description = "杩涘叆灏忚溅杩滅▼妗岄潰")] + [I18N.DocumentTranslation(Name = "Open remote desktop", Description = "Open the car's remote desktop", locale = "en")] + public void Mstsc() + { + var ip = this.address; + // 鍚姩mstsc骞朵紶閫扞P鍦板潃 + Process.Start( + new ProcessStartInfo + { + FileName = "mstsc", + Arguments = $"/v:{ip}", + UseShellExecute = false, + CreateNoWindow = true + } + ); + } + + + } +} diff --git a/StandardScene.QrLidar/CarTypes/DualLiftingCar.cs b/StandardScene.QrLidar/CarTypes/DualLiftingCar.cs new file mode 100644 index 0000000..8bdaa1e --- /dev/null +++ b/StandardScene.QrLidar/CarTypes/DualLiftingCar.cs @@ -0,0 +1,105 @@ +using LessokajiWeaverUtilities.Utilities; +using Newtonsoft.Json; +using SimpleLite.RCS; +using SimpleLite.RCS.CarTypes; +using SimpleCore; +using SimpleCore.Compiler; +using SimpleCore.PropType; +using System; +using System.Diagnostics; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using System.Windows.Forms; + +namespace StandardScene.CarTypes +{ + [CarType(Name = "閿傜數鍙屼妇鍗")] + [I18N.DocumentTranslation(Name = "Dual Lift Car", locale = "en")] + public class DualLiftingCar : GhostCar + { + public static async Task Create() + { + var car = new DualLiftingCar() + { + lstatus = "杩炴帴涓", + address = "127.0.0.1", + name = "閿傜數鍙屼妇鍗" + }; + return car; + } + public override string SetDisplayInfo() + { + try + { + var str = $"{name}({id})\n"; + status.enums.TryGetValue("ChassisMode", out var manual); + var ms = manual == "0" ? "鑷姩" : "鎵嬪姩"; + status.enums.TryGetValue("Soc", out var soc); + str = $"{str}{ms}|soc:{soc}"; + var alarmStr = Commons.GetCarStatus(this, "AlarmInfo"); + if (alarmStr != "") + str = $"{str}|{alarmStr}"; + if (status.enums.TryGetValue("ElectricCurrent", out var electricCurrent) && + !string.IsNullOrEmpty(electricCurrent)) + { + var charging = float.Parse(electricCurrent) * 0.01f > 0 ? "鍏呯數涓" : "鏈厖鐢"; + str = $"{str}|charge:{charging}"; + } + + var thresSpeed = Commons.CarValue(this, "ThresSpeed"); + str = $"{str}{((thresSpeed <= 0) ? "|閬块殰瑙﹀彂" : "")}"; + return str; + } + catch (Exception e) + { + Console.WriteLine(e); + return "bad car"; + } + } + + [MethodMember(Name = "杩涘叆灏忚溅杩滅▼", Description = "杩涘叆灏忚溅杩滅▼妗岄潰")] + [I18N.DocumentTranslation(Name = "Open remote desktop", Description = "Open the car's remote desktop", locale = "en")] + public void Mstsc() + { + var ip = this.address; + // 鍚姩mstsc骞朵紶閫扞P鍦板潃 + Process.Start( + new ProcessStartInfo + { + FileName = "mstsc", + Arguments = $"/v:{ip}", + UseShellExecute = false, + CreateNoWindow = true + } + ); + } + + private bool _running; + [MethodMember(Name = "娴嬭瘯")] + [I18N.DocumentTranslation(Name = "Start test", locale = "en")] + public async void Test() + { + _running = true; + var siteA = SimpleLib.GetAllSites().First(s => s.name == "A"); + var siteB = SimpleLib.GetAllSites().First(s => s.name == "B"); + while (_running) + { + var planA = new SegmentPlan() { usingCar = this }; + planA.FindRoute(SimpleLib.GetSite(GetLastSite()), siteA); + await planA.Compile("A").Queue(); + var planB = new SegmentPlan(){usingCar = this }; + planB.FindRoute(siteA, siteB); + await planB.Compile("B").Queue(); + Thread.Sleep(1000); + } + } + + [MethodMember(Name = "鍋滄娴嬭瘯")] + [I18N.DocumentTranslation(Name = "Stop test", locale = "en")] + public void Stop() + { + _running = false; + } + } +} diff --git a/StandardScene.QrLidar/CarTypes/Forklift.cs b/StandardScene.QrLidar/CarTypes/Forklift.cs new file mode 100644 index 0000000..36511d1 --- /dev/null +++ b/StandardScene.QrLidar/CarTypes/Forklift.cs @@ -0,0 +1,435 @@ +锘縰sing LessokajiWeaverUtilities.Utilities; +using SimpleLite; +using SimpleLite.RCS; +using SimpleLite.RCS.CarTypes; +using SimpleLite.CADTools; +using SimpleLite.Props; +using SimpleLite.UI; +using SimpleCore; +using SimpleCore.Compiler; +using SimpleCore.Library; +using SimpleCore.PropType; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Drawing; +using System.Drawing.Drawing2D; +using System.Linq; +using System.Net.Http; +using System.Numerics; +using System.Threading.Tasks; +using StandardScene.Model; +using StandardScene.Coders; + +namespace StandardScene.CarTypes +{ + class ForkliftSiteFields : BasicSiteFields + { + public int AngleTarget = 0; + public bool Turn = false; + + public float FetchSpeed = 0; + public int FetchLidarArea = -2; + public int FetchIOArea = -1; + public float FetchBlindMoveDist = 0; + public float FetchLiftDownTarget = -1; + public float FetchLiftUpTarget = -1; + public bool FetchUseDetector = false; + public int FetchDetector = 0; + public float FetchDetectWidth = -1; + public float FetchDetectDepth = -1; + public bool FetchLeaveSrcEarly = false; + public float FetchShieldObstacleDist = -1; + + public float PutSpeed = 0; + public int PutLidarArea = -1; + public int PutIOArea = -1; + public float PutBlindMoveDist = 0; + public float PutLiftDownTarget = -1; + public float PutLiftUpTarget = -1; + public bool PutUseDetector = false; + public int PutDetector = 0; + public float PutDetectWidth = -1; + public float PutDetectDepth = -1; + public bool PutLeaveSrcEarly = false; + public float PutShieldObstacleDist = -1; + + public float LeaveShelfSpeed = 0; + public int LeaveShelfLidarArea = -1; + public int LeaveShelfIOArea = -1; + public float LeaveShelfBlindMoveDist = 0; + public float LeaveShelfLiftDownTarget = -1; + public float LeaveShelfRecoveryObstacleDist = -1; + } + + class ForkliftTrackFields : BasicTrackFields + { + public int ManeuverDir = 0; + public int ForwardDst = 0; + public int ForwardObChooseDst = -2; + public float BlindMoveDist = 0; + public bool UseDetector = false; + public int DetectorMode = -1; + public float DetectWidth = -1; + public float DetectDepth = -1; + public bool LeaveSrcEarly = false; + public float ShieldObstacleDist = -1; + } + + class ForkliftPlanFields : BasicPlanFields + { + public bool reverse = false; + public int level = 0; + } + + + // 閫氱敤閬块殰/IO coder 宸叉娊绂讳负 StandardScene.Coders.*锛堣绫诲墠 [ProgramTrackCoderSettings] 寮曠敤锛 + + // 閬块殰鍖哄昂瀵(2鍙)鍒囨崲 coder 宸叉娊绂讳负 StandardScene.Coders.AvoidanceParamLWCoder锛堟柟妗 B锛涜涓嬫柟 [ProgramTrackCoderSettings] 寮曠敤锛 + [ProgramTrackCoderSettings(priority = 20, program = typeof(AvoidanceParamLWCoder))] + + //plan.SegN 鏄寚 褰撳墠璺緞鎵鏈夌偣绾跨殑鏁伴噺 + //plan.SegN-2 灏辨槸缁堢偣鍊掓暟绗簩涓珯鐐 + [TemplateTrackCoderSettings( + priority = 20, + useVerb = "plan.action=='fetch' && dst.Shelf", + templateString = "agv.Wait();" + + "agv.Fetch(${src.x},${src.y},${src.id},${dst.x},${dst.y},${dst.id}," + + "${dst.FetchSpeed},${dst.FetchLidarArea},${dst.FetchIOArea}," + + "${dst.FetchBlindMoveDist},${dst.FetchLiftDownTarget},${dst.FetchLiftUpTarget}," + + "${dst.FetchUseDetector},${dst.FetchDetector},${dst.FetchDetectWidth},${dst.FetchDetectDepth}," + + "${plan.CarLength},${plan.CarWidth},${dst.FetchLeaveSrcEarly},${dst.FetchShieldObstacleDist});" + + "agv.Wait();", + blockVerb = "true", + siteFields = typeof(ForkliftSiteFields), + trackFields = typeof(ForkliftTrackFields), + planFields = typeof(ForkliftPlanFields))] + + [TemplateTrackCoderSettings( + priority = 20, + useVerb = "plan.action=='put' && dst.Shelf", + templateString = "agv.Wait();" + + "agv.Put(${src.x},${src.y},${src.id},${dst.x},${dst.y},${dst.id}," + + "${dst.PutSpeed},${dst.PutLidarArea},${dst.PutIOArea}," + + "${dst.PutBlindMoveDist},${dst.PutLiftDownTarget},${dst.PutLiftUpTarget}," + + "${dst.PutUseDetector},${dst.PutDetector}," + + "${dst.PutDetectWidth},${dst.PutDetectDepth},${dst.PutLeaveSrcEarly},${dst.PutShieldObstacleDist});" + + "agv.Wait();", + blockVerb = "true", + siteFields = typeof(ForkliftSiteFields), + trackFields = typeof(ForkliftTrackFields), + planFields = typeof(ForkliftPlanFields))] + + [TemplateTrackCoderSettings( + priority = 22, + useVerb = "src.Shelf", + templateString = "agv.LeaveShelf(${src.x},${src.y},${src.id},${dst.x},${dst.y},${dst.id}," + + "${src.LeaveShelfSpeed},${src.LeaveShelfLidarArea},${src.LeaveShelfIOArea}," + + "${src.LeaveShelfBlindMoveDist},${src.LeaveShelfLiftDownTarget},${src.LeaveShelfRecoveryObstacleDist},);" + + "agv.Wait();", + blockVerb = "true", + siteFields = typeof(ForkliftSiteFields), + trackFields = typeof(ForkliftTrackFields), + planFields = typeof(ForkliftPlanFields))] + + [TemplateSiteCoderSettings( + priority = 25, + useVerb = "plan.action=='fetch'&&plan.segN==1&&dst.Shelf", + templateString = + "agv.FetchInPlace(${dst.FetchLiftUpTarget},${plan.CarLength},${plan.CarWidth});" + + "agv.Wait();", + blockVerb = "true", + siteFields = typeof(ForkliftSiteFields), + trackFields = typeof(ForkliftTrackFields), + planFields = typeof(ForkliftPlanFields))] + // 绾犲亸闃堝 coder 宸叉娊绂讳负 StandardScene.Coders.TrackingErrThreshCoder + + [ProgramTrackCoderSettings(priority = 20, program = typeof(LidarAreaSwitchCoder))] + [ProgramTrackCoderSettings(priority = 20, program = typeof(IoAreaSwitchCoder))] + [ProgramTrackCoderSettings(priority = 20, program = typeof(TrackingErrThreshCoder))] + [CarType(Name = "鍙夎溅", editor = typeof(Forklift))] + [I18N.DocumentTranslation(Name = "ForkLift", locale = "en")] + [EnvelopConfig(centerX = -900, centerY = 0, lengthX = 3000, lengthY = 1500)] + public class Forklift : GhostCar + { + public static async Task Create() // boilerplate + { + var fl = new Forklift() + { + lstatus = "杩炴帴涓", + address = "127.0.0.1", + name = $"鍙夎溅", + haveCoordination = true, + speed = 1, + }; + + return fl; + } + + private const float DrawWidth = 1100, DrawLength = 600, DrawForkLen = 1532; + private readonly Pen _orientPen = new Pen(Color.White, 3); + + protected override void draw(Graphics eGraphics) + { + var lineCap = new AdjustableArrowCap(6, 6, true); + _orientPen.CustomEndCap = lineCap; + _orientPen.StartCap = LineCap.RoundAnchor; + + eGraphics.FillRectangle(Brushes.DimGray, -DrawLength / 2, -DrawWidth / 2, DrawForkLen, 200); + eGraphics.FillRectangle(Brushes.DimGray, -DrawLength / 2, DrawWidth / 2 - 200, DrawForkLen, 200); + eGraphics.FillRectangle(Brushes.Gray, DrawForkLen - DrawLength / 2, -DrawWidth / 2, DrawLength, DrawWidth); + + eGraphics.DrawEllipse(Pens.White, -160, -160, 320, 320); + eGraphics.DrawLine(_orientPen, 0, 0, 480, 0); + } + [MethodMember(Name = "杩涘叆灏忚溅杩滅▼", Description = "杩涘叆灏忚溅杩滅▼妗岄潰")] + [I18N.DocumentTranslation(Name = "Open remote desktop", Description = "Open the car's remote desktop", locale = "en")] + public void Mstsc() + { + var ip = this.address; + // 鍚姩mstsc骞朵紶閫扞P鍦板潃 + Process.Start( + new ProcessStartInfo + { + FileName = "mstsc", + Arguments = $"/v:{ip}", + UseShellExecute = false, + CreateNoWindow = true + } + ); + } + public override string SetDisplayInfo() + { + try + { + var str = $"{name}({id})\n"; + status.enums.TryGetValue("ChassisMode", out var manual); + var ms = manual == "0" ? "鑷姩" : "鎵嬪姩"; + status.enums.TryGetValue("Soc", out var soc); + str = $"{str}{ms}|soc:{soc}"; + var alarmStr = Commons.GetCarStatus(this, "AlarmInfo"); + if (alarmStr != "") + str = $"{str}|{alarmStr}"; + if (status.enums.TryGetValue("ElectricCurrent", out var electricCurrent) && + !string.IsNullOrEmpty(electricCurrent)) + { + var charging = float.Parse(electricCurrent) * 0.01f > 0 ? "鍏呯數涓" : "鏈厖鐢"; + str = $"{str}|charge:{charging}"; + } + + var thresSpeed = Commons.CarValue(this, "ThresSpeed"); + str = $"{str}{((thresSpeed <= 0) ? "|閬块殰瑙﹀彂" : "")}"; + return str; + } + catch (Exception e) + { + Console.WriteLine(e); + return "bad car"; + } + } + + HttpClient hc = new HttpClient() { Timeout = TimeSpan.FromMilliseconds(300) }; + [MethodMember(Name = "灏忚溅鏆傚仠", Description = "灏忚溅鏆傚仠")] + [I18N.DocumentTranslation(Name = "EmergencyStop", Description = "EmergencyStop", locale = "en")] + public void EmergencyStop(string reason) + { + Diagnosis.Post($"car{name}:EmergencyStop"); + hc.GetStringAsync($"http://{address}:8008/setValue?FieldName=ISRelease&Value=1"); + } + + [MethodMember(Name = "灏忚溅鏆傚仠鎭㈠", Description = "灏忚溅鏆傚仠鎭㈠")] + [I18N.DocumentTranslation(Name = "EmergencyRelease", Description = "EmergencyRelease", locale = "en")] + public void EmergencyRelease() + { + Diagnosis.Post($"car{name}:EmergencyStop release"); + hc.GetStringAsync($"http://{address}:8008/setValue?FieldName=ISRelease&Value=0"); + } + [MethodMember(Name = "灏忚溅閲嶅惎C", Description = "灏忚溅閲嶅惎Clumsy")] + [I18N.DocumentTranslation(Name = "ResetClumsy", Description = "ResetClumsy", locale = "en")] + public void ResetClumsy() + { + Diagnosis.Post($"car{name}-{address}:ResetClumsy"); + hc.GetStringAsync($"http://{address}:8008/reset"); + } + + [MethodMember(Name = "鏄剧ず杞﹁締鐩戞帶", Description = "鎵撳紑杞﹁締鐘舵佺洃鎺х獥鍙")] + public void ShowVehicleMonitor() + { + VehicleMonitor.ShowMonitor(); + } + public void newReset(int resetId = 0) + { + //if (!status.programs.task.IsCompleted && + // MessageBox.Show($"灏忚溅{name}({id})褰撳墠鏈変换鍔★細{status.programs.printStatus()}锛岀‘璁ゅ垵濮嬪寲锛", "纭", + // MessageBoxButtons.YesNo) == DialogResult.No) return; + try + { + if (GetLastSite() != -1 && Commons.GetVehicleStatus((Car)this) != VehicleStatus.NeedInit) + { + Diagnosis.Post($"Car{id}鍒濆鍖栧け璐ワ細宸插垵濮嬪寲鎴栬溅杈嗕笉鍦ㄧ嚎"); + return; + } + var debug = "ui invoke reset. "; + + // todo: 鎵剧偣鍜岀嚎娈电殑鏈杩戠偣銆傘傘 + // lock + UISite site1 = null; + float dist = float.MaxValue; + List<(float, Site, Site)> trackList = new List<(float, Site, Site)>(); + if (resetId == 0) + { + + (float bias, Vector2 hPnt, float fd) Project2DLine( + Vector2 pnt, + Vector2 segSt, + Vector2 segEnd + ) + { + var dir = Vector2.Normalize(segEnd - segSt); + var fd = Vector2.Dot(pnt - segSt, dir); + var hPnt = segSt + fd * dir; + var bias = dir.X * (pnt.Y - segSt.Y) - (pnt.X - segSt.X) * dir.Y; + return (bias, hPnt, fd); + } + + foreach (var allTrack in SimpleLib.GetAllTracks()) + { + var s = SimpleLib.GetSite(allTrack.siteA); + var e = SimpleLib.GetSite(allTrack.siteB); + var (b, h, fd) = Project2DLine( + new Vector2(x, y), + new Vector2(s.x, s.y), + new Vector2(e.x, e.y) + ); + b = Math.Abs(b); + if (fd < 0 || fd > 1) + continue; + if (dist > b) + { + UISite ss; + if (fd < 0.5) + ss = (UISite)s; + else + ss = (UISite)e; + + foreach (var ccar in SimpleLib.GetAllCars()) + { + if (ccar == this) + continue; + if (ccar.status.holdingLocks.Contains(s.id)) + goto next; + } + + dist = b; + site1 = ss; + next: + ; + } + } + float radius = 5000; + //鏍规嵁灏忚溅涓哄渾蹇冩壘鍒板崐寰剅adius鍐呯殑绔欑偣 + var surroundSites = SimpleLib.GetAllSites().ToList().FindAll(p => LessMath.dist(x, y, p.x, p.y) <= radius); + foreach (var allTrack in SimpleLib.GetAllTracks()) + { + var s = SimpleLib.GetSite(allTrack.siteA); + var e = SimpleLib.GetSite(allTrack.siteB); + if (!surroundSites.Contains(s) && !surroundSites.Contains(e) && surroundSites.Count != 0) continue; + + var (b, h, fd) = Project2DLine(new Vector2(x, y), new Vector2(s.x, s.y), new Vector2(e.x, e.y)); + b = Math.Abs(b); + double pathAngle = Double.NaN; + if (allTrack.direction == 1) + pathAngle = Math.Atan2((e.y - s.y), (e.x - s.x)) / Math.PI * 180; + else if (allTrack.direction == 2) + pathAngle = Math.Atan2((e.y - s.y), (e.x - s.x)) / Math.PI * 180 + 180; + else + trackList.Add((b, s, e)); + if (Math.Abs(this.th - pathAngle) < 30) + trackList.Add((b, s, e)); + foreach (var ccar in SimpleLib.GetAllCars()) + { + if (ccar == this) continue; + if (ccar.status.holdingLocks.Contains(s.id)) goto next; + } + next:; + + } + //var sites = trackList.OrderBy(p => p.Item1).ToArray(); + List<(double, Site)> distList = new List<(double, Site)>(); + foreach (var site in trackList) + { + if (site.Item2.fields.ContainsKey("no_reset") || site.Item3.fields.ContainsKey("no_reset")) + continue; + foreach (var ccar in SimpleLib.GetAllCars()) + { + if (ccar == this) continue; + if (ccar.status.holdingLocks.Contains(site.Item2.id) || ccar.status.holdingLocks.Contains(site.Item3.id)) goto next; + } + distList.Add((LessMath.dist(site.Item2.x, site.Item2.y, x, y), site.Item2)); + distList.Add((LessMath.dist(site.Item3.x, site.Item3.y, x, y), site.Item3)); + next:; + } + + site1 = (UISite)distList.OrderBy(p => p.Item1).First().Item2; + + //foreach (var site in SimpleLib.GetAllSites()) + //{ + // if (site.fields.ContainsKey("no_reset")) + // continue; + // foreach (var ccar in SimpleLib.GetAllCars()) + // { + // if (ccar == this) + // continue; + // if (ccar.status.holdingLocks.Contains(site.id)) + // goto next; + // } + + // var d = LessMath.dist(site.x, site.y, x, y); + // if (d < dist) + // { + // dist = (float)d; + // site1 = (UISite)site; + // } + // next: + // ; + //} + TrafficReset(site1, true, strict: false); + } + else + { + TrafficReset(SimpleLib.GetSite(resetId), true, strict: false); + } + + + siteID = site1.id; + + lstatus = "涓婄嚎"; + debug += $"site={site1.id}"; + AppendDebug(debug); + G.pushStatus($"Car {name}({id}) reset to Site {site1.id}"); + } + catch (Exception e) + { + Diagnosis.Post($"{this.name}---鍒濆鍖栧け璐--{e.Message}", $"{this.name}", true); + } + } + + [MethodMember(Name = "娴嬭瘯", Description = "寰幆")] + [I18N.DocumentTranslation(Name = "Test", Description = "Loop", locale = "en")] + public async void Test() + { + var siteA = SimpleLib.GetAllSites().First(s => s.name == "A"); + var siteB = SimpleLib.GetAllSites().First(s => s.name == "B"); + while (true) + { + var plan = new SegmentPlan() { usingCar = this }; + plan.FindRoute(SimpleLib.GetSite(this.GetLastSite()), siteA); + await plan.Compile("GoA").Queue(); + var planB = new SegmentPlan() { usingCar = this }; + planB.FindRoute(SimpleLib.GetSite(this.GetLastSite()), siteB); + await planB.Compile("GoB").Queue(); + await Task.Delay(1000); + } + } + } +} diff --git a/StandardScene.QrLidar/CarTypes/MultiVehicleCar.cs b/StandardScene.QrLidar/CarTypes/MultiVehicleCar.cs new file mode 100644 index 0000000..353ba6c --- /dev/null +++ b/StandardScene.QrLidar/CarTypes/MultiVehicleCar.cs @@ -0,0 +1,255 @@ +锘縰sing System.Drawing; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; + +using SimpleLite.RCS; + +using SimpleLite.RCS.CarTypes; +using SimpleCore; +using SimpleCore.Compiler; +using SimpleCore.Library; +using SimpleCore.PropType; +using StandardScene.Coders; + +namespace StandardScene.CarTypes +{ + //閽昏溅 + [TemplateTrackCoderSettings( + priority = 18, + useVerb = "(dst.name=='FetchSite1'||dst.name=='FetchSite2')&&(plan.action=='fetch')", + templateString = "agv.Wait();agv.TireFollowing(${plan.TireNum},${plan.FrontLidarDetect},${src.id},${dst.id},${plan.FirstTire});agv.Wait();", + blockVerb = "true", + siteFields = typeof(MultiWheelLifterSiteFields), + trackFields = typeof(MultiWheelLifterTrackFields), + planFields = typeof(MultiWheelLifterPlanFields))] + + [TemplateTrackCoderSettings( + priority = 18, + useVerb = "(dst.name=='PutSite1'||dst.name=='PutSite2')&&(plan.action=='leave')", + templateString = "agv.Wait();agv.TireFollowing(${plan.TireNum},${plan.FrontLidarDetect},${src.id},${dst.id},${plan.FirstTire});agv.Wait();", + blockVerb = "true", + siteFields = typeof(MultiWheelLifterSiteFields), + trackFields = typeof(MultiWheelLifterTrackFields), + planFields = typeof(MultiWheelLifterPlanFields))] + + //绂诲紑鏃堕捇杞(鍚庡嚭鐨勯偅鍙拌溅) + [TemplateTrackCoderSettings( + priority = 18, + useVerb = "dst.name=='ReadyFetchSite'&&plan.action=='leave'&&plan.Reverse==true", + templateString = "agv.Wait();agv.TireFollowing(${plan.TireNum},${plan.FrontLidarDetect},${src.id},${dst.id},${plan.FirstTire});agv.Wait();" + + "agv.LineTracking(${src.id},${dst.id},${plan.LineDistance});agv.Wait()", + blockVerb = "true", + siteFields = typeof(MultiWheelLifterSiteFields), + trackFields = typeof(MultiWheelLifterTrackFields), + planFields = typeof(MultiWheelLifterPlanFields))] + + //绂诲紑鏃剁洿绾胯璧(鍏堝嚭鐨勯偅鍙拌溅) + [TemplateTrackCoderSettings( + priority = 18, + useVerb = "(dst.name=='LeaveSite'||dst.name=='ReadyFetchSite')&&plan.action=='leave'&&plan.Reverse==false", + templateString = "agv.Wait();agv.LineTracking(${src.id},${dst.id},${plan.LineDistance});agv.Wait();", + blockVerb = "true", + siteFields = typeof(MultiWheelLifterSiteFields), + trackFields = typeof(MultiWheelLifterTrackFields), + planFields = typeof(MultiWheelLifterPlanFields))] + + //涓嬩娇鑳+澶规姳+涓婁娇鑳 + [TemplateSiteCoderSettings( + priority = 18, + useVerb = "((dst.name=='FetchSite1'||dst.name=='FetchSite2')&&plan.action=='clamp')", + templateString = "agv.Wait();agv.DriverDisable();agv.Wait();agv.ClamptoTarget(${dst.ClampClose});agv.Wait();agv.DriverAble();", + blockVerb = "true", + siteFields = typeof(MultiWheelLifterSiteFields), + trackFields = typeof(MultiWheelLifterTrackFields), + planFields = typeof(MultiWheelLifterPlanFields))] + + + //鎶卞す鎵撳紑 + [TemplateSiteCoderSettings( + priority = 18, + useVerb = "(dst.name=='PutSite1'||dst.name=='PutSite2')&&plan.action=='put'", + templateString = "agv.Wait();agv.DriverDisable();agv.Wait();agv.ClamptoTarget(${dst.ClampClose});agv.Wait();agv.DriverAble();", + blockVerb = "true", + siteFields = typeof(MultiWheelLifterSiteFields), + trackFields = typeof(MultiWheelLifterTrackFields), + planFields = typeof(MultiWheelLifterPlanFields))] + + + //涓存椂澶规姳鍔ㄤ綔 + [TemplateSiteCoderSettings( + priority = 18, + useVerb = "plan.action=='templateclamp'||plan.action=='templateput'", + templateString = "agv.Wait();agv.DriverDisable();agv.Wait();agv.ClamptoTarget(${plan.ClampClose});agv.Wait();agv.DriverAble();", + blockVerb = "true", + siteFields = typeof(MultiWheelLifterSiteFields), + trackFields = typeof(MultiWheelLifterTrackFields), + planFields = typeof(MultiWheelLifterPlanFields))] + + [TemplateTrackCoderSettings( + priority = 2, + useVerb = "plan.action=='fetch'", + templateString = "agv.BasicGo(${src.x},${src.y},${src.id},${dst.x},${dst.y},${dst.id}," + + "${track.id},${track.Speed},${track.Reverse||plan.Reverse}," + + "${track.CarDirectionBias},${track.EnableCarAbsoluteDirection},${track.CarAbsoluteDirection},${track.MagnetChoose},${track.MultiVehicleSync},${track.typeInfo});", + blockVerb = "true", + siteFields = typeof(MultiWheelLifterSiteFields), + trackFields = typeof(MultiWheelLifterTrackFields), + planFields = typeof(MultiWheelLifterPlanFields))] + + //[TemplateTrackCoderSettings( + // priority = 3, + // useVerb = "plan.action=='leave'&&(dst.name=='KeepDirectionSite1'||dst.name=='KeepDirectionSite2')", + // templateString = "agv.BasicGo(${src.x},${src.y},${src.id},${dst.x},${dst.y},${dst.id}," + + // "${track.id},${track.Speed},${track.Reverse||plan.Reverse}," + + // "${track.CarDirectionBias},${track.EnableCarAbsoluteDirection},${track.CarAbsoluteDirection},${track.MagnetChoose},${track.MultiVehicleSync},${track.typeInfo});", + // blockVerb = "true", + // siteFields = typeof(MultiWheelLifterSiteFields), + // trackFields = typeof(MultiWheelLifterTrackFields), + // planFields = typeof(MultiWheelLifterPlanFields))] + + [TemplateTrackCoderSettings( + priority = 3, + useVerb = "plan.action=='leave'", + templateString = "agv.BasicGo(${src.x},${src.y},${src.id},${dst.x},${dst.y},${dst.id}," + + "${track.id},${track.Speed},${track.Reverse||plan.Reverse}," + + "${track.CarDirectionBias},${track.EnableCarAbsoluteDirection},${track.CarAbsoluteDirection},${track.MagnetChoose},${track.MultiVehicleSync},${track.typeInfo});", + blockVerb = "true", + siteFields = typeof(MultiWheelLifterSiteFields), + trackFields = typeof(MultiWheelLifterTrackFields), + planFields = typeof(MultiWheelLifterPlanFields))] + + [TemplateTrackCoderSettings( + priority = 19, + useVerb = "dst.tag>0 && src.tag>0", + templateString = "agv.QrGo(${src.x},${src.y},${src.id},${src.tag},${dst.x},${dst.y},${dst.id},${dst.tag},${track.id}," + + "${track.Speed},${track.Reverse || (track.ReverseDst == dst.id)}," + + "${track.CarDirectionBias},${track.EnableCarAbsoluteDirection},${track.CarAbsoluteDirection}," + + "${track.typeInfo});", + blockVerb = "true", + siteFields = typeof(MultiWheelLifterSiteFields), + trackFields = typeof(MultiWheelLifterTrackFields), + planFields = typeof(MultiWheelLifterPlanFields))] + + // 閫氱敤閬块殰/IO coder 宸叉娊绂讳负 StandardScene.Coders.*锛堣绫诲墠 [ProgramTrackCoderSettings] 寮曠敤锛 + + // 閬块殰鍖哄昂瀵(4鍙,鍚腑蹇冪偣)鍒囨崲 coder 宸叉娊绂讳负 StandardScene.Coders.AvoidanceParamCoder锛堣绫诲墠 [ProgramTrackCoderSettings] 寮曠敤锛 + + // 绾犲亸闃堝 coder 宸叉娊绂讳负 StandardScene.Coders.TrackingErrThreshCoder + + [ProgramTrackCoderSettings(priority = 27, program = typeof(LidarAreaSwitchCoder))] + [ProgramTrackCoderSettings(priority = 20, program = typeof(AvoidanceDistanceCoder))] + [ProgramTrackCoderSettings(priority = 20, program = typeof(IoAreaSwitchCoder))] + [ProgramTrackCoderSettings(priority = 20, program = typeof(TrackingErrThreshCoder))] + [ProgramTrackCoderSettings(priority = 20, program = typeof(AvoidanceParamCoder))] + [EnvelopConfig(lengthX = 2000, lengthY = 2000, centerX = 0, centerY = 0)] + + [CarType(Name = "澶氳溅鑱斿姩AGV")] + public class MultiVehicleCar : GhostCar + { + [FieldMember] public float CarLength = 2000; + [FieldMember] public float CarWidth = 1400; + public static async Task Create() + { + var car = new MultiVehicleCar() + { + lstatus = "杩炴帴涓", + address = "127.0.0.1", + name = "鑱斿姩AGV" + }; + return car; + } + + protected override void draw(Graphics eGraphics) + { + var HalfCarLength = CarLength / 2; + var HalfCarWidth = CarWidth / 2; + eGraphics.FillRectangle(Brushes.Gray, -HalfCarLength, -HalfCarWidth, HalfCarLength * 2, HalfCarWidth * 2); + eGraphics.DrawRectangle(Pens.White, -HalfCarLength, -HalfCarWidth, HalfCarLength * 2, HalfCarWidth * 2); + eGraphics.DrawLine(Pens.White, HalfCarLength - HalfCarWidth, -HalfCarWidth, HalfCarLength, 0); + eGraphics.DrawLine(Pens.White, HalfCarLength - HalfCarWidth, HalfCarWidth, HalfCarLength, 0); + } + + [MethodMember(Name = "妯℃嫙杞﹁仈鍔ㄥ垵濮嬪寲")] + public void GhostMultiAgvSyncInit() + { + var car1 = (GhostCar)SimpleLib.GetAllCars().First(cc => cc.name == "sync-1"); + while (true) + { + car1.ManualTeleport(); + car1.x = 1770; + car1.y = 0; + car1.th = 0; + Thread.Sleep(100); + if (LessMath.dist(car1.x, car1.y, 1770, 0) < 1 && car1.th == 0) break; + } + car1.NoSchedule(); + car1.siteID = -1; + car1.tags.Clear(); + car1.status.usage.AddUsage("base", new CarUsage.CarUsageInfo() { scheduling = false, refreshing = false }); + car1.fields["MultiVehicleSync"] = "True"; + + var car2 = (GhostCar)SimpleLib.GetAllCars().First(cc => cc.name == "sync-2"); + while (true) + { + //car2.ManualTeleport(-750, 0, 180); + car2.ManualTeleport(); + car2.x = -750; + car2.y = 0; + car2.th = 180; + Thread.Sleep(100); + if (LessMath.dist(car2.x, car2.y, -750, 0) < 1 && car2.th == 180) break; + } + var car2Site = SimpleLib.GetAllSites().OrderBy(ss => LessMath.dist(ss.x, ss.y, car2.x, car2.y)).First(); + var syncInitSite = SimpleLib.GetSite(int.Parse(car2Site.fields["SyncInitId"])); + car2.TrafficReset(syncInitSite); + car2.siteID = syncInitSite.id; + car2.fields["MultiVehicleSync"] = "True"; + } + + [MethodMember(Name = "杩涘叆鑱斿姩鐘舵")] + public void StartMultiAgvSync() + { + var car1 = (Car)SimpleLib.GetAllCars().First(cc => cc.name == "sync-1"); + car1.NoSchedule(); + car1.siteID = -1; + car1.tags.Clear(); + car1.status.usage.AddUsage("base", new CarUsage.CarUsageInfo() { scheduling = false, refreshing = false }); + car1.fields["MultiVehicleSync"] = "True"; + + var car2 = (Car)SimpleLib.GetAllCars().First(cc => cc.name == "sync-2"); + var car2Site = SimpleLib.GetAllSites().OrderBy(ss => LessMath.dist(ss.x, ss.y, car2.x, car2.y)).First(); + var syncInitSite = SimpleLib.GetSite(int.Parse(car2Site.fields["SyncInitId"])); + car2.TrafficReset(syncInitSite); + car2.siteID = syncInitSite.id; + car2.fields["MultiVehicleSync"] = "True"; + } + + [MethodMember(Name = "閫鍑鸿仈鍔ㄧ姸鎬")] + public void ExitMultiAgvSync() + { + var car1 = (Car)SimpleLib.GetAllCars().First(cc => cc.name == "sync-1"); + car1.fields["MultiVehicleSync"] = "False"; + car1.Reset(); + var car2 = (Car)SimpleLib.GetAllCars().First(cc => cc.name == "sync-2"); + car2.fields["MultiVehicleSync"] = "False"; + car2.Reset(); + } + + //[MethodMember(Name = "杩斿巶妫淇畁ew", Description = "涓嶅啀鍒锋柊灏忚溅鐘舵侊紝褰撶劧涔熶笉鍐嶈璋冨害")] + //[I18N.DocumentTranslation(Name = "Blown new", Description = "Car no longer refresh status and scheduling", locale = "en")] + public new void Blown() + { + AppendDebug("ui-blown"); + Diagnosis.Post($"Car {name}({id}) blown"); + NoSchedule(); + siteID = -1; + tags.Clear(); + status.usage.AddUsage( + "base", + new CarUsage.CarUsageInfo { scheduling = false, refreshing = false } + ); + lstatus = "杩斿巶妫淇"; + } + } +} diff --git a/StandardScene.QrLidar/CarTypes/MultiWheelForkLifter.cs b/StandardScene.QrLidar/CarTypes/MultiWheelForkLifter.cs new file mode 100644 index 0000000..29a9de4 --- /dev/null +++ b/StandardScene.QrLidar/CarTypes/MultiWheelForkLifter.cs @@ -0,0 +1,156 @@ +using LessokajiWeaverUtilities.Utilities; +using SimpleLite.RCS; +using SimpleLite.RCS.CarTypes; +using SimpleCore; +using SimpleCore.Compiler; +using SimpleCore.PropType; +using StandardScene.Model; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using static System.Windows.Forms.VisualStyles.VisualStyleElement; + +namespace StandardScene.CarTypes +{ + class SiteFields + { + public float FetchSpeed = 0; + public float FetchLiftDownTarget = -1; + public float FetchLiftUpTarget = -1; + public float PutSpeed = 0; + public float PutLiftDownTarget = -1; + public float PutLiftUpTarget = -1; + public float LeaveShelfSpeed = 0; + public float LeaveShelfLiftDownTarget = -1; + public bool Shelf = false; + } + + class TrackFields + { + public int ReverseDst = -1; + } + + class PlanFields + { + public string action = "/"; + } + + //[TemplateTrackCoderSettings( + // priority = 20, + // useVerb = "src.Shelf&&plan.curSeg==1", + // templateString = "agv.Wait();agv.LeaveShelf(${src.x},${src.y},${src.id},${dst.x},${dst.y},${dst.id},${src.LeaveShelfSpeed},${src.LeaveShelfLiftDownTarget});agv.Wait();", + // blockVerb = "true", + // siteFields = typeof(SiteFields), + // trackFields = typeof(TrackFields), + // planFields = typeof(PlanFields))] + [TemplateTrackCoderSettings( + priority = 20, + useVerb = "plan.action=='fetch' && dst.Shelf&&plan.curSeg==plan.segN-2", + templateString = "agv.Wait();agv.Fetch(${src.x},${src.y},${src.id},${dst.x},${dst.y},${dst.id},${dst.FetchSpeed},${dst.FetchLiftDownTarget},${dst.FetchLiftUpTarget});agv.Wait();", + blockVerb = "true", + siteFields = typeof(SiteFields), + trackFields = typeof(TrackFields), + planFields = typeof(PlanFields))] + [TemplateTrackCoderSettings( + priority = 20, + useVerb = "plan.action=='put' && dst.Shelf&&plan.curSeg==plan.segN-2", + templateString = "agv.Wait();agv.Put(${src.x},${src.y},${src.id},${dst.x},${dst.y},${dst.id},${dst.FetchSpeed},${dst.FetchLiftDownTarget},${dst.FetchLiftUpTarget});agv.Wait();", + blockVerb = "true", + siteFields = typeof(SiteFields), + trackFields = typeof(TrackFields), + planFields = typeof(PlanFields))] + + + [CarType(Name = "澶氳埖杞弶杞", editor = typeof(MultiWheelForkLifter))] + [I18N.DocumentTranslation(Name = "MultiWheel ForkLift Car", locale = "en")] + public class MultiWheelForkLifter : GhostCar + { + public static async Task Create() + { + var car = new MultiWheelForkLifter() + { + lstatus = "杩炴帴涓", + address = "127.0.0.1", + name = "澶氳埖杞弶杞", + haveCoordination = true, + speed = 1 + }; + return car; + } + public static string GetCarStatus(Car car, string key) + { + string value = "0"; + if (car == null) return value; + if (car.status.enums.TryGetValue(key, out var valueStr)) + value = valueStr; + return value; + } + public override string SetDisplayInfo() + { + try + { + var str = $"{name}({id})\n"; + status.enums.TryGetValue("ChassisMode", out var manual); + var ms = manual == "0" ? "鑷姩" : "鎵嬪姩"; + status.enums.TryGetValue("Soc", out var soc); + str = $"{str}{ms}|soc:{soc}"; + var alarmStr = GetCarStatus(this, "AlarmInfo"); + if (alarmStr != "") + str = $"{str}|{alarmStr}"; + if (status.enums.TryGetValue("ElectricCurrent", out var electricCurrent) && + !string.IsNullOrEmpty(electricCurrent)) + { + var charging = float.Parse(electricCurrent) * 0.01f > 0 ? "鍏呯數涓" : "鏈厖鐢"; + str = $"{str}|charge:{charging}"; + } + + var thresSpeed = Commons.CarValue(this, "ThresSpeed"); + str = $"{str}{((thresSpeed <= 0) ? "|閬块殰瑙﹀彂" : "")}"; + return str; + } + catch (Exception e) + { + Console.WriteLine(e); + return "bad car"; + } + } + + private bool _running = false; + [MethodMember(Name = "test")] + public async void Test() + { + _running = true; + var siteA = SimpleLib.GetAllSites().First(s => s.name == "A"); + var siteB = SimpleLib.GetAllSites().First(s => s.name == "B"); + var siteC = SimpleLib.GetAllSites().First(s => s.name == "C"); + while (_running) + { + var fetchPlan = new SegmentPlan() { usingCar = this }; + fetchPlan.fields["action"] = "fetch"; + fetchPlan.FindRoute(SimpleLib.GetSite(GetLastSite()), siteA); + await fetchPlan.Compile("fetch").Queue(); + var putPlan = new SegmentPlan() { usingCar = this }; + putPlan.fields["action"] = "put"; + putPlan.FindRoute(SimpleLib.GetSite(GetLastSite()), siteB); + await putPlan.Compile("put").Queue(); + var plan = new SegmentPlan() { usingCar = this }; + plan.FindRoute(SimpleLib.GetSite(GetLastSite()), siteC); + await plan.Compile("go").Queue(); + var fetchPlan2 = new SegmentPlan() { usingCar = this }; + fetchPlan2.fields["action"] = "fetch"; + fetchPlan2.FindRoute(SimpleLib.GetSite(GetLastSite()), siteB); + await fetchPlan2.Compile("fetch").Queue(); + var putPlan2 = new SegmentPlan() { usingCar = this }; + putPlan2.fields["action"] = "put"; + putPlan2.FindRoute(SimpleLib.GetSite(GetLastSite()), siteA); + await putPlan2.Compile("put").Queue(); + var plan2 = new SegmentPlan() { usingCar = this }; + plan2.FindRoute(SimpleLib.GetSite(GetLastSite()), siteC); + await plan2.Compile("go").Queue(); + + } + } + } +} diff --git a/StandardScene.QrLidar/QrLidarSceneProfile.cs b/StandardScene.QrLidar/QrLidarSceneProfile.cs new file mode 100644 index 0000000..4ae47eb --- /dev/null +++ b/StandardScene.QrLidar/QrLidarSceneProfile.cs @@ -0,0 +1,37 @@ +using System; +using System.Collections.Generic; +using SimpleCore.Navigation; +using StandardScene.CarTypes; + +namespace StandardScene.QrLidar +{ + /// + /// scene.qrlidar 骞冲彴鐢诲儚锛氭縺鍏 + 浜岀淮鐮佽瀺鍚堝鑸満鏅彃浠躲 + /// 婵鍏夛紙SLAM 鍧愭爣瀵艰埅锛夌敱鍐呮牳 GhostCar 鐨 BasicGo 鍏滃簳鎻愪緵锛涗簩缁寸爜 QrGo 鎸夎建閬撲袱绔 + /// tag 瀛楁閫愭瑙﹀彂鈥斺斿悓涓鍙拌溅鍚屼竴鏉¤矾绾垮彲鍏ㄦ縺鍏夈佸叏浜岀淮鐮佹垨娣峰悎锛堣瀺鍚 / 鍗曠嫭浣跨敤鍧囧彲锛夈 + /// + public sealed class QrLidarSceneProfile : NavigationProfileBase + { + public override NavKind Kind => NavKind.Laser; + + public override IReadOnlyList Kinds => new[] { NavKind.Laser, NavKind.QrCode }; + + public override string SceneId => "scene.qrlidar"; + + public override string DisplayName => "婵鍏+浜岀淮鐮佸钩鍙"; + + public override IReadOnlyList CarTypes => new[] + { + typeof(Forklift), + typeof(MultiWheelForkLifter), + typeof(DualLiftingCar), + typeof(MultiVehicleCar), + typeof(ArmCar), + }; + + public override void OnActivate(ISceneContext context) + { + context.Log($"{DisplayName} 宸叉縺娲伙紙杞﹀瀷锛氬弶杞 / 澶氳埖杞弶杞 / 閿傜數鍙屼妇鍗 / 澶氳溅鑱斿姩 / ArmCar锛"); + } + } +} diff --git a/StandardScene.QrLidar/StandardScene.QrLidar.csproj b/StandardScene.QrLidar/StandardScene.QrLidar.csproj new file mode 100644 index 0000000..004032e --- /dev/null +++ b/StandardScene.QrLidar/StandardScene.QrLidar.csproj @@ -0,0 +1,58 @@ + + + + net8.0-windows + Library + true + StandardScene.QrLidar + StandardScene.QrLidar + latest + true + AnyCPU;x64 + x64 + true + false + disable + disable + false + $(NoWarn);NU1701;CS0618;CS0612;MSB3277;CA1416 + {HintPathFromItem};{TargetFrameworkDirectory};{RawFileName};{GAC} + + + + + + + + + + + + + + + E:\Work\Core\Simple-FR\Simple\SimpleLite\bin\Debug\SimpleLite.dll + + + E:\Work\Core\Simple-FR\Simple\SimpleCore\bin\Debug\netstandard2.0\SimpleCore.dll + + + D:\MDCS\Dependencies\Commons\CommonUsage.dll + + + D:\MDCS\Dependencies\Commons\MDCSToolBox.dll + + + E:\Work\Core\Simple-FR\Simple\tools\Topaz.dll + + + E:\Work\Core\Simple-FR\Simple\SimpleLite\bin\Debug\LessokajiWeaverUtilities.dll + + + + + + + + diff --git a/StandardScene.QrLidar/StandardScene.QrLidar.scene.json b/StandardScene.QrLidar/StandardScene.QrLidar.scene.json new file mode 100644 index 0000000..05cc634 --- /dev/null +++ b/StandardScene.QrLidar/StandardScene.QrLidar.scene.json @@ -0,0 +1,13 @@ +{ + "id": "scene.qrlidar", + "displayName": "婵鍏+浜岀淮鐮佸钩鍙", + "navKind": "laser", + "navKinds": [ "laser", "qrcode" ], + "assembly": "StandardScene.QrLidar.dll", + "coreVersion": ">=1.0.0", + "requiresCore": "StandardScene.dll", + "provides": { + "carTypes": [ "Forklift", "MultiWheelForkLifter", "DualLiftingCar", "MultiVehicleCar", "ArmCar" ], + "missionTypes": [] + } +} diff --git a/StandardScene.sln b/StandardScene.sln new file mode 100644 index 0000000..075b79b --- /dev/null +++ b/StandardScene.sln @@ -0,0 +1,49 @@ +锘 +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.10.35013.160 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StandardScene.Core", "StandardScene.Core\StandardScene.Core.csproj", "{7A745509-1593-4044-BA49-9B6B0A35B505}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StandardScene.Protocol.VDA5050", "StandardScene.Protocol.VDA5050\StandardScene.Protocol.VDA5050.csproj", "{7E5050AA-0001-4A11-9C22-0A0B0C0D0E01}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StandardScene.Devices", "StandardScene.Devices\StandardScene.Devices.csproj", "{7E5050AA-0005-4A11-9C22-0A0B0C0D0E05}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StandardScene.Magnetic", "StandardScene.Magnetic\StandardScene.Magnetic.csproj", "{7E5050AA-0006-4A11-9C22-0A0B0C0D0E06}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StandardScene.QrLidar", "StandardScene.QrLidar\StandardScene.QrLidar.csproj", "{7E5050AA-0007-4A11-9C22-0A0B0C0D0E07}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {7A745509-1593-4044-BA49-9B6B0A35B505}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7A745509-1593-4044-BA49-9B6B0A35B505}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7A745509-1593-4044-BA49-9B6B0A35B505}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7A745509-1593-4044-BA49-9B6B0A35B505}.Release|Any CPU.Build.0 = Release|Any CPU + {7E5050AA-0001-4A11-9C22-0A0B0C0D0E01}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7E5050AA-0001-4A11-9C22-0A0B0C0D0E01}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7E5050AA-0001-4A11-9C22-0A0B0C0D0E01}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7E5050AA-0001-4A11-9C22-0A0B0C0D0E01}.Release|Any CPU.Build.0 = Release|Any CPU + {7E5050AA-0005-4A11-9C22-0A0B0C0D0E05}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7E5050AA-0005-4A11-9C22-0A0B0C0D0E05}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7E5050AA-0005-4A11-9C22-0A0B0C0D0E05}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7E5050AA-0005-4A11-9C22-0A0B0C0D0E05}.Release|Any CPU.Build.0 = Release|Any CPU + {7E5050AA-0006-4A11-9C22-0A0B0C0D0E06}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7E5050AA-0006-4A11-9C22-0A0B0C0D0E06}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7E5050AA-0006-4A11-9C22-0A0B0C0D0E06}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7E5050AA-0006-4A11-9C22-0A0B0C0D0E06}.Release|Any CPU.Build.0 = Release|Any CPU + {7E5050AA-0007-4A11-9C22-0A0B0C0D0E07}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7E5050AA-0007-4A11-9C22-0A0B0C0D0E07}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7E5050AA-0007-4A11-9C22-0A0B0C0D0E07}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7E5050AA-0007-4A11-9C22-0A0B0C0D0E07}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {C6119F94-0318-401A-A944-DF38DD88AD83} + EndGlobalSection +EndGlobal diff --git a/StandardScene浠g爜瀹℃煡鎶ュ憡-浼氳瘽8澶嶆牳澧炲己鐗.md b/StandardScene浠g爜瀹℃煡鎶ュ憡-浼氳瘽8澶嶆牳澧炲己鐗.md new file mode 100644 index 0000000..00cd5bc --- /dev/null +++ b/StandardScene浠g爜瀹℃煡鎶ュ憡-浼氳瘽8澶嶆牳澧炲己鐗.md @@ -0,0 +1,166 @@ +# StandardScene 浠g爜瀹℃煡鎶ュ憡 路 浼氳瘽8 澶嶆牳澧炲己鐗 + +> 瀹℃煡瀵硅薄锛歚E:\Work\Core\Simple-FR\StandardSence`锛圫tandardScene.Core / StandardScene.Devices / StandardScene.Protocol.VDA5050锛 +> 瀹℃煡鏂瑰紡锛氬叏閲忓弽妯″紡鎵弿锛坮ipgrep锛+ 楂橀闄/浠h〃鎬ф枃浠堕愯绮捐鏍稿疄 + 瀵规棦鏈夈奡tandardScene浠g爜瀹℃煡鎶ュ憡.md銆嬬殑閫愰」澶嶆牳 +> 鐩爣妗嗘灦锛歚net8.0-windows`锛堟渶缁堢洰鏍 `net8.0`锛屽幓 WinForms锛 +> 鎶ュ憡鏃ユ湡锛2026-06-09锛堜細璇8锛 +> 璇存槑锛氭湰鎶ュ憡鏄鍚屾棩鏃㈡湁鎶ュ憡鐨**澶嶆牳澧炲己鐗**銆傚鏍稿彂鐜版棦鏈夋姤鍛婁腑鐨**澶氫釜 P0 宸茶淇**锛屾湰鎶ュ憡鎹疄鏇存柊鐜扮姸銆佽ˉ鍏呯簿纭鍙枫佸苟璁板綍鑻ュ共**鏂板彂鐜伴棶棰**銆 + +--- + +## 銆囥佽鐩栧害涓庢柟娉曡鏄 + +- **鍏ㄩ噺鎵弿**锛氬瑙e喅鏂规鍐呭叏閮 `.cs` 鍋氬弽妯″紡鎵弿锛坄Thread.Abort` / `async void` / 绌 `catch{}` / 纭紪鐮 IP / 灞閮 `new HttpClient` / `throw ex;` / `while(true)` / `Thread.Sleep` / `Console.*` / 鍙嶅皠 `GetMethod`锛夈 +- **绮捐鏍稿疄**锛堥愯璇诲彇銆佽鍙风簿纭級锛歚ChargeUdpService`銆乣StandardChargeMission`(鍋滄)銆乣AbstractLoopMission`(鍋滄)銆乣WebApi`(鍙嶅皠绔偣+鐧藉悕鍗+寮澶)銆乣Commons`銆乣PCBChargeStation`銆乣MuXingChargeStation`銆乣VDA5050Car`銆乣AsyncTcpClient`銆乣CommunicationMessageService`銆乣AtomicFileUpdateHelper`銆乣SnowflakeIdGenerator`銆乣WebAPIHelper`銆乣JsonParser`銆乣JsonTool`銆乣ModbusDoorController`銆乣Kiva`(浠h〃杞﹀瀷)銆 +- **鏈愯瑕嗙洊**锛氶儴鍒 Model/Designer/Viewer 涓庡皯鏁拌溅鍨嬩粎鍋氭壂鎻忕骇鏍稿锛堝凡鍦ㄦ竻鍗曟爣娉級锛屼笉褰卞搷涓荤粨璁恒 + +--- + +## 涓銆佸鏃㈡湁鎶ュ憡鐨勫鏍哥粨璁猴紙閲嶇偣锛 + +| 鏃㈡湁鎶ュ憡鏉$洰 | 澶嶆牳鐜扮姸 | 璇佹嵁锛堢簿纭鍙凤級 | +|---|---|---| +| **P0-1 `Thread.Abort` 琚┖ catch 鍚** | 鉁 **宸蹭慨澶** | `Charge/StandardChargeMission.cs:683-685` 鏀逛负 `myThread?.Join(2000)`锛沗Chained/AbstractLoopMission.cs:1313-1314 / 1328-1329` 鏀逛负 `flag=false + Join(2000)`锛涘叏瑙e喅鏂规 `Thread.Abort` 浠呬綑 1 澶勬敞閲 | +| **P0-2 WebApi 鏃犻壌鏉冨弽灏勪换鎰忔柟娉曪紙RCE锛** | 鈿狅笍 **閮ㄥ垎淇锛堥檷涓 P1锛** | `WebApi.cs:145-151` 鏂板鐧藉悕鍗 `IsReflectionInvokable`锛堥粦鍚嶅崟 `NoReflectionApi` 浼樺厛 + 蹇呴』鏍 `MethodMember`/`ReflectionApiWithParameter`锛夛紱绔偣 `330-331 / 387-388` 宸叉嫤鎴**浣嗕粛 GET 鎵ц锛295/358锛夈佷粛鏃犵綉缁滃眰閴存潈** | +| **P0-3 瀹炴椂鎶ユ枃鎸夊浐瀹氫笅鏍囧彇鍊笺佺己闀垮害鏍¢獙** | 鉁 **涓昏璺緞宸蹭慨澶** | `Charge/ChargeUdpService.cs:49-50` 鍔 `message.Length>28` 鏍¢獙涓 `56` 涓嶅悶鏂嚎绋嬶紱`Devices/Charge/PCBChargeStation.cs:30-31` 鍔 `message.Length>1` 鏍¢獙 | +| 鑼冩湰锛歚CommunicationMessageService` 瀹夊叏瑙f瀽 | 鉁 纭鑼冩湰 | `Charge/CommunicationMessageService.cs:198 / 276` 鍏堟牎楠 `parts.Length` 鍐 `byte.TryParse(InvariantCulture)` | +| 鑼冩湰锛歚AsyncTcpClient` | 鉁 纭鑼冩湰 | `TCP/AsyncTcpClient.cs` `_reconnectGate` 閿 + `_closing/_isConnecting/_isReconnecting` + Timer 閲嶈繛 + IDisposable | +| 鑼冩湰锛歚ModbusDoorController` | 鉁 纭鑼冩湰 | `Devices/Door/ModbusDoorController.cs:40` `CancellationTokenSource` + `_syncLock` + 鍘绘姈 `_lastSentControl` + 鍙厤闂撮殧 | + +**缁撹**锛氭棦鏈夋姤鍛婃爣娉ㄧ殑 3 涓 P0 涓紝P0-1銆丳0-3 宸插疄璐ㄤ慨澶嶏紝P0-2 宸茶鏂规硶鐧藉悕鍗曟湁鏁堢紦瑙c**褰撳墠宸叉棤 P0 绾ч樆鏂**銆傛妧鏈轰富瑕侀泦涓湪 P1/P2锛堟棫妯″潡鐨 async void銆佺‖缂栫爜銆丆onsole 鏃ュ織銆佷笂god鏂囦欢銆佸崐鎴愬搧姝讳唬鐮侊級銆 + +--- + +## 浜屻佷粛鐒跺瓨鍦ㄧ殑闂 + +### P1 楂樺嵄 + +#### P1-1銆WebApi 鍙嶅皠绔偣鏃犵綉缁滃眰閴存潈 + 鐢 GET 鎵ц鍓綔鐢ㄦ搷浣 +- 浣嶇疆锛歚StandardScene.Core/WebApi.cs:295`锛坄/car_reflection/execute/{id}/{method}`锛夈乣358`锛坄/mission_reflection/execute/{id}/{method}`锛夛紱寮澶 `38` `ApiController : NancyModule` 鏃犱换浣 `Before`/閴存潈绠$嚎 +- 鐜拌薄锛氳櫧鏈夋柟娉曠櫧鍚嶅崟锛145-151锛夛紝浣嗕换浣曡兘璁块棶璇 HTTP 绔彛鑰呭潎鍙鐧藉悕鍗曞唴鏂规硶鍙戣捣璋冪敤锛屽叾涓寘鍚 `鍏抽棴杩涚▼`銆乣绔嬪嵆寮哄埗缁撴潫` 绛夊嵄闄╂搷浣滐紙濡 `Kiva.ForceStop`銆乣StandardChargeMission.Stop`锛夛紱涓斾负 GET 璇箟锛屾槗琚祻瑙堝櫒棰勫彇/鏃ュ織/CSRF 瑙﹀彂銆 +- 褰卞搷锛氱幇鍦鸿瑙﹀彂鍙鑷村皬杞﹀己鍋溿佸厖鐢佃繘绋嬪叧闂瓑瀹夊叏鐩稿叧鍚庢灉銆 +- 淇锛氣憼 澧炲姞缁熶竴閴存潈锛圓PI Token / 鏉ユ簮 IP 鐧藉悕鍗曪紝Nancy `Before` 绠$嚎闆嗕腑鏍¢獙锛夛紱鈶 鎵ц绫荤鐐规敼 `POST`锛涒憿 瀵光滃嵄闄╂柟娉曗濆鍔犱簩娆$‘璁/鍗曠嫭鏉冮檺浣嶃 + +#### P1-2銆VDA5050 纭紪鐮佺幇鍦鸿澶 IP锛堟崲鐜板満/澶氳溅蹇呭け鏁堬級 +- 浣嶇疆锛歚StandardScene.Protocol.VDA5050/VDACar/VDA5050Car.cs:156`銆乣909`銆乣913`锛沗VDACar/VDA5050Interface.cs:152`銆乣156`锛堝潎涓 `http://192.168.2.1:8008/...`锛 +- 鐜拌薄锛氭妸瀵瑰鍙栧/缃肩殑璁惧 IP 鍐欐涓 `192.168.2.1`锛屼笖 `VDA5050Car.cs:154` 娉ㄩ噴閲屾湰鏈 `this.address` 鐨勬纭啓娉曞嵈琚純鐢ㄣ +- 褰卞搷锛氬 AGV 鎴栨崲鐜板満鏃跺叏閮ㄥけ鏁堬紱鎵鏈夎溅閮芥墦鍒板悓涓 IP銆 +- 淇锛氱粺涓鍙 `this.address`/閰嶇疆椤癸紱绔彛涓庤矾寰勮蛋閰嶇疆锛涘垹闄ゅ啓姝诲垎鏀 + +#### P1-3銆`Commons.AddOrUpdateTag` 鍚嶄笉绗﹀疄锛堝彧 Add锛屽凡瀛樺湪浼氭姏寮傚父锛 +- 浣嶇疆锛歚StandardScene.Core/Commons.cs:96-107`锛堟纭殑鈥滃瓨鍦ㄥ垯鏇存柊鈥濋昏緫琚敞閲婏紝浠呬繚鐣 `item.Add(tag, value)` 浜 `106`锛 +- 鐜拌薄锛氭柟娉曡涔夋槸 AddOrUpdate锛屽疄闄呭彧 Add锛涘綋 tag 宸插瓨鍦ㄦ椂鎸 `TagSet.Add` 琛屼负鍙兘鎶涘紓甯告垨浜х敓閲嶅椤广 +- 褰卞搷锛氳皟鐢ㄦ柟锛堝 `AbstractLoopMission.MarkTaskStartSitesAsTerminal 鈫 AddOrUpdateSiteField`锛夊湪閲嶅鏍囪鍦烘櫙涓嬪彲鑳藉紓甯告垨鑴忔暟鎹 +- 淇锛氭仮澶嶁滃瓨鍦ㄥ垯 `item[tag]=value`锛屽惁鍒 `Add`鈥濊涔夈 + +#### P1-4銆灞閮 `new HttpClient()`锛坰ocket/绔彛鑰楀敖椋庨櫓锛 +- 浣嶇疆锛歚WebApi.cs:977 / 1256 / 1322`銆乣Model/Map.cs:194`锛堝潎涓烘柟娉曞唴 `new HttpClient()` 鍚庡嵆鐢ㄥ嵆寮冿級 +- 鐜拌薄锛氶珮棰戣矾寰勬瘡娆℃柊寤 HttpClient锛屽簳灞 socket 杩涘叆 TIME_WAIT 绱Н锛岄暱鏈熻繍琛岀鍙h楀敖銆 +- 褰卞搷锛氳繍琛屼竴娈垫椂闂村悗 HTTP 璋冪敤澶ч潰绉秴鏃/澶辫触銆 +- 淇锛氬鐢ㄩ潤鎬佸崟渚 / `IHttpClientFactory`锛涢」鐩凡鏈夋纭寖渚嬪彲鍙傜収锛坄Chained/DeliveryViewer.cs:24`銆乣Chained/TransportDeliveryCallbacks.cs:26` 鐨 `static readonly HttpClient`锛屼互鍙 `Utils/WebAPIHelper` 鐨勮繛鎺ユ睜璁捐锛夈 + +#### P1-5銆`async void` 娉涙互锛堝紓甯搁冮搞佹棤娉曠瓑寰呫佹棤娉曞彇娑堬級 +- 浠h〃浣嶇疆锛歚VDA5050Car.cs:150`(`GetVDA5050StateFromC`锛屼笖 `165-167` 绌 catch 鍚炲紓甯)銆乣Charge/ChargeUdpService.cs:28`(`ListenerProcess`锛岃 `new Thread(...)` 鍖呰9鏇村け璇箟)銆乣Devices/Charge/MuXingChargeStation.cs:424`(`SendMessage`锛屽唴閮ㄦ棤 await)銆乣Chained/TransportMission.cs:374/425/470/507`銆乣Chained/TransportDeliveryCallbacks.cs:79/109/136/145/154/161`銆乣Chained/AbstractChainedDeliveryMission.cs:672/1020/1276`銆乣InterLock/AbstractInterlockMission.cs:289/326`銆佽溅鍨 `Kiva.cs:495/619`銆乣Forklift.cs:419`銆乣DummyCar.cs:461/576/626/671` 绛 +- 鐜拌薄锛歚async void` 鎶涘嚭鐨勫紓甯哥洿杈 SynchronizationContext锛屽父瀵艰嚧杩涚▼绾ф湭瑙傛祴寮傚父锛涘叾涓澶 catch 涓虹┖锛堝紓甯歌鍚烇級銆 +- 褰卞搷锛氬伓鍙戝穿婧/鐘舵侀敊涔变笖闅炬帓闅溿 +- 淇锛氫笟鍔″紓姝ユ柟娉曚竴寰嬭繑鍥 `Task` 骞剁敱涓婂眰 `await`/`ContinueWith` 澶勭悊寮傚父锛涚‘闇 fire-and-forget 鐨勫叆鍙o紙浜嬩欢/妗嗘灦鍥炶皟锛夊唴閮ㄥ繀椤 `try/catch + Diagnosis.Log`锛沗MuXing.SendMessage` 杩欑鏃 await 鐨勫簲鐩存帴鏀 `void`銆 + +### P2 缁撴瀯 / 鍙淮鎶ゆ + +#### P2-1銆`WebApi.cs` 涓婂笣鏂囦欢锛堢害 2800+ 琛屽崟 NancyModule锛 +- 浣嶇疆锛歚StandardScene.Core/WebApi.cs`锛坄ApiController : NancyModule` 鍗曠被鎵胯浇鍏ㄩ儴璺敱锛 +- 淇锛氭寜鍔熻兘鍩熸媶鍒嗕负澶氫釜 NancyModule锛堣溅杈/浠诲姟/鍦板浘/鍏呯數/浜ょ/绯荤粺锛夛紝鍏叡閫昏緫锛堥壌鏉冦佺粺涓鍝嶅簲灏佽銆佸弬鏁扮粦瀹氥佸弽灏勬墽琛岋級涓嬫矇鍒板熀绫/涓棿浠躲 + +#### P2-2銆`GetMethods` 鍙壂鎻忓綋鍓嶇▼搴忛泦锛屼笌杩愯鏈熺被鍨嬪彂鐜板彛寰勪笉涓鑷 +- 浣嶇疆锛歚WebApi.cs:47` `Assembly.GetExecutingAssembly().GetTypes()` +- 鐜拌薄锛氭媶鍒嗗悗鍗槦 dll锛圖evices/VDA5050锛変腑鐨勮溅鍨/Mission 鏂规硶涓嶄細鍑虹幇鍦 `get_type_methods` 鍒楄〃閲岋紱鑰岃繍琛屾湡绫诲瀷鍙戠幇璧扮殑鏄 `UiTypeDiscovery.AllTypes()`锛堝叏鍩燂級銆 +- 褰卞搷锛氬墠绔滃彲鐢ㄥ姩浣溾濆垪琛ㄧ己澶卞崼鏄熺被鍨嬬殑鏂规硶锛坋xecute 绔偣鎸夊疄渚嬪弽灏勪粛鍙敤锛屼絾 UI 鍙戠幇涓嶅叏锛夈 +- 淇锛歚GetMethods` 鏀圭敤 `UiTypeDiscovery.AllTypes()` 缁熶竴鍙e緞銆 + +#### P2-3銆`AtomicFileUpdateHelper` 骞堕潪鐪熸鈥滃師瀛愨濆啓 +- 浣嶇疆锛歚StandardScene.Core/CommonTools/AtomicFileUpdateHelper.cs:54`锛坄File.WriteAllText` 鐩存帴瑕嗙洊锛 +- 鐜拌薄锛氫粎鐢 `ConcurrentDictionary` 淇濊瘉**杩涚▼鍐呭悓璺緞涓茶**锛堢嚎绋嬪畨鍏 OK锛夛紝浣嗗啓鍏ユ槸鐩存帴瑕嗙洊锛**杩涚▼宕╂簝/鏂數鏃舵枃浠跺彲鑳芥崯鍧忥紙鍗婃埅鍐呭锛**锛涚被鍚嶁淎tomic鈥濇湁璇銆傚彟锛歚PathLocks` 鍙涓嶅噺锛堥暱鏈熻繍琛岃交寰疮绉紝`14`锛夈 +- 淇锛氭敼鈥滃啓涓存椂鏂囦欢 鈫 Flush 鈫 `File.Replace`/`File.Move` 瑕嗙洊鈥濆疄鐜扮湡姝e師瀛愯惤鐩橈紱鎴栧湪鏂囨。/鍛藉悕涓婃槑纭叾浠呬繚璇佷覆琛岃岄潪宕╂簝鍘熷瓙鎬с + +#### P2-4銆`JsonParser` 姝讳唬鐮佷笌鏂囦欢鎹熷潖闅愭偅 +- 浣嶇疆锛歚StandardScene.Core/Utils/JsonParser.cs:31-59`锛坄JsonChangeValue` 鐨 `foreach` 寰幆浣撴暣娈佃娉ㄩ噴锛宍Task.Run` 璺戠┖寰幆锛岀瓑鍚 NOP锛夛紱`22` `WriteJsonFile` 鐢 `File.AppendAllText` +- 鐜拌薄锛歚JsonChangeValue` 鏄滄敼鍊尖濊涔夊嵈浠涔堥兘涓嶅仛锛沗WriteJsonFile` 瀵瑰悓涓 `TaskId` 閲嶅璋冪敤浼氭妸澶氫釜 JSON 杩藉姞杩涘悓涓鏂囦欢锛屽緱鍒伴潪娉 JSON銆 +- 淇锛氬垹闄/閲嶅啓 `JsonChangeValue`锛沗WriteJsonFile` 鏀逛负瑕嗙洊鍐欙紙閰嶅悎 P2-3 鐨勫師瀛愬啓锛夈 + +#### P2-5銆`WebAPIHelper` 閫鍖栦负绌哄3 +- 浣嶇疆锛歚StandardScene.Core/Utils/WebAPIHelper.cs:52-142`锛圙et/Post 绛夊叏閮ㄦ柟娉曡娉ㄩ噴锛夛紱`getClient` 鐢 `ContainsKey + 绱㈠紩鍣╜锛坄29-33`锛夐潪鍘熷瓙 +- 鐜拌薄锛氳繛鎺ユ睜璁捐姝g‘锛坄23` `ConcurrentDictionary`锛夛紝浣嗗澶栨病鏈変换浣曞彲鐢ㄨ姹傛柟娉 鈫 鍚勫鍙兘鍚勮嚜 `new HttpClient`锛堟鏄 P1-4 鐨勬牴鍥犱箣涓锛夛紱`getClient` 骞跺彂涓嬪彲鑳藉垱寤哄涓 client銆 +- 淇锛氭仮澶/閲嶅啓 `GetAsync/PostAsync` 骞跺叏椤圭洰鏀圭敤涔嬶紱`getClient` 鏀 `GetOrAdd`銆 + +#### P2-6銆UDP 鍙戦佺殑 `SendAsync` 鏈 await + `using` 绔炴 +- 浣嶇疆锛歚Devices/Charge/PCBChargeStation.cs:71-73`锛坄udpClient.SendAsync(...)` 鏈 await锛岀揣鎺 `Thread.Sleep(100)` 鍚 `using` 鍧楃粨鏉 Dispose锛 +- 鐜拌薄锛氬紓姝ュ彂閫佸彲鑳藉湪 `UdpClient` 琚 Dispose 鍚庢墠鐪熸鍙戝嚭锛屽瓨鍦 `ObjectDisposedException`/涓㈠寘椋庨櫓锛堥潬 `Sleep(100)` 鎺╃洊锛夈 +- 淇锛氭敼鍚屾 `Send` 鎴 `await SendAsync` 鍚庡啀閫鍑 `using`銆 + +#### P2-7銆`Console.*` 浣滀负鐢熶骇鏃ュ織 +- 浠h〃锛歚VDA5050Car.cs`锛堢害 30 澶勶級銆乣MasterMQTTCommunication.cs`锛堢害 22 澶勶級銆乣DummyCar.cs`锛堢害 29 澶勶級銆乣PCBChargeStation.cs:97`銆乣MuXingChargeStation.cs:438`銆乣Kiva.cs:567`銆乣Commons.cs`(绾6)銆乣WebApi.cs`(绾8) +- 淇锛氱粺涓鏀 `Diagnosis.Log/Post`锛堥」鐩棦鏈夋棩蹇楅棬闈級锛屼繚鐣欑骇鍒笌鍙绱㈡с + +#### P2-8銆`throw ex;` 涓㈠け鍘熷鍫嗘爤 +- 浣嶇疆锛歚VDA5050Car.cs:240`銆乣CarTypes/DummyCar.cs:451` +- 淇锛氭敼 `throw;`锛堥噸鎶涳級鎴 `throw new XxxException(msg, ex)`锛堝寘瑁呬繚鐣 inner锛夈 + +#### P2-9銆鍙嶅皠璋冪敤闈炲叕寮鏂规硶 / 鎸夐厤缃悕鍙嶅皠 +- 浣嶇疆锛歚CarTypes/VehicleMonitor.cs:1671`锛坄GetMethod(name, Public|NonPublic)` 鍙揪绉佹湁鏂规硶锛夛紱`ExtendDevice/ButtonBox/ButtonMission.cs:594`锛堟寜 `buttonConfig.TriggerMethod` 鍙嶅皠锛 +- 淇锛氶檺鍒跺埌鍏紑+鐧藉悕鍗曪紱瀵归厤缃┍鍔ㄧ殑鍙嶅皠鍋氭柟娉曞瓨鍦ㄦт笌鐧藉悕鍗曟牎楠屻 + +#### P2-10銆UI 涓庝笟鍔¤﹀悎锛堟湇鍔$/鏃犱汉鍊煎畧浼氶樆濉烇級 +- 浣嶇疆锛歚Commons.cs:42`锛堟閿佸洖璋冮噷 `MessageBox.Show`锛夈乣Kiva.cs:648` 绛夎溅鍨嬪湪鍚庡彴绾跨▼ `MessageBox.Show` +- 淇锛氫笟鍔″眰鍙骇鐢熶簨浠/鏃ュ織锛屾槸鍚﹀脊绐椾氦鐢辫〃鐜板眰鍐冲畾锛堣縼 migu 骞冲彴鏃朵竴骞惰В鍐筹級銆 + +### P3 鏁存磥 / 鍗敓 + +- **绌 `catch{}`/闈欓粯鍚炲紓甯**锛堝缓璁嚦灏 `Diagnosis.Log`锛夛細`Kiva.cs:606-610 / 637-639`銆乣VDA5050Car.cs:165-167 / 997-1000`銆乣Devices/Charge/FLChargeStation.cs:173 / 243`銆乣Chained/AbstractLoopMission.cs:1318/1333/1396/1576/1587/1601`銆乣Chained/LoopViewer.cs:260`銆乣Charge/CommunicationMessageService.cs:183-186 / 261-264`銆乣Commons.cs:44`銆傦紙娉細`AsyncTcpClient` 涓 `try{Close();}catch{}` 灞炴竻鐞嗘у悶寮傚父锛屽彲鎺ュ彈銆傦級 +- **鏈湴鍥炵幆/绔彛纭紪鐮**锛堝缓璁厤缃寲锛岄闄╀綆浜 P1-2锛夛細杞﹀瀷 `address="127.0.0.1"` 澶氬锛沗Model/Map.cs:195/207`(绔彛 4321)锛沗Chained/LoopMission.cs:74`(`SiemensClient ...,"127.0.0.1",103`)锛沗Chained/TransportDeliveryCallbacks.cs:27`(`_callbackUrl ...20101`)銆 +- **`float.Parse`/`int.Parse` 鏈寚瀹 Culture / 鏈 TryParse**锛歚Devices/Charge/PCBChargeStation.cs:61-62`銆乣Kiva.cs:557/601-603` 绛夈 +- **`SnowflakeIdGenerator`**锛氬疄鐜拌壇濂斤紙`51` 閿併乣54-58` 鏃堕挓鍥炴嫧绛夊緟锛夛紱浠呮彁绀 `DefaultEpochMs=2026-01-01`锛坄10`锛夐儴缃插埌绯荤粺鏃堕棿鏃╀簬璇ュ肩殑鏈哄櫒浼氬湪鏋勯犳湡鎶涘紓甯革紙`33-36`锛夈 + +--- + +## 涓夈佹湰娆℃柊鍙戠幇锛堟棦鏈夋姤鍛婃湭璁板綍锛 + +| 绾у埆 | 闂 | 浣嶇疆 | +|---|---|---| +| P2(閫昏緫bug) | `Kiva.LoopTest` 澶嶅埗绮樿创閿欒锛氭瀯閫犱簡 `plan4` 鍗 `await plan2.Compile("go").Queue()`锛涗笖 `LoopTestRunning` 鏍囧織璁句簡浣嗗惊鐜綋浠庝笉妫鏌ワ紙`LoopTestStop` 瀹為檯鏃犳晥锛屸滃惊鐜祴璇曗濆苟涓嶅惊鐜級 | `CarTypes/Kiva.cs:517-520`銆乣492/497/528` | +| P2 | `MuXingChargeStation.SendMessage` 鏍 `async void` 浣嗗唴閮ㄥ叏鏄悓姝 `stream.Write/Flush`锛屾棤 await锛涗笖鍙垽 `client!=null` 鏈垽 `stream` | `Devices/Charge/MuXingChargeStation.cs:424-440` | +| P2 | `JsonParser.JsonChangeValue` 绌哄惊鐜浠g爜锛沗WriteJsonFile` 鐢 `AppendAllText` 浼氭崯鍧 JSON | `Utils/JsonParser.cs:31-59 / 22` | +| P2 | `AtomicFileUpdateHelper` 闈炵湡鍘熷瓙鍐 | `CommonTools/AtomicFileUpdateHelper.cs:54` | +| P2 | `WebAPIHelper` 璇锋眰鏂规硶鍏ㄦ敞閲婃垚绌哄3 | `Utils/WebAPIHelper.cs:52-142` | +| P2 | `GetMethods` 浠呮壂褰撳墠绋嬪簭闆嗭紝涓庡叏鍩熺被鍨嬪彂鐜板彛寰勪笉涓鑷 | `WebApi.cs:47` | + +--- + +## 鍥涖佸瓙绯荤粺璇勫垎锛堝鏍告洿鏂帮級 + +| 瀛愮郴缁 | 鏃ц瘎 | 澶嶆牳鏂拌瘎 | 鍙樺寲璇存槑 | +|---|---|---|---| +| 璁惧椹卞姩 `StandardScene.Devices` | 鈽呪槄鈽呪槄 | 鈽呪槄鈽呪槄 | `ModbusDoorController` 鑼冩湰锛沗MuXing/PCB` 鏈 async void/UDP 绔炴佸緟淇 | +| TCP 鍩虹璁炬柦 `AsyncTcpClient` | 鈽呪槄鈽呪槄 | 鈽呪槄鈽呪槄 | 缁存寔 | +| `SnowflakeIdGenerator` / `AtomicFileUpdateHelper` | 鈥旓紙鏈崟鍒楋級 | 鈽呪槄鈽呪槄 / 鈽呪槄鈽 | Snowflake 濂斤紱AtomicFile 鍚嶄笉绗﹀疄 | +| Coders锛堥噸鏋勫悗锛 | 鈽呪槄鈽呪槄 | 鈽呪槄鈽呪槄 | 缁存寔 | +| 浠诲姟鏃 Missions | 鈽呪槄 | 鈽呪槄鈽 | Thread.Abort 宸叉敼鍗忎綔寮忓仠姝紙鍏抽敭鍥炲崌锛夛紱async void/while+Sleep 浠嶅湪 | +| 鍏呯數 Charge | 鈽呪槄 | 鈽呪槄鈽 | 鎶ユ枃瓒婄晫宸插姞鏍¢獙銆佸仠姝㈠凡鍗忎綔寮忥紱UDP 鍙戦佺珵鎬/async void 寰呬慨 | +| VDA5050 鍗忚 | 鈽呪槄 | 鈽呪槄 | 纭紪鐮 IP / async void / throw ex / Console 浠嶉泦涓紝鍊哄姟鏈閲 | +| WebApi | 鈽 | 鈽呪槄 | 鍙嶅皠鐧藉悕鍗曞凡鍔狅紙鍏抽敭鍥炲崌锛夛紱浠嶄笂甯濇枃浠 + 鏃犻壌鏉 + GET 鎵ц | +| Commons / 鍏叡灞 | 鈽呪槄 | 鈽呪槄 | `AddOrUpdateTag` 鍚嶄笉绗﹀疄銆乣WebAPIHelper` 绌哄3銆乣JsonParser` 姝讳唬鐮 | + +--- + +## 浜斻佷紭鍏堟暣鏀规竻鍗曪紙寤鸿椤哄簭锛 + +1. **P1-1 WebApi 閴存潈 + 鍗遍櫓鎿嶄綔璇箟鍖**锛堝畨鍏ㄧ浉鍏筹紝AGV 鐜板満椋庨櫓鏈楂橈級銆 +2. **P1-2 VDA5050 纭紪鐮 `192.168.2.1` 閰嶇疆鍖**锛堝杞/鎹㈢幇鍦哄繀韪╋級銆 +3. **P1-3 `Commons.AddOrUpdateTag` 淇璇箟**锛堝奖鍝嶉潰骞裤佹槗寮曞紓甯革級銆 +4. **P1-4 灞閮 `new HttpClient` 鏀舵暃涓哄崟渚/宸ュ巶**锛堥暱绋虫э級銆 +5. **P1-5 `async void` 鏀舵暃涓 `Task` + 寮傚父澶勭悊**锛堝厛 VDA5050 / 鍏呯數 / Transport 鍥炶皟涓夊閲嶇偣锛夈 +6. **P2-3/2-4/2-5 淇浠g爜涓庝吉鍘熷瓙**锛坄AtomicFileUpdateHelper`銆乣JsonParser`銆乣WebAPIHelper`锛夈 +7. **P2-1/2-2 WebApi 鎷嗗垎 + 绫诲瀷鍙戠幇鍙e緞缁熶竴**銆 +8. **P2-7 Console 鈫 Diagnosis 鏃ュ織缁熶竴**锛堝彲鑴氭湰鍖栨壒閲忔浛鎹紝鍏 VDA5050锛夈 +9. **P3 绌 catch 琛ユ棩蹇 / 鏈湴绔彛閰嶇疆鍖 / Parse 鍔 Culture**锛堟竻鎵級銆 + +> 璇存槑锛氭湰杞负鍙瀹℃煡锛屾湭鏀瑰姩浠讳綍婧愮爜銆俙Thread.Abort`銆佹姤鏂囪秺鐣屻佸弽灏勭櫧鍚嶅崟绛夋棫 P0 缁忔牳瀹炲凡淇锛屾晠褰撳墠涓嶅啀鍒 P0銆 diff --git a/StandardScene浠g爜瀹℃煡鎶ュ憡.md b/StandardScene浠g爜瀹℃煡鎶ュ憡.md new file mode 100644 index 0000000..d2c4664 --- /dev/null +++ b/StandardScene浠g爜瀹℃煡鎶ュ憡.md @@ -0,0 +1,267 @@ +# StandardScene 浠g爜瀹℃煡鎶ュ憡 + +> 瀹℃煡瀵硅薄锛歚E:\Work\Core\Simple-FR\StandardSence`锛圫tandardScene.Core / StandardScene.Devices / StandardScene.Protocol.VDA5050锛 +> 瀹℃煡鏂瑰紡锛氬叏閲忓弽妯″紡鎵弿锛坮ipgrep锛+ 鍏抽敭鏂囦欢绮捐 + 缂栬瘧鍛婅鍒嗙被锛坉otnet build --no-incremental锛 +> 鐩爣妗嗘灦锛歚net8.0-windows`锛堟渶缁堢洰鏍 `net8.0`锛 +> 鎶ュ憡鏃ユ湡锛2026-06-09 + +--- + +## 涓銆佸鏌ヨ寖鍥翠笌鏂规硶 + +- **浠g爜瑙勬ā**锛113 涓 `.cs` 鏂囦欢锛涙渶澶ф枃浠 `WebApi.cs`锛2661 琛岋級銆乣AbstractLoopMission.cs`锛1858 琛岋級銆 +- **鎵弿缁村害**锛氬苟鍙/绾跨▼銆佸紓甯稿鐞嗐佹姤鏂囪В鏋愯竟鐣屻佽祫婧愮鐞嗐侀厤缃‖缂栫爜銆佹棩蹇椼乁I/涓氬姟鑰﹀悎銆佸畨鍏ㄣ佺紪璇戝憡璀︺ +- **璇佹嵁鏍囨敞**锛 + - `绮捐纭`锛氬凡閫愯璇诲彇銆佽鍙风簿纭 + - `鎵弿鍛戒腑`锛歳ipgrep 鍛戒腑鏂囦欢绾э紝琛屽彿寰呮暣鏀规椂閫愪竴鏍稿銆 + +--- + +## 浜屻佹讳綋璇勪环涓庡瓙绯荤粺璇勫垎 + +| 瀛愮郴缁 | 璇勫垎 | 璇存槑 | +|---|---|---| +| 璁惧椹卞姩锛圫tandardScene.Devices路鏂帮級 | 鈽呪槄鈽呪槄鈽 | `ModbusDoorController` 宸ョ▼鍖栦紭绉锛屽彲浣滃洟闃熸牱鏉 | +| TCP 鍩虹璁炬柦锛圓syncTcpClient锛 | 鈽呪槄鈽呪槄鈽 | 閿/闄堟棫鍥炶皟闃叉姢/閲嶈繛瀹屽杽锛屽皯閲忕憰鐤 | +| Coders锛堥噸鏋勫悗锛 | 鈽呪槄鈽呪槄鈽 | 鏈疆宸插幓閲嶏紝缁撴瀯娓呮櫚 | +| CarTypes 杞﹀瀷鏃 | 鈽呪槄鈽嗏槅鈽 | 宸ㄧ被銆乤sync void銆佺┖ catch銆佺‖缂栫爜闆嗕腑 | +| 浠诲姟鏃 Missions | 鈽呪槄鈽嗏槅鈽 | Thread.Abort銆亀hile(true)+Sleep銆佺姸鎬佹満鍒嗘暎 | +| 鍏呯數 Charge | 鈽呪槄鈽嗏槅鈽 | 瀹炴椂鎶ユ枃璺緞瓒婄晫椋庨櫓銆乁DP 绾跨▼妯″瀷绮楁斁 | +| VDA5050 鍗忚 | 鈽呪槄鈽嗏槅鈽 | 纭紪鐮 IP銆乤sync void銆乼hrow ex銆丆onsole 鏃ュ織 | +| WebApi锛堣 Nancy锛 | 鈽呪槅鈽嗏槅鈽 | 2661 琛屼笂甯濇枃浠躲佹棤閴存潈鍙嶅皠璋冪敤銆佹ā鏉夸唬鐮佺垎鐐 | +| Commons / 鍏叡灞 | 鈽呪槄鈽嗏槅鈽 | 涓婂笣宸ュ叿绫汇侀噸澶嶅疄鐜般侀殣鎬 bug | + +**鏍稿績鍒ゆ柇**锛氭妧鏈**闆嗕腑鍦ㄦ棫妯″潡**锛圕arTypes / Missions / Charge / VDA5050 / WebApi / Commons锛夛紱**鏂板啓妯″潡**锛圖evices銆丄syncTcpClient锛夎川閲忔槑鏄炬洿楂橈紝璇存槑鍥㈤槦鍏峰鍐欏ソ浠g爜鐨勮兘鍔涳紝鍊哄姟涓昏鏄巻鍙查仐鐣欍傛暣鏀瑰簲"浠ユ柊妯″潡涓鸿寖鏈佹寜瀛愮郴缁熸敹鏁涙棫鍊"銆 + +--- + +## 涓夈侀棶棰樹弗閲嶇骇鍒眹鎬 + +| 绾у埆 | 鍚箟 | 涓昏鏉$洰 | +|---|---|---| +| **P0 闃绘柇** | .NET8 涓嬩細宕╂簝/澶辨晥锛屾垨瀛樺湪瀹夊叏椋庨櫓 | Thread.Abort 琚悶銆乄ebApi 鏃犻壌鏉冨弽灏勩佸疄鏃舵姤鏂囪秺鐣 | +| **P1 楂樺嵄** | 鐢熶骇鐜鏄撹Е鍙戞晠闅/闅炬帓闅 | 纭紪鐮 IP銆乤sync void+throw銆佺┖ catch銆丠ttpClient 婊ョ敤 | +| **P2 缁撴瀯** | 鍙淮鎶ゆ/鎵╁睍鎬у樊 | 涓婂笣绫汇侀噸澶嶄唬鐮併丆onsole 鏃ュ織銆乁I/涓氬姟鑰﹀悎 | +| **P3 鏁存磥** | 缂栬瘧鍛婅涓庝唬鐮佸崼鐢 | 45 椤瑰憡璀︼紙閲嶅 using銆佹湭鐢ㄥ瓧娈点侀殣钘忔垚鍛樼瓑锛 | + +--- + +## 鍥涖丳0 闃绘柇绾ч棶棰 + +### P0-1銆`Thread.Abort()` 鍦 .NET8 蹇呮姏寮傚父涓旇闈欓粯鍚炴帀锛堢嚎绋嬪仠涓嶆帀锛 + +`Thread.Abort()` 鍦 .NET8 鎶 `PlatformNotSupportedException`锛堝憡璀 SYSLIB0006锛屽叡 14 鏉/鍙岄厤缃級銆傚澶 `Abort()` 澶栧眰鏄┖ `catch{}`锛屽鑷**寮傚父琚悶銆佺嚎绋嬪疄闄呮湭鍋滄**鈥斺斾换鍔"鍋滄"鍚庡悗鍙扮嚎绋嬩粛鍦ㄨ窇锛岄犳垚閲嶅涓嬪彂銆佽祫婧愭硠婕忋佺姸鎬侀敊涔便 + +绮捐纭鍛戒腑鐐癸細 + +| 鏂囦欢 | 琛 | +|---|---| +| `StandardScene.Core/Chained/AbstractLoopMission.cs` | 1314 `_strategyThread?.Abort()`銆1329 `_logicThread?.Abort()` | +| `StandardScene.Core/Charge/StandardChargeMission.cs` | 681 `myThread?.Abort()`銆684 `ChargeThread?.Abort()` | +| `StandardScene.Core/Scheduler/SecuritySignalMission.cs` | 176 `myThread?.Abort()` | +| `StandardScene.Core/Scheduler/NodeIsEnableMission.cs` | 121 `myThread?.Abort()` | +| `StandardScene.Core/Scheduler/HeartBeatMission.cs` | 68 `_myThread?.Abort()` | +| `StandardScene.Core/Chained/AbstractChainedDeliveryMission.cs` | 1606 `myThread.Abort()` | + +鍏稿瀷浠g爜锛坄AbstractLoopMission.cs:1309`锛宍绮捐纭`锛夛細 + +```1309:1334:E:\Work\Core\Simple-FR\StandardSence\StandardScene.Core\Chained\AbstractLoopMission.cs + public virtual void StopLoop() + { + try + { + _strategyRunning = false; + _strategyThread?.Abort(); + _strategyThread = null; + ... + } + catch { } + } +``` + +**淇鏂瑰悜**锛氭敼涓**鍗忎綔寮忓仠姝**鈥斺斿凡鏈 `_strategyRunning/_logicRunning` 甯冨皵鏍囧織锛屽惊鐜綋搴斿懆鏈熸鏌ヨ鏍囧織閫鍑猴紱绾跨▼鍒涘缓鐢 `IsBackground=true`锛屽仠姝㈡椂 `flag=false` 鍚 `Join(timeout)`銆傚垹闄ゆ墍鏈 `Abort()`銆傚闃诲鍨嬪惊鐜敤 `CancellationToken` + 鍙腑鏂瓑寰咃紙`Task.Delay(token)` / `ManualResetEventSlim.Wait(token)`锛夋浛浠 `Thread.Sleep`銆傚弬鑰 `ModbusDoorController` 鐨 `CancellationTokenSource` 鑼冨紡銆 + +--- + +### P0-2銆WebApi 鍙嶅皠鎺ュ彛锛氭棤閴存潈杩滅▼璋冪敤浠绘剰鏂规硶锛圧CE 绾ч闄╋級 + +`StandardScene.Core/WebApi.cs`锛坄绮捐纭`锛272鈥314 / 332鈥368锛夋毚闇诧細 + +```272:296:E:\Work\Core\Simple-FR\StandardSence\StandardScene.Core\WebApi.cs + Get("/car_reflection/execute/{id}/{method}", parameters => + { + ... + methodInfo = car.GetType().GetMethod((string)parameters.method); + ... + return ExecuteMethod(methodInfo, car, queryParams); + }); +``` + +`mission_reflection/execute/{id}/{method}` 鍚屾銆傞棶棰橈細**閫氳繃 HTTP 鍗冲彲鎸夊悕鍙嶅皠璋冪敤 Car/Mission 涓婁换鎰 public 鏂规硶**锛屾棤韬唤鏍¢獙銆佹棤鏂规硶鐧藉悕鍗曘佹棤鍗遍櫓鏂规硶鎷︽埅銆傜粨鍚 `GET` 璇箟锛岄厤缃笉褰撲細鏆撮湶鍦ㄥ唴缃戠敋鑷冲缃戯紝鏋勬垚鍛戒护鎵ц绾ф敾鍑婚潰銆 + +**淇鏂瑰悜**锛 +1. 鏈灏忓寲锛氬姞璁块棶浠ょ墝/鏉ユ簮 IP 闄愬埗锛堜腑闂翠欢缁熶竴鏍¢獙锛夈 +2. 鏂规硶鐧藉悕鍗曪細浠呭厑璁告爣娉ㄤ簡 `[MethodMember]`锛堟垨鏂板 `[WebInvokable]`锛夌殑鏂规硶琚弽灏勮皟鐢紱鎷掔粷鍏朵綑銆 +3. 璇箟鍖栵細鎵ц绫绘搷浣滄敼 `POST`锛涚粺涓閿欒鍝嶅簲灏佽锛堣 P2-2锛夈 + +--- + +### P0-3銆瀹炴椂鎶ユ枃瑙f瀽鎸夊浐瀹氫笅鏍囧彇鍊笺佺己闀垮害鏍¢獙锛圛ndexOutOfRange 宕╂簝锛 + +瀵规瘮鍙戠幇涓ゆ潯瑙f瀽璺緞**椋庨櫓绛夌骇涓嶅悓**锛 + +- 鉁 **姝g‘鑼冩湰**锛歚StandardScene.Core/Charge/CommunicationMessageService.cs`锛坄绮捐纭`锛夊湪鍙栦笅鏍囧墠鍏堟牎楠 `parts.Length < 30 / < 10`锛198銆276 琛岋級锛屽啀璁块棶 `bytes[28]` 绛夛紝瓒婄晫宸茶闃蹭綇銆 +- 鉂 **椋庨櫓璺緞**锛氬疄鏃 UDP/鍥炶皟璺緞鎸夊浐瀹氫笅鏍囩洿鎺ュ彇鍊硷紝鏈绛変环闀垮害鏍¢獙锛坄鎵弿鍛戒腑`锛夛細 + - `StandardScene.Core/ChargeUdpService.cs`锛坄message[28]` 绛夛級 + - `StandardScene.Devices/Charge/PCBChargeStation.cs`锛坄OnUdpMessage` 涓 `message[1]`锛 + - `StandardScene.Core/ChargeStationType/MuXingChargeStation.cs` + +璁惧鎺夌嚎/鍗婂寘/寮傚父甯ф椂锛岀洿鎺 `IndexOutOfRangeException`锛屼笖鑻ュ彂鐢熷湪 UDP 鎺ユ敹绾跨▼浼氭嫋鍨暣鏉℃帴鏀堕摼璺 + +**淇鏂瑰悜**锛氭墍鏈夋姤鏂囪В鏋愬叆鍙g粺涓"鍏堟牎楠岄暱搴︼紙涓庡抚澶/绫诲瀷鍖归厤锛夊啀鍙栧"锛岃秺鐣岃繑鍥 null 骞 `Diagnosis.Log` 璁板綍鍘熷甯э紱鎶藉嚭 `ChargeFrameParser` 澶嶇敤 `CommunicationMessageService` 鐨勫畨鍏ㄨВ鏋愩 + +--- + +## 浜斻丳1 楂樺嵄闂 + +### P1-1銆閰嶇疆纭紪鐮侊紙IP/URL/璺緞锛夛紝澶 AGV/鎹㈢幆澧冨繀澶辨晥锛坄鎵弿鍛戒腑` 14 鏂囦欢锛 + +鏈鍏稿瀷锛坄绮捐纭`锛塦StandardScene.Protocol.VDA5050/VDACar/VDA5050Car.cs:150`锛 + +```150:168:E:\Work\Core\Simple-FR\StandardSence\StandardScene.Protocol.VDA5050\VDACar\VDA5050Car.cs + public async void GetVDA5050StateFromC() + { + try + { + //string jsonResponse1 = await hc.GetStringAsync($"http://{this.address}:8008/getStat"); + string jsonResponse1 = await hc.GetStringAsync($"http://192.168.2.1:8008/getStat"); + ... + } + catch (Exception ex) + { + } + } +``` + +鎶婃寜杞 `this.address` 鐨勫啓娉**娉ㄩ噴鎺夈佸啓姝 `192.168.2.1`**鈥斺斿 AGV 鍦烘櫙蹇呯劧鍏ㄩ儴鎵撳埌鍚屼竴鍦板潃锛涘鍔犵┖ catch 鍚為敊锛屾晠闅"闈欓粯"銆傚叾浣欏懡涓細`Kiva.cs`/`Forklift.cs`锛坄192.168.2.1:8008`锛夈乣Map.cs`锛坄127.0.0.1:4321`锛夈乣LoopMission.cs`锛堣タ闂ㄥ瓙 PLC IP锛夈乣TransportDeliveryCallbacks.cs`锛堝洖璋 URL锛夈乣StandardCADTool.cs`锛圵indows 鐩樼璺緞锛夌瓑銆 + +**淇鏂瑰悜**锛氭娊 `scene.json`/閰嶇疆涓績缁熶竴娉ㄥ叆锛涚姝笟鍔′唬鐮佸唴鑱旂鐐癸紱`StandardCADTool` 璺緞鏀圭浉瀵/鍙厤缃 + +### P1-2銆`async void` + 鍦ㄥ叾涓 `throw`锛圕A2200 澶辨爤锛夆啋 杩涚▼绾у穿婧冮闄 + +- `Kiva.cs:619` `public new async void ForceStop()`锛坄绮捐纭`锛夛細`async void`锛宑atch 鍐 `MessageBox.Show`锛圲I 鑰﹀悎锛夛紝鏈澶栧眰 `Console.WriteLine(e); throw;`鈥斺擿async void` 鎶涘嚭鐨勫紓甯告棤娉曡璋冪敤鏂规崟鑾凤紝鐩存帴鎵撳埌 `SynchronizationContext`/绾跨▼姹狅紝鍙嚧杩涚▼宕╂簝銆俙new` 杩橀殣钘忓熀绫 `ForceStop`锛圕S0114锛夈 +- `VDA5050Car.cs:240` `throw ex;`锛坄绮捐纭`锛夌牬鍧忓師濮嬪爢鏍堬紙CA2200锛屽叏浠 4 鏉★級銆俙DummyCar.cs` 鍚屾銆 +- `async void` 鍦 CarTypes/Charge/VDA5050/CAD 绛夊鏂囦欢骞挎硾瀛樺湪锛坄鎵弿鍛戒腑`锛夈 + +**淇鏂瑰悜**锛氫簨浠跺鐞嗗櫒涔嬪涓寰 `async Task`锛涚‘闇 `async void` 鐨勫叆鍙g敤 `try/catch` 鍏滃簳骞 `Diagnosis.Log`锛屼笉寰楀鎶涳紱`throw ex;` 鈫 `throw;`銆 + +### P1-3銆绌 `catch{}` 闈欓粯鍚炲紓甯革紙`鎵弿鍛戒腑` 10 鏂囦欢锛 + +`Kiva.cs`銆乣VDA5050Car.cs`銆乣FLChargeStation.cs`銆乣AbstractLoopMission.cs`銆乣Commons.cs`銆乣AsyncTcpClient.cs`銆乣LoopViewer.cs` 绛夈俙Kiva.cs:606`銆乣VDA5050Car.cs:165` 涓哄吀鍨嬩笟鍔¤矾寰勭┖ catch銆 + +> 娉細`AsyncTcpClient` / `ModbusDoorController` 涓幆缁 `Close()/Dispose()` 鐨勭┖ catch 灞炲彲鎺ュ彈鐨勬竻鐞嗗厹搴曪紝搴斾繚鐣欎絾鍔犳敞閲婏紱涓氬姟璺緞绌 catch 蹇呴』鏀逛负璁板綍鏃ュ織銆 + +### P1-4銆鍚屾闃诲璋冪敤 `.Result/.Wait()/GetAwaiter().GetResult()`锛坄鎵弿鍛戒腑` 7 鏂囦欢锛 + +`ButtonMission.cs`銆乣DoorMission.cs`銆乣VDA5050Car.cs` 绛夈俙Kiva.cs:635 status.programs.task.Wait()`锛坄绮捐纭`锛夊湪 UI/绾跨▼涓婁笅鏂囨槗姝婚攣銆**淇**锛氬紓姝ラ摼璺墦閫氾紝閬垮厤 sync-over-async銆 + +### P1-5銆`new HttpClient()` 鍙嶅瀹炰緥鍖栵紙Socket 鑰楀敖锛夛紙`鎵弿鍛戒腑`锛 + +`Forklift.cs`/`Kiva.cs`/`VDA5050Car.cs`/`WebApi.cs` 绛夐绻 `new HttpClient`銆**淇**锛氬崟渚嬫垨 `IHttpClientFactory`/`SocketsHttpHandler`锛堣 `PooledConnectionLifetime`锛夈 + +--- + +## 鍏丳2 缁撴瀯 / 璐ㄩ噺闂 + +### P2-1銆`Commons.cs` 涓婂笣宸ュ叿绫伙紙`绮捐纭`锛 +- `AddOrUpdateCarField/SiteField/MissionField` "ContainsKey鈫扲emove+Add / else Add" 妯℃澘**閲嶅 4 浠**锛屽簲涓 `dict[key]=value`銆 +- `AddOrUpdateTag` 鍚嶄负 update 瀹炰负 add锛堟洿鏂伴昏緫琚敞閲婏級锛岄噸澶嶉敭浼氭姏閿欙紝鍛藉悕璇銆 +- `CarValue`锛氬瓨鍦 `electricCurrent` 鏃**蹇界暐鍏ュ弬 key** 鐩存帴杩斿洖锛岀枒浼 bug銆 +- `GoSite` catch 鍐 `Console.WriteLine + Thread.Sleep(3000)`锛屾敞閲婂啓"閲嶆柊鎵ц"浣**骞舵湭鐪熸閲嶈瘯**銆 +- 璋冨害 `NearestTask`锛堢害 398鈥538锛夊法鍑芥暟銆佹繁宓屽銆佸ぇ娈 `#region ObsoleteCode` 娉ㄩ噴浠g爜銆侀瓟娉曚紭鍏堢骇 `Priority=50`銆 + +### P2-2銆`WebApi.cs` 2661 琛屼笂甯濇枃浠讹紙`绮捐纭`锛 +閿欒鍝嶅簲 `new { Success=false, Code=500, Data="null", Message=... }` 妯℃澘**澶嶅埗鍑犲崄澶**锛涜矾鐢卞叏鍫嗕竴涓枃浠躲**淇**锛氭娊 `ApiResult.Fail/Ok` 甯姪鍣紱鎸夎祫婧愭媶鍒嗚矾鐢辨ā鍧楋紱鑰佹帴鍙e綊鍏 `WebApi.Core(deprecated)` 骞惰鍒掕縼绉汇 + +### P2-3銆鏃ュ織浣撶郴涓嶇粺涓锛坄鎵弿鍛戒腑` 22 鏂囦欢 `Console.WriteLine`锛 +涓 `Diagnosis.Log` 娣风敤銆俙ModbusDoorController` 宸插叏绋 `Diagnosis.Log`锛屽簲浣滀负缁熶竴鑼冨紡鎺ㄥ箍锛涚鐢 `Console.WriteLine` 浜庝笟鍔′唬鐮併 + +### P2-4銆UI 涓庝笟鍔¤﹀悎锛坄鎵弿鍛戒腑`锛 +`MessageBox.Show` 鍑虹幇鍦 `Commons.cs`锛圱rafficControl.OnDeadLock锛夈乣Kiva.cs:648`銆乣DoorManager.cs`銆乣ButtonBoxManager.cs` 绛変笟鍔/绠$悊绫讳腑銆**淇**锛氫笟鍔″眰鍙彂浜嬩欢/鏃ュ織锛屽脊绐椾氦鐢 UI 灞傦紙鍚庣画 migu 骞冲彴锛夈 + +### P2-5銆`while(true)+Thread.Sleep` 蹇欑瓑/闃诲锛坄鎵弿鍛戒腑` 13 鏂囦欢锛 +`Forklift.cs`銆乣StandardChargeMission.cs`銆乣ChargeUdpService.cs`銆乣VDA5050Car.cs` 绛夈**淇**锛氭敼 `CancellationToken`+鍙腑鏂瓑寰呮垨瀹氭椂鍣紱涓 P0-1 鍗忎綔寮忓仠姝竴骞跺鐞嗐 + +### P2-6銆AsyncTcpClient 缁嗚妭锛坄绮捐纭`锛 +- `Send` 澶辫触鎶 `InvalidProgramException`锛堝紓甯哥被鍨嬩笉褰擄紝搴 `InvalidOperationException`/鑷畾涔夛級銆 +- `HandleDatagramWritten` 璋 `EndWrite` 鏃 try锛屽啓澶辫触寮傚父钀藉埌绾跨▼姹犳棤浜鸿娴嬨 +- `uint on = 1;` 鏈娇鐢ㄥ瓧娈碉紙CS0414锛夈 + +--- + +## 涓冦佺紪璇戝憡璀﹀垎绫伙紙鍏ㄩ噺锛屽弻閰嶇疆鍚堣 90 瀹炰緥 鈮 45/閰嶇疆锛 + +| 鍛婅鐮 | 鏁伴噺 | 鍚箟 | 澶勭疆 | +|---|---|---|---| +| CS0108 | 16 | 闅愯棌缁ф壙鎴愬憳鏈姞 `new` | 鏄惧紡 `new`/`override` 鎴栨敼鍚 | +| CS4014 | 14 | 璋冪敤鏈 `await`锛堝嵆鍙戝嵆寮冿級 | 鏄惧紡 `await` 鎴 `_ =` 骞惰鏄 | +| CS0414 | 14 | 绉佹湁瀛楁璧嬪间絾浠庢湭浣跨敤 | 鍒犻櫎 | +| SYSLIB0006 | 14 | `Thread.Abort` 宸插純鐢 | 瑙 **P0-1** | +| CS0105 | 6 | 閲嶅 using | 鍒犻櫎 | +| CS0162 | 6 | 涓嶅彲杈句唬鐮 | 娓呯悊 | +| CS0168 | 6 | 鍙橀噺澹版槑鏈敤 | 鍒犻櫎 | +| CA2200 | 4 | `throw ex` 澶辨爤 | 鏀 `throw;` | +| CS8321 | 2 | 灞閮ㄥ嚱鏁版湭鐢 | 鍒犻櫎锛堝 VDA5050Car `monitor()`锛 | +| CS0169 | 2 | 瀛楁浠庢湭浣跨敤 | 鍒犻櫎 | +| CS0114 | 2 | 闅愯棌缁ф壙鎴愬憳锛堝 ForceStop锛 | `override`/`new` | +| CS0219 | 2 | 鍙橀噺璧嬪兼湭鐢 | 鍒犻櫎 | +| CS0649 | 2 | 瀛楁浠庢湭璧嬪 | 鍒濆鍖栨垨鍒犻櫎 | + +--- + +## 鍏佸垎瀛愮郴缁熻瘎杩帮紙鎽樿锛岃鐗堣鏋舵瀯鏂规锛 + +- **CarTypes**锛歚BasicCarFields/SiteFields/TrackFields/PlanFields` 瀛楁琚嬭璁″悎鐞嗭紙`BasicFields.cs`锛宍CarLength/CarWidth` 榛樿 `-1` 浣"鏈厤缃"鍝ㄥ叺锛屽凡琚伩闅 Coder 姝g‘鍒╃敤锛夛紱浣嗗叿浣撹溅鍨嬶紙Kiva/Forklift/VDA5050Car锛夋槸宸ㄧ被锛屾贩鏉傞氫俊銆佺姸鎬併乁I銆佽皟搴︼紝async void/绌 catch/纭紪鐮侀泦涓 +- **Coders**锛氭湰杞凡瀹屾垚纾佸鑸粺涓涓庨伩闅滃幓閲嶏紙`AvoidanceParamCoder` 4 鍙 + `AvoidanceParamLWCoder` 2 鍙傦級锛岀粨鏋勬竻鏅帮紝寤鸿缁х画鎶婅溅鍨嬪唴鑱 Coder 鏀舵暃鍒 `CommonTrackCoders`銆 +- **Missions**锛欳hained/InterLock/Scheduler 绾跨▼妯″瀷绮楁斁锛堣8 `new Thread`+`Abort`+`while(true)`锛夛紝鐘舵佹満鏁h惤瀛楃涓 `status.status`銆傚缓璁粺涓 `MissionRunnerBase`锛圕ancellationToken + 鐘舵佹灇涓撅級銆 +- **Charge**锛氳В鏋愬瓨鍦"瀹夊叏鑼冩湰"涓"椋庨櫓璺緞"骞跺瓨锛堣 P0-3锛夛紱UDP 鏈嶅姟涓 Mission 绾跨▼鑰﹀悎銆 +- **Devices锛堟柊锛**锛歚ModbusDoorController` 浼樼锛涘敮涓鐟曠柕鏄敤鏋愭瀯鍑芥暟鍏滃簳 `Disconnect`锛堝湪 GC 绾跨▼鍙栭攣+`Wait`锛屾湁椋庨櫓锛夛紝搴斿疄鐜 `IDisposable` 鏄惧紡閲婃斁銆 +- **VDA5050**锛歁QTT/HTTP 寮傛鐢ㄦ硶涓嶈鑼冿紙async void銆乼hrow ex銆佺‖缂栫爜 IP銆丆onsole 鏃ュ織锛夛紝鐘舵佸鐞 `ProcessCacheAndSendOrderMessage` 鍐呭ぇ閲 Console銆 +- **WebApi**锛氳 P0-2 / P2-2锛屾渶楂樹紭鍏堢骇閲嶆瀯瀵硅薄銆 + +--- + +## 涔濄佹闈㈡牱鏉匡紙寤鸿浣滀负鍥㈤槦鍩虹嚎锛 + +1. `StandardScene.Devices/Door/ModbusDoorController.cs`锛歚CancellationTokenSource` 鍗忎綔寮忓仠姝€乣lock` 绾跨▼瀹夊叏銆佸彉鏇存娴嬶紙`_lastSentControl`锛夈侀噸杩炶妭娴併佺粺涓 `Diagnosis.Log`銆佽祫婧愭竻鐞嗐 +2. `StandardScene.Core/TCP/AsyncTcpClient.cs`锛氶攣淇濇姢銆侀檲鏃у洖璋冮槻鎶ゃ佽嚜鍔ㄩ噸杩炪 +3. `StandardScene.Core/Charge/CommunicationMessageService.cs`锛氭姤鏂囪В鏋愬墠缃暱搴︽牎楠岋紙瀹夊叏瑙f瀽鑼冩湰锛夈 + +--- + +## 鍗併佹暣鏀硅矾绾垮浘 + +**绗 1 鎵癸紙P0锛屾湰娆℃墽琛岋級** +1. Thread.Abort 鈫 鍗忎綔寮忓仠姝紙6 鏂囦欢 8 澶勶級銆 +2. WebApi 鍙嶅皠 execute 鍔犳潵婧愭牎楠 + `[MethodMember]` 鐧藉悕鍗曘 +3. 鍏呯數瀹炴椂鎶ユ枃璺緞琛ラ暱搴︽牎楠岋紙澶嶇敤瀹夊叏瑙f瀽锛夈 + +**绗 2 鎵癸紙P1锛** +4. 纭紪鐮佺鐐归厤缃寲锛圴DA5050/Kiva/Forklift/Map/Loop/CAD锛夈 +5. `async void`鈫抈Task`銆乣throw ex`鈫抈throw`銆佷笟鍔$┖ catch 鍔犳棩蹇椼 +6. HttpClient 鍗曚緥鍖栵紱sync-over-async 鎷嗚В銆 + +**绗 3 鎵癸紙P2/缁撴瀯锛** +7. `ApiResult` 甯姪鍣 + WebApi 鎷嗗垎锛沗Commons` 鎷嗘湇鍔°佷慨 `CarValue`/`GoSite`/`AddOrUpdate`銆 +8. 缁熶竴 `Diagnosis.Log`锛沀I 瑙h︼紱`MissionRunnerBase` 缁熶竴绾跨▼/鐘舵佹満銆 + +**绗 4 鎵癸紙P3 鍗敓锛** +9. 娓 45 椤瑰憡璀︼紙閲嶅 using銆佹湭鐢ㄥ瓧娈点佷笉鍙揪浠g爜銆侀殣钘忔垚鍛橈級銆 + +--- + +## 闄勫綍锛氭壂鎻忔柟娉 + +- 鍙嶆ā寮忥細`rg` 鎵弿 `Thread.Abort` / `catch\s*\{\s*\}` / `throw ex;` / `async void` / `\.Result|\.Wait\(\)` / 纭紪鐮 IP / `Console.WriteLine` / `while\s*\(\s*true\s*\)` / `new HttpClient` / `MessageBox.Show` / 榄旀硶涓嬫爣銆 +- 鍛婅锛歚dotnet build --no-incremental` 鈫 `_buildwarnings.txt` 鈫 鎸夊憡璀︾爜鑱氬悎璁℃暟銆 +- 绮捐锛欰syncTcpClient銆丆ommons銆丆ommunicationMessageService銆乂DA5050Car銆乄ebApi銆並iva銆丄bstractLoopMission銆丮odbusDoorController銆丅asicFields 绛夈 diff --git a/StandardScene浼氳瘽14浜ゆ帴鎽樿.md b/StandardScene浼氳瘽14浜ゆ帴鎽樿.md new file mode 100644 index 0000000..323c853 --- /dev/null +++ b/StandardScene浼氳瘽14浜ゆ帴鎽樿.md @@ -0,0 +1,241 @@ +# StandardScene 鎻掍欢鍖栨媶鍒 鈥 浼氳瘽14 浜ゆ帴鎽樿 + +> **鐢ㄩ**锛氫緵鍏朵粬 AI 浼氳瘽蹇熻鍙栦笂涓嬫枃锛岀户缁帹杩 StandardScene 澶ф敼鎴 SimpleLite 鍐呮牳鏀归犮 +> **宸ヤ綔鐩綍**锛歚E:\Work\Core\Simple-FR\StandardSence` +> **涓讳氦浠樼墿**锛歚StandardScene鎷嗗垎璁″垝.md`锛**v2**锛屽凡钀界洏锛 +> **涓婃父璁捐**锛歚E:\Work\Core\Simple-FR\閰嶇疆鍚戝涓庡鑸満鏅彃浠跺寲璁捐.md`锛堢 5 鑺 StandardScene 鎷嗗垎銆佺 11.3 鑺 Phase C锛 +> **鏂板涓 API 鏂囨。**锛歚E:\Work\Core\Simple-FR\Simple\SimpleLite\Docs\MIGU-API.md` +> **鏁寸悊鏃堕棿**锛2026-06-09 + +--- + +## 1. 鏈細璇濆仛浜嗕粈涔 + +| 闃舵 | 鍐呭 | 鐘舵 | +|---|---|---| +| 鍚姩 | 鐢ㄦ埛瑕佹眰鐢 wuxianChat 寤虹珛銆屼細璇14銆 | 鏇惧皾璇 MCP锛屼竴搴﹀洜鎵ц鍚庣涓嶅彲鐢ㄥけ璐 | +| 绮捐浠g爜 | 瀵 `StandardSence` 绾 110 涓 `.cs` 鍋氶愭ā鍧楄蛋鏌ワ紙杞﹀瀷銆丮ission銆佽澶囥乄ebApi銆乂DA5050 绛夛級 | 鉁 瀹屾垚 | +| 浜у嚭 v1 璁″垝 | 鍐欏叆 `StandardScene鎷嗗垎璁″垝.md`锛氱洰褰曡鍒掋佸姛鑳藉綊灞炵煩闃点佹娊绂绘竻鍗曘佹妧鏈毦鐐广佸垎闃舵璺嚎 | 鉁 瀹屾垚 | +| 鐢ㄦ埛璇勫鍙嶉 | 瀹夸富鏀 SimpleLite銆乶et8.0銆佽澶囩嫭绔 dll+鐑彃鎷斻佸垹闄 FactoryTest/鏉剧伒銆乄ebApi 鏆傜暀鍚庢湡搴熷純 | 鉁 宸茬撼鍏 | +| 闃呰 SimpleLite | 纭 `SimpleLite.csproj` = net8.0 / CycleGUI / EmbedIO锛涢槄璇 MIGU-API 鍋 WebApi 杩佺Щ绱㈠紩 | 鉁 瀹屾垚 | +| 浜у嚭 v2 璁″垝 | 鍏ㄦ枃鏇存柊 `StandardScene鎷嗗垎璁″垝.md` | 鉁 瀹屾垚 | +| 浠g爜鏀瑰姩 | **鏃**锛堟湰浼氳瘽浠呭垎鏋愪笌鏂囨。锛屾湭鏀逛笟鍔′唬鐮侊級 | 鈥 | + +--- + +## 2. 鐢ㄦ埛鍘熷璇夋眰锛堟寜鏃堕棿锛 + +### 2.1 绗竴杞 + +> 鏁翠釜浠撳簱闇瑕佸ぇ鏀广傚弬鑰 `閰嶇疆鍚戝涓庡鑸満鏅彃浠跺寲璁捐.md` 涓叧浜 StandardScene 鐨勩屽鑸満鏅彃浠跺寲銆嶉儴鍒嗭紱绮剧粏鍖栬褰撳墠浠g爜锛屾娊绂绘爣鍑嗗姛鑳斤紝鍋氬ソ浠撳簱鐩綍瑙勫垝涓庡姛鑳借鍒掞紝**鍏堟妸 plan 鍋氬嚭鏉**鍒 StandardScene 鏈湴 md 鏂囦欢銆 + +### 2.2 绗簩杞紙璇勫鍙嶉锛屽凡鍐欏叆 v2锛 + +1. **璁惧椹卞姩**闇瑕佺嫭绔 dll锛屽苟涓**鏀寔鐑嵏杞藉拰鍔犺浇**銆 +2. StandardScene 渚濊禆 **`SimpleLite.dll`**锛岃 **`SimpleComposer` 搴熷純**锛**鍙︿竴涓 AI 瀵硅瘽绐楀彛**姝e湪鏀圭浉鍏冲紩鐢級銆 +3. **鐩爣妗嗘灦缁熶竴 `net8.0`**锛圵inForms 鍦烘櫙闇 `net8.0-windows` 鐨勮鏄庡湪璇勫涓淇锛歋impleLite 鏈韩鏄函 `net8.0` + CycleGUI锛屾晠 StandardScene 搴**鍘 WinForms**锛夈 +4. **`FactoryTest`銆佹澗鐏垫畫鐣**涓嶇撼鍏 Core锛**鐩存帴鍒犻櫎**銆 +5. **`WebApi.cs`** 鏄拡瀵硅佸钩鍙扮殑 Nancy 鎺ュ彛锛氭暣鐞嗗悗**鏆傛椂淇濈暀鍦 Core**锛屽悗鏈熷簾寮冿紝鏀圭敤 SimpleLite 鎺ュ彛鑳藉姏锛圡IGU-API.md锛夛紱**姝ょ増鏈厛淇濈暀**銆 + +### 2.3 绗笁杞 + +> 鎶婁互涓婁細璇濇暣鐞嗘垚 md 鏂囦欢锛屽垎浜粰鍙︿竴涓細璇濊鍙栥 +> 鈫 鍗虫湰鏂囨。銆 + +--- + +## 3. 鏍稿績缁撹锛堝彟涓浼氳瘽蹇呴』鐭ラ亾鐨勶級 + +### 3.1 鏋舵瀯淇锛氬鑸 鈮 杞﹀瀷 + +涓婃父璁捐闅愬惈銆屾寜杞﹀瀷/瀵艰埅鏁村寘鍒 dll銆嶏紝**涓庝唬鐮佷簨瀹炰笉绗**锛 + +- 瀵艰埅鐢 **杞ㄩ亾/绔欑偣 fields + TrackCoder** 鍐冲畾锛屼笌杞﹀瀷**姝d氦**銆 +- 渚嬶細`Kiva` 鍚屼竴绫讳笂鍚屾椂鎸 **纾佸鑸 coder**銆**浜岀淮鐮 coder**銆**婵鍏夐伩闅 coder**锛堥潪 SLAM 瀹氫綅锛夈 +- **姝g‘鎷嗘硶**锛歚StandardScene.Core` 淇濈暀杞﹀瀷鏈綋 + 浠诲姟/鍏呯數/浜掗攣绛夛紱瀵艰埅鑳藉姏鎶藉埌 `Magnetic` / `QrCode` / `Laser` 绛 dll锛屼互**鍙彃鎷 coder** 褰㈠紡鎸傝浇銆 + +### 3.2 婵鍏夛細閬块殰 vs 瀵艰埅瀹氫綅 + +| 鑳藉姏 | 浠h〃 | 褰掑睘 | +|---|---|---| +| 婵鍏夐伩闅 | `LidarArea`銆乣SwitchLidarArea`銆佸悇杞﹀瀷閫氱敤 | **Core** | +| 婵鍏 SLAM 瀹氫綅/鍦板浘 | `LidarMap`銆乣getLidarMap`銆佹媺 `127.0.0.1:4321` | **Laser dll** | + +### 3.3 涓変釜姝d氦缁村害 + +1. **瀵艰埅**锛氱 / 浜岀淮鐮 / 婵鍏夛紙SLAM锛 +2. **杞﹀瀷**锛欿iva銆佸弶杞︺佸鑸佃疆椤跺崌銆佹満姊拌噦銆佷豢鐪熻溅绛 +3. **璁惧椹卞姩**锛氬厖鐢垫々锛團L/MuXing/PCB锛夈侀棬锛圡odbus锛夈佹寜閽洅锛圠eeg/Azowie锛 + +璁惧椹卞姩蹇呴』 **鐙珛 `Devices.*` dll + 鐑彃鎷**锛圫impleLite `/plugins` + collectible ALC锛夈 + +### 3.4 VDA5050 + +- `CarTypes/VDACar/` 鏄畬鏁 **MQTT 鍗忚鏍**锛屽缓璁嫭绔 `StandardScene.Protocol.VDA5050` dll銆 +- 鑰﹀悎鐐癸細`ArmCar` 寮曠敤 `VDA5050SiteField`锛屾媶鍒嗘椂闇瑙h︺ + +### 3.5 瀹夸富涓庢鏋讹紙v2 宸插畾锛 + +| 椤 | 鏃 | 鏂 | +|---|---|---| +| 瀹夸富 | `SimpleComposer.exe` (.NET 4.8) | **`SimpleLite`** (net8.0) | +| 濂戠害 | `RefSimpleCore.dll` + Composer 绋嬪簭闆 | **`SimpleCore` + SimpleLite** | +| UI | WinForms锛堝ぇ閲忕獥浣擄級 | **CycleGUI / 骞冲彴 Web**锛堝幓 WinForms锛 | +| 瀵瑰 API | `WebApi.cs` (Nancy 1.4.5) | 鏆傜暀 Core锛涘悗鏈 **EmbedIO**锛圡IGU-API锛 | + +**SimpleLite 浜嬪疄**锛堟潵鑷 `SimpleLite.csproj`锛夛細`TargetFramework=net8.0`锛宍OutputType=Exe`锛孶I 鐢 CycleGUI锛學ebApi 鐢 EmbedIO锛屽紩鐢 `SimpleCore`銆 + +### 3.6 鑰 WebApi 澶勭疆 + +- `WebApi.cs` ~123KB锛40+ 绔偣锛**涓庡鑸急鐩稿叧**锛堝鑸浉鍏充富瑕佹槸 `QrMap`銆乣getLidarMap`锛夈 +- **鏈増**锛氭暣鐞嗕负 `WebApi.Core.cs` 鏆傜暀 Core锛屾爣 `[Obsolete]` / deprecated銆 +- **鍚庢湡**锛氭寜 `StandardScene鎷嗗垎璁″垝.md` 搂4.7 鏄犲皠鍒 SimpleLite `/api/sl/projection/*`銆 +- **缂哄彛**锛氫簩缁寸爜鍦板浘涓嬪彂銆佹縺鍏 SLAM 鍙栧浘鍦 MIGU-API **鏆傛棤鐩存帴瀵瑰簲**锛岃縼绉诲墠闇鍦 SimpleLite 鎴栧満鏅彃浠朵晶琛ユ帴鍙c + +### 3.7 蹇呴』鍒犻櫎鐨勬枃浠讹紙涓嶈繘 Core/Customer锛 + +| 鏂囦欢 | 鍘熷洜 | +|---|---| +| `FactoryTest.cs` | 浜ф祴涓撶敤 | +| 鏍圭洰褰 `SongLingDeliveryViewer.Designer.cs` | 鏉剧伒瀹㈡埛娈嬬暀锛屾棤涓绘枃浠躲佹湭缂栧叆 csproj | +| 鏍圭洰褰 `MultiWheelLifterCar.cs` | 涓 `CarTypes/MultiWheelLifterCar.cs` 閲嶅悕姝绘枃浠讹紝鏈紪鍏 csproj | +| `CarTypes/UselessCar.cs` | `[CarType]` 宸叉敞閲婏紝娴嬭瘯娈嬬暀 | + +--- + +## 4. 鐩爣 dll 缁撴瀯锛堟憳瑕侊級 + +``` +StandardScene.Core/ net8.0 鍩哄骇锛坅lwaysLoad锛 +StandardScene.Magnetic/ 纾佸鑸 +StandardScene.QrCode/ 浜岀淮鐮佸鑸 + SyncQrMap +StandardScene.Laser/ SLAM / LidarMap +StandardScene.Protocol.VDA5050/ VDA5050 MQTT 鏍 +StandardScene.Devices.Charge/ 鍏呯數妗╅┍鍔紙鐑彃鎷旓級 +StandardScene.Devices.Door/ 闂ㄦ帶椹卞姩锛堢儹鎻掓嫈锛 +StandardScene.Devices.ButtonBox/ 鎸夐挳鐩掗┍鍔紙鐑彃鎷旓級 +``` + +鍚勫彲婵娲诲鑸/璁惧 dll 閰 `scene.json`锛屽鎺 SimpleLite `POST /api/sl/projection/scenes/apply` 涓 `active-scenes.json`銆 + +**璇︾粏鏂囦欢绾у綊灞炵煩闃**瑙 `StandardScene鎷嗗垎璁″垝.md` 搂4锛屽嬁鍦ㄦ湰鎽樿閲嶅灞曞紑銆 + +--- + +## 5. 鍙娊绂荤殑閲嶅浠g爜锛圕0 浼樺厛锛 + +| 閲嶅椤 | 鐜扮姸浣嶇疆 | 鐩爣 | +|---|---|---| +| 纾佸惊杩瑰櫒 | `Kiva.AllCarMagTrackCoder` + `MultiWheelLifterCar.MagTrackCoder` | 鍚堝苟涓 `Magnetic.MagneticTrackCoder` | +| `newReset` / `newTrafficReset` | Kiva銆丗orklift銆丮ultiWheelLifterCar 鍚勪竴浠 | 涓婃彁 `StandardCarBase` | +| `SetDisplayInfo`銆佽繙绋嬫ュ仠/澶嶄綅 HTTP | 澶氳溅鍨嬮浄鍚 | Core 鍩虹被榛樿瀹炵幇 | +| `GetCarStatus` | Commons 涓 MultiWheelForkLifter 鍚勪竴浠 | 缁熶竴 Commons | +| 閬块殰/IO/绾犲亸 coder 妯℃澘 | 鍚勮溅鍨嬬矘璐 | Core 閫氱敤 coder 妯℃澘闆 | + +--- + +## 6. 鎶鏈毦鐐癸紙闇涓庡唴鏍镐細璇濆崗鍚岋級 + +### 6.1 TrackCoder 杩愯鏈熸敞鍐岋紙鏈鍏抽敭锛 + +- **鐜扮姸**锛歚[TemplateTrackCoderSettings]` / `[ProgramTrackCoderSettings]` **缂栬瘧鏈熺‖缁戝畾**鍦ㄨ溅鍨嬬被涓娿 +- **鐩爣**锛氬鑸 dll 鍔犺浇鏃朵负杞﹀瀷**娉ㄥ唽** coder锛屽嵏杞芥椂绉婚櫎銆 +- **鏂规**锛氫紭鍏堝湪 **`SimpleCore` 澧炲姞 coder 娉ㄥ唽琛**锛涘鑸 dll 鍦 `INavigationProfile.OnActivate` 娉ㄥ唽銆 +- **鍏滃簳**锛欳ore 鏆備繚鐣欏叏閲 coder锛屽鑸 dll 鍙壙杞 API/鍦板浘/娓呭崟锛堜繚璇佷笉鍥炲綊锛夈 + +### 6.2 net4.8 鈫 net8.0 + 鍘 WinForms + +- 寮曠敤浠 `SimpleComposer.exe` 鍒囧埌 `SimpleLite` / `SimpleCore`銆 +- 鍛藉悕绌洪棿 `SimpleComposer.RCS` 鈫 `SimpleLite.RCS` 绛夛紙**鍙︿竴浼氳瘽杩涜涓**锛夈 +- 鎵鏈 WinForms 绐椾綋杩 **CycleGUI** 鎴栧钩鍙 Web锛沗MessageBox` 鏀 CycleGUI 寮圭獥銆 +- 娑夊強绐椾綋锛歚DeliveryViewer`銆乣LoopViewer`銆乣TrafficInterlockViewer`銆乣VehicleMonitor`銆佸悇 `Charge/*Form`銆乣DoorMonitor`銆乣ButtonBoxManager`銆乣VDACar/TextViewer` 绛夈 + +### 6.3 鐑彃鎷旀竻鐞 + +璁惧/瀵艰埅 dll 鍗歌浇鍓嶉』 `OnDeactivate`锛氭竻鐞 coder 娉ㄥ唽銆丮QTT/TCP 杩炴帴銆佸叏灞鍥炶皟锛涢厤鍚 collectible ALC锛岀‘淇濇棤瀛樻椿 `Car`/`Mission` 瀹炰緥銆 + +--- + +## 7. 鍒嗛樁娈靛疄鏂斤紙褰撳墠鍧囨湭鍚姩浠g爜锛 + +| 闃舵 | 鍐呭 | 鐘舵 | +|---|---|---| +| **C0** | 鍒犻櫎娈嬬暀 + 鍘婚噸娌夋穩 + 鍛藉悕绌洪棿鏀舵暃 + 涓 SimpleCore 鍐呮牳瀵归綈 | 猬 寰呭惎鍔 | +| **C1** | 鏂板缓 `StandardScene.Core`(net8.0)锛屾崲瀹夸富锛屽幓 WinForms锛屾暣鐞 WebApi.Core | 猬 | +| **C2** | Magnetic / QrCode / Laser 涓夊鑸 dll | 猬 | +| **C3** | Devices.Charge / Door / ButtonBox锛堢儹鎻掓嫈锛 | 猬 | +| **C4** | Protocol.VDA5050 | 猬 | +| **C5** | 鑱旇皟 + WebApi 杩 SimpleLite + 鏂囨。鏀跺熬 | 猬 | + +--- + +## 8. 寰呯‘璁ら」锛堣瘎瀹℃湭鎷嶆澘锛 + +1. **SimpleCore 鏄惁鎻愪緵 TrackCoder 杩愯鏈熸敞鍐 API**锛燂紙鍐冲畾 C2 鐢ㄦ敞鍐岃〃杩樻槸鍏滃簳鏂规锛 +2. **UI 杩佺Щ鑺傚**锛氫竴娆℃ц縼 CycleGUI锛岃繕鏄寜 dll 鍒嗘壒锛涘摢浜涗粎淇濈暀骞冲彴 Web锛 +3. **VDA5050** 鏄惁纭鐙珛 dll锛燂紙璁″垝鎺ㄨ崘鐙珛锛 +4. **浜岀淮鐮佸湴鍥句笅鍙 / 婵鍏 SLAM 鍙栧浘** 鍦 SimpleLite 渚х敱璋佽ˉ鎺ュ彛銆佷綍鏃惰ˉ锛 +5. 鏄惁瀛樺湪**杞﹀瀷浠呮敮鎸佸崟涓瀵艰埅**鐨勭‖绾︽潫锛 + +--- + +## 9. 璺ㄤ細璇濆垎宸ワ紙閲嶈锛 + +| 浼氳瘽/鏂瑰悜 | 璐熻矗鍐呭 | 涓庢湰璁″垝鍏崇郴 | +|---|---|---| +| **鏈細璇濓紙浼氳瘽14锛** | StandardScene 浠g爜绮捐 + 鎷嗗垎璁″垝 v1/v2 | 涓绘枃妗e凡钀界洏 | +| **鍙︿竴 AI 浼氳瘽锛堢敤鎴锋彁鍙婏級** | SimpleLite / SimpleCore 寮曠敤鏀归犮乣SimpleComposer` 鈫 `SimpleLite` 鍛藉悕绌洪棿涓庣▼搴忛泦寮曠敤 | StandardScene C1 渚濊禆鍏惰繘搴 | +| **骞冲彴 / 閰嶇疆鍚戝** | `WizardController`銆乣data/config-deployment.json` | 閫氳繃 `/scenes/apply` 椹卞姩鎻掍欢鍔犺浇 | + +**鍗忎綔鎺ュ彛寤鸿**锛 + +- StandardScene 渚х瓑寰咃細SimpleCore 鐨 **coder 娉ㄥ唽琛**銆**鎻掍欢鐢熷懡鍛ㄦ湡**锛圤nActivate/OnDeactivate锛夈**Collectible ALC** 绾﹀畾銆 +- 鍐呮牳渚х瓑寰咃細StandardScene 鐨 **scene.json 娓呭崟**銆**NavKind 鏋氫妇**銆佸悇 dll 鐨 **provides** 瀛楁锛堣鎷嗗垎璁″垝 搂9锛夈 + +--- + +## 10. 鍏抽敭鏂囦欢绱㈠紩 + +| 璺緞 | 璇存槑 | +|---|---| +| `E:\Work\Core\Simple-FR\StandardSence\StandardScene鎷嗗垎璁″垝.md` | **涓昏鍒掞紙v2锛**锛屽惈褰掑睘鐭╅樀銆伮4.7 WebApi 杩佺Щ琛ㄣ佸垎闃舵銆侀闄 | +| `E:\Work\Core\Simple-FR\StandardSence\StandardScene浼氳瘽14浜ゆ帴鎽樿.md` | **鏈枃妗**锛屼細璇濈骇浜ゆ帴 | +| `E:\Work\Core\Simple-FR\閰嶇疆鍚戝涓庡鑸満鏅彃浠跺寲璁捐.md` | 涓婃父妗嗘灦璁捐 | +| `E:\Work\Core\Simple-FR\Simple\SimpleLite\Docs\MIGU-API.md` | SimpleLite 鏂 API锛圵ebApi 杩佺Щ鐩爣锛 | +| `E:\Work\Core\Simple-FR\Simple\SimpleLite\SimpleLite.csproj` | 瀹夸富锛歯et8.0 / CycleGUI / EmbedIO | +| `E:\Work\Core\Simple-FR\StandardSence\StandardScene.csproj` | 鐜扮姸锛歯et4.8 鍗曚綋鎻掍欢 | +| `E:\Work\Core\Simple-FR\StandardSence\Commons.cs` | 鎻掍欢濂戠害銆乣CustomOperationsBeforeLoading`銆乣NoReflectionApi` | +| `E:\Work\Core\Simple-FR\StandardSence\WebApi.cs` | 鑰 Nancy API锛堝緟 deprecated锛 | +| `E:\Work\Core\Simple-FR\StandardSence\CarTypes\Kiva.cs` | 瀵艰埅涓庤溅鍨嬭﹀悎鐨勫吀鍨嬫牱鏈 | +| `E:\Work\Core\Simple-FR\StandardSence\CarTypes\BasicFields.cs` | `TagValue` 绛夎建閬/绔欑偣瀛楁瀹氫箟 | + +--- + +## 11. 缁欎笅涓浼氳瘽鐨勬帹鑽愯捣鎵嬪紡 + +鑻ョ户缁 **StandardScene 瀹炴柦**锛 + +1. 鍏堣 `StandardScene鎷嗗垎璁″垝.md` 搂0锛圱L;DR锛夊拰 搂4锛堝綊灞炵煩闃碉級銆 +2. 浠 **C0** 寮濮嬶細鍒犻櫎 搂3.7 鎵鍒楁枃浠讹紱鍚堝苟纾佸惊杩瑰櫒锛涙彁鍙 `StandardCarBase`锛沗AMRScene1` 鍛藉悕绌洪棿鏀舵暃銆 +3. 涓 **SimpleLite 鏀归犱細璇**瀵归綈锛氬紩鐢ㄦ槸鍚﹀凡鍙紪璇戙乧oder 娉ㄥ唽琛 API 鏄惁灏辩华銆 +4. **涓嶈**鍦ㄦ湭纭鍓嶅ぇ瑙勬ā鏀 WebApi 鎴 WinForms鈥斺擟1 鎵嶇郴缁熸у鐞嗐 + +鑻ョ户缁 **SimpleLite / SimpleCore 鏀归**锛 + +1. 浼樺厛钀藉疄 **TrackCoder 杩愯鏈熸敞鍐**銆**鎻掍欢 OnActivate/OnDeactivate**銆**/plugins 鐑嵏杞** 涓 StandardScene 璁″垝 搂7 瀵归綈銆 +2. 璇勪及 MIGU-API 鏄惁闇琛 **QrMap**銆**getLidarMap** 绛変环绔偣銆 +3. 纭 `SimpleComposer.RCS` 鈫 `SimpleLite.RCS` 杩佺Щ鑼冨洿锛岄伩鍏 StandardScene 寮曠敤鏂銆 + +--- + +## 12. 鍙樻洿璁板綍 + +| 鐗堟湰 | 鏃ユ湡 | 璇存槑 | +|---|---|---| +| v1 | 2026-06-09 | 鍒濈増鎷嗗垎璁″垝锛堜唬鐮佺簿璇荤粨璁猴級 | +| v2 | 2026-06-09 | 绾冲叆 SimpleLite/net8.0/鍘 WinForms銆佽澶囩儹鎻掓嫈銆佸垹闄ら」銆乄ebApi 鏆傜暀+杩佺Щ绱㈠紩 | +| 浜ゆ帴鎽樿 | 2026-06-09 | 鏈細璇濇暣鐞嗭紝渚涜法浼氳瘽闃呰 | + +--- + +*鏈枃妗d负浼氳瘽绾ф憳瑕侊紱瀹炴柦缁嗚妭銆佸畬鏁寸被绾у綊灞炰笌 WebApi 绔偣鏄犲皠浠 `StandardScene鎷嗗垎璁″垝.md` 涓哄噯銆* diff --git a/StandardScene鎷嗗垎璁″垝.md b/StandardScene鎷嗗垎璁″垝.md new file mode 100644 index 0000000..3c0c9b4 --- /dev/null +++ b/StandardScene鎷嗗垎璁″垝.md @@ -0,0 +1,613 @@ +# StandardScene 鎻掍欢鍖栨媶鍒嗚鍒掞紙瀵艰埅鍦烘櫙鎻掍欢鍖 路 Phase C 钀藉湴锛 + +> 鐗堟湰锛氳崏妗 **v2**锛堟寜璇勫鍙嶉鏇存柊锛 +> 缂栧啓渚濇嵁锛氬褰撳墠宸ョ▼ `E:\Work\Core\Simple-FR\StandardSence`锛.NET Framework 4.8 鍗曚綋鎻掍欢锛夌殑**閫愭枃浠剁簿璇**銆 +> 涓婃父璁捐锛氭壙鎺ュ苟缁嗗寲銆婇厤缃悜瀵间笌瀵艰埅鍦烘櫙鎻掍欢鍖栬璁.md銆嬬 5 鑺傘孲tandardScene 鎷嗗垎銆嶄笌绗 11.3 鑺傘孭hase C銆嶃 +> 鏈枃鐩爣锛氭妸涓婃父"妗嗘灦绾"鎷嗗垎鎰忓浘锛岃惤鍦颁负**鍩轰簬鐪熷疄浠g爜浜嬪疄銆佸彲鐩存帴鎵ц**鐨勬枃浠剁骇/绫荤骇鎷嗗垎璁″垝銆 +> 鑼冨洿澹版槑锛氭湰鏂囧師涓**璁″垝鏂囨。**銆**鑷屼細璇1銆(2026-06-09) 璧峰凡寮濮嬭惤鍦板疄鏂**锛屽疄闄呰繘搴︿笌浠g爜鐜扮姸瑙 **搂11 瀹炴柦杩涘害璁板繂**锛堝惈宸茶惤鍦版敼鍔ㄣ佸綋鍓嶅彲缂栬瘧鐘舵併佸緟纭闃诲椤癸級銆 + +### 鈿狅笍 鏋舵瀯鍐崇瓥鍙樻洿锛2026-06-12锛岀敤鎴锋媿鏉匡級锛氥岀 / 浜岀淮鐮 / 婵鍏夈嶄笁鍦烘櫙 鈫 **涓ゅ満鏅钩鍙** + +鍘熻鍒掔殑 `Magnetic` / `QrCode` / `Laser` 涓変釜瀵艰埅 dll 璋冩暣涓 **涓や釜鍦烘櫙骞冲彴鎻掍欢**锛堝凡钀藉湴锛屽叏瑙e喅鏂规 0 閿欒锛夛細 + +| 鎻掍欢 dll | scene id | 鍐呭 | 璇存槑 | +|---|---|---|---| +| `StandardScene.Magnetic.dll` | `scene.mag` | Kiva銆丮ultiWheelLifterCar + `MagneticTrackCoder` | 纾佸鑸钩鍙帮紙淇濈暀杞﹀瀷涓婄殑 Qr 鍦版爣娈佃兘鍔涳級 | +| `StandardScene.QrLidar.dll` | `scene.qrlidar` | Forklift銆丮ultiWheelForkLifter銆丏ualLiftingCar銆丮ultiVehicleCar銆丄rmCar + `SyncQrMap` | 婵鍏+浜岀淮鐮佽瀺鍚堝钩鍙帮細婵鍏夊潗鏍囧鑸敱鍐呮牳 `GhostCar.BasicGo` 鍏滃簳锛宍QrGo` 鎸夎建閬 tag 閫愭瑙﹀彂锛**铻嶅悎鎴栧崟鐙娇鐢ㄥ潎鍙** | +| `StandardScene.Devices.dll` | `scene.device` | 涓嶅彉 | 娓呭崟 id 鐢 `devices` 瑙勮寖鍖 | +| `StandardScene.Protocol.VDA5050.dll` | `scene.vda5050` | 涓嶅彉 | 娓呭崟 id 鐢 `protocol.vda5050` 瑙勮寖鍖 | + +鍏抽敭鏈哄埗缁撹涓庨厤濂楁敼鍔細 + +1. **杞﹀瀷鎸夊钩鍙板綊浣**锛堟帹缈 v2 "杞﹀瀷鐣欏熀搴"锛夛細渚濇嵁 = 椤圭洰瀛樻。鎸**鐭被鍚**锛坄JSONLoader` 鐢 `type.Name.ToLower()`锛夊尮閰嶈溅鍨嬶紝绫昏縼 dll 涓嶇牬鍧忔棫瀛樻。锛沜oder 鐗规 `GetCustomAttributes(inherit:true)` 娌跨户鎵块摼鏀堕泦锛屽唴鏍 `GhostCar` 鐨 BasicGo 澶╃劧琚墍鏈夎溅鍨嬬户鎵裤 +2. **鍏变韩瀛楁琚嬩笅娌夊熀搴**锛歚KivaFields.cs` / `MultiWheelLifterFields.cs`锛圓rmCar銆丮ultiVehicleCar 璺ㄥ钩鍙扮户鎵垮畠浠級锛沗IScriptErrorRecoverable` 鎺ュ彛瑙h `AbstractLoopMission` 瀵 Kiva 鐨勫弽鍚戜緷璧栥 +3. **娓呭崟鍛藉悕淇**锛氬唴鏍稿彧璇嗗埆 `.scene.json`锛屽師瑁 `scene.json` 姘歌繙涓嶄細琚壂鎻忓埌鈥斺斿凡鍏ㄩ儴鏀瑰悕骞惰鑼 id銆 +4. **鍐呮牳閰嶅**锛圫imple 浠撳簱锛夛細鈶 `Startup.LoadPlugins` 涓ら樁娈靛姞杞解斺旀竻鍗 `requiresCore` 澹版槑鐨勫熀搴э紙`StandardScene.dll`锛夎嚜鍔ㄥ苟鍏 alwaysLoad 涓 **non-collectible 鍏堣鍔犺浇**锛堝惁鍒欒鐢熸彃浠剁殑 collectible ALC 瑙f瀽涓嶅埌鍩哄骇锛岃繍琛屾湡蹇呯偢锛夛紱鈶 `SceneManifest.navKinds` 澶氬鑸0鏄 + `INavigationProfile.Kinds`锛坬rlidar 鍚屾椂瑕嗙洊 Laser+QrCode锛宍FindByKind` 鎸夐泦鍚堝尮閰嶏級锛涒憿 `ShouldLoad` 瑙勫垯鏀逛负銆**鏈夋竻鍗曪紙鍚澶/鍗忚锛夊嵆鍙備笌婵娲诲垽瀹**锛屾棤娓呭崟鍏煎鍏ㄥ姞杞姐嶏紝scene.device 鍥犳鍙閫夋嫨鎬у姞杞姐 +5. **骞冲彴閰嶅**锛圡igu2.0锛夛細`DeploymentProfile.NavKindToSceneId` 鏀规槧灏 magnetic鈫抯cene.mag銆乹rcode/laser鈫抯cene.qrlidar锛涘悜瀵煎啓 active-scenes.json 鏃跺浐瀹氬苟鍏 scene.device銆 + +### v2 璇勫鍙嶉瑕佺偣锛堟湰娆℃洿鏂颁緷鎹級 + +1. **瀹夸富鍒囨崲**锛歋tandardScene 鏀逛负渚濊禆 **`SimpleLite`锛坣et8.0锛**锛**鑰 `SimpleComposer.exe`锛坣et4.8锛夊簾寮**銆傚唴鏍稿紩鐢ㄥ眰锛堝懡鍚嶇┖闂 `SimpleComposer.RCS` 鈫 `SimpleLite.RCS` 绛夛級姝g敱**鍙︿竴涓 AI 浼氳瘽**鍚屾鏀归狅紝鏈枃鎸夋柊瀹夸富琛ㄨ堪銆 +2. **鐩爣妗嗘灦缁熶竴 `net8.0`**锛堜笌 SimpleLite 涓鑷达紱**闈 net8.0-windows**锛夈係impleLite UI 閲囩敤 **CycleGUI**銆乄ebApi 閲囩敤 **EmbedIO**锛屾晠 StandardScene **闇鍘婚櫎 WinForms 渚濊禆**锛岀獥浣撹縼绉诲埌 CycleGUI 鎴栧钩鍙 Web銆 +3. **璁惧椹卞姩蹇呴』鐙珛 dll**锛屼笖**鏀寔鐑嵏杞/鍔犺浇**锛堥厤鍚 SimpleLite `/plugins` reload/unload + collectible ALC锛夈 +4. **`FactoryTest` 涓庢澗鐏垫畫鐣欙細鐩存帴鍒犻櫎**锛屼笉绾冲叆 Core锛屼篃涓嶇撼鍏 Customer銆 +5. **`WebApi.cs` 鏄潰鍚戣佸钩鍙扮殑 Nancy 鎺ュ彛**锛氭湰娆**鏁寸悊鍚庢殏鐣 Core**锛**鍚庢湡搴熷純**锛岃兘鍔涜縼绉诲埌 SimpleLite 鎺ュ彛锛堣 `E:\Work\Core\Simple-FR\Simple\SimpleLite\Docs\MIGU-API.md`锛夈傛鐗堟湰鍏堜繚鐣欍 + +--- + +## 0. TL;DR锛堢粨璁哄厛琛岋級 + +1. **瀵艰埅鏂瑰紡涓庤溅鍨嬫槸姝d氦鐨勪袱涓淮搴**銆傚鑸紙纾 / 浜岀淮鐮 / 婵鍏夛級鐢**杞ㄩ亾/绔欑偣瀛楁 + TrackCoder**鍐冲畾锛屼笉鏄溅鍨嬪浐鏈夊睘鎬э紱鍚屼竴杞﹀瀷锛堝 `Kiva`锛夌殑绫讳笂**鍚屾椂**鎸傜銆佷簩缁寸爜銆佹縺鍏夐伩闅滀笁绫 coder銆備笂娓"鎸夎溅鍨嬫暣鍖呭垏鍒版煇瀵艰埅 dll"**鍒囦笉骞插噣**锛屼慨姝d负銆**杞﹀瀷鐣欏熀搴 + 瀵艰埅鑳藉姏鎶界涓哄彲鎻掓嫈 coder**銆嶃 +2. **婵鍏"閬块殰"(LidarArea) 鈮 婵鍏"瀵艰埅瀹氫綅"(LidarMap/SLAM)**锛氶伩闅滈氱敤鐣 Core锛屼粎 SLAM 鍦板浘杩 Laser銆 +3. **`WebApi.cs`(123KB) 鏄佸钩鍙 Nancy 鎺ュ彛**锛屽鑸﹀悎鏋佸急锛堜粎浜岀淮鐮 `QrMap`銆佹縺鍏 `getLidarMap`锛夈**鏁寸悊鍚庢殏鐣 Core锛屾爣璁 deprecated锛屽悗鏈熻縼 SimpleLite EmbedIO 鎺ュ彛**锛埪4.7 缁欏嚭绔偣鏄犲皠锛夈 +4. **瀛樺湪澶ч噺閲嶅浠g爜**鍙熸湰娆"鎶界鏍囧噯鍔熻兘"娌夋穩锛氱瀵艰埅寰抗鍣ㄥ啓浜**涓や唤**銆乣newReset/newTrafficReset` 鍦 3 涓溅鍨嬪鍒躲乣SetDisplayInfo/GetCarStatus/Mstsc/EmergencyStop` 闆峰悓銆 +5. **涓変釜姝d氦缁村害**锛氣憼瀵艰埅(纾/浜岀淮鐮/婵鍏) 鈶¤溅鍨(椤跺崌/鍙夎溅/Kiva/鏈烘鑷傗) 鈶㈣澶囬┍鍔(鍏呯數妗/闂/鎸夐挳鐩)銆傝澶囬┍鍔**鐙珛鎴愬彲鐑彃鎷 dll**銆 +6. **VDA5050 鏄嚜鎴愪竴浣撶殑 MQTT 鍗忚鏍**锛岀嫭绔 dll銆 +7. **涓ゅぇ鎶鏈富绾**锛氣憼 鎶 TrackCoder 浠"缂栬瘧鏈熺壒鎬х‖缁戝畾杞﹀瀷"鏀逛负"瀵艰埅鎻掍欢杩愯鏈熸敞鍐"锛岄渶鍐呮牳 `SimpleCore` 閰嶅悎锛涒憽 **net4.8 鈫 net8.0 杩佺Щ**锛氭崲瀹夸富锛圫impleComposer鈫扴impleLite锛+ **鍘 WinForms锛堢獥浣撹縼 CycleGUI/Web锛**銆 + +--- + +## 1. 鐜扮姸鐩樼偣锛堝熀浜庝唬鐮佷簨瀹烇級 + +### 1.1 宸ョ▼姒傚喌锛堟潵鑷 `StandardScene.csproj`锛 + +| 椤 | 鐜扮姸 | 鐩爣锛堟湰娆℃柟鍚戯級 | +|---|---|---| +| 杈撳嚭绫诲瀷 | `Library`锛堟彃浠讹級 | 澶氫釜 `Library` 鎻掍欢 dll | +| 鐩爣妗嗘灦 | `.NET Framework 4.8` | **`net8.0`**锛堜笌 SimpleLite 涓鑷达紝鍘 WinForms锛 | +| 瀹夸富 | `SimpleComposer.exe`(net4.8) | **`SimpleLite`(net8.0)**锛岃 Composer 搴熷純 | +| 濂戠害寮曠敤 | `RefSimpleCore.dll` + `SimpleComposer.exe` | **`SimpleCore`(netstandard2.0/net8) + `SimpleLite` 绋嬪簭闆** | +| UI | `System.Windows.Forms`锛堝ぇ閲忕獥浣擄級 | **CycleGUI / 骞冲彴 Web**锛堢Щ闄 WinForms锛 | +| 瀵瑰鎺ュ彛 | `WebApi.cs`锛圢ancy 1.4.5锛 | 鏆傜暀 Core锛涘悗鏈熻縼 **SimpleLite EmbedIO**锛圡IGU-API锛 | +| 鍏跺畠绋嬪簭闆 | `CommonUsage`銆乣MDCSToolBox`銆乣leegKeys-sdk`銆乣LessokajiWeaverUtilities` | 闅忓綊灞 dll 淇濈暀/涓嬫矇 | +| NuGet | `MQTTnet`銆乣EasyModbusTCP`銆乣IoTClient`銆乣Jint`銆乣Nancy(1.4.5)`銆乣Newtonsoft.Json`銆乣DocumentFormat.OpenXml` | 闅忓綊灞炴媶鍒嗭紙Nancy 楠岃瘉 net8 鍏煎鎴栧榻 SimpleLite 鐨 Nancy 2.0锛 | +| 浜х墿钀藉湴 | `PostBuildEvent` 鎷 dll 鈫 `build\plugins\` | 鍚 dll + `scene.json` 鈫 SimpleLite `plugins\` | + +### 1.2 妯″潡涓庢枃浠跺垎缁勶紙绾 110 涓湁鏁 `.cs`锛 + +| 鐩綍 | 鍐呭 | 鐜扮姸鎬ц川 | +|---|---|---| +| `CarTypes/` | `Forklift`銆乣Kiva`銆乣ArmCar`銆乣DualLiftingCar`銆乣MultiWheelForkLifter`銆乣MultiVehicleCar`銆乣MultiWheelLifterCar`銆乣DummyCar`銆乣UselessCar`銆乣BasicFields`銆乣VehicleMonitor`(绐椾綋) | 杞﹀瀷 + 鍐呰仈瀵艰埅 coder | +| `CarTypes/VDACar/` | `VDA5050Car`銆乣VDA5050Commons/Helper/Interface/Segment/WebApi`銆乣MasterMQTTCommunication`銆乣TextViewer`(绐椾綋) | VDA5050 閫氫俊鍗忚鏍 | +| `Chained/`锛堝惈 `Loop/`锛 | 鎼繍/鐜嚎浠诲姟 + `DeliveryViewer/LoopViewer`(绐椾綋) | 浠诲姟璋冨害 | +| `Charge/` | 鍏呯數浠诲姟 + `ChargeStation*`/`ChargeStrategy*`/`Alarm*`/`Communication*` + 澶氫釜 `*Form`(绐椾綋) | 鍏呯數閫昏緫 + UI/鏈嶅姟 | +| `ChargeStationType/` | `AbstractChargeStation` 鈫 `FL`/`MuXing`/`PCB` | 鍏呯數妗╁巶鍟嗛┍鍔 | +| `InterLock/` | `AbstractInterlockMission`銆乣TrafficInterlockMission`銆乣TrafficInterlockViewer`(绐椾綋) | 浜掗攣/浜ょ | +| `Scheduler/` | `HeartBeat`/`NodeIsEnable`/`RegionalTrafficControl`/`SecuritySignal` Mission | 璋冨害淇″彿 | +| `ExtendDevice/Door/` | `BasicDoorController` 鈫 `ModbusDoorController`銆乣DoorMission`銆乣DoorTypeAttribute`銆乣DoorManager/DoorMonitor`(绐椾綋) | 闂ㄦ帶椹卞姩 | +| `ExtendDevice/ButtonBox/` | `BasicButtonBox` 鈫 `LeegButtonBox`/`AzowieButtonBox`銆乣ButtonMission`銆乣ButtonBoxManager`(绐椾綋) | 鍛煎彨鍣ㄩ┍鍔 | +| `Model/` | `Map`(鍚 `LidarMap`)銆乣SimpleMap`銆乣TaskModel`銆乣LoopTask`銆佸悇 `*Setting`銆乣VehicleStatus`銆乣MissionState`銆乣MapStructure` | 鏁版嵁妯″瀷 | +| `Utils/` `TCP/` `CommonTools/` | Json/Modbus/WebAPIHelper銆丄syncTcpClient銆丼nowflake/AtomicFile | 鍩虹璁炬柦 | +| 鏍 | `Commons.cs`銆乣Heuristic.cs`銆乣LadderLogic.cs`銆乣StandardCADTool.cs`銆乣WebApi.cs`銆乣FactoryTest.cs` | 鍏叡 + 濂戠害 + 鑰 API + 浜ф祴 | + +### 1.3 杞﹀瀷娓呭崟涓庣户鎵 + +| 杞﹀瀷绫 | `[CarType]` | 鍩虹被 | 鍏抽敭鐗瑰緛 | +|---|---|---|---| +| `Kiva` | "Kiva" | `GhostCar` | 鍐呰仈纾 coder `AllCarMagTrackCoder` + Qr + 閬块殰 + 鍙栨斁璐 + `KivaCarTrackCoder`(杞集) | +| `Forklift` | "鍙夎溅" | `GhostCar` | 閬块殰 + 鍙栨斁璐э紙鏃犳樉寮忕/浜岀淮鐮侊級 | +| `MultiWheelLifterCar`(CarTypes) | "澶氳埖杞《鍗囪溅" | `GhostCar` | 鍐呰仈纾 coder `MagTrackCoder` + Qr + 閽昏溅/澶规姳 | +| `MultiWheelForkLifter` | "澶氳埖杞弶杞" | `GhostCar` | 绠鍖栧彇鏀捐揣 | +| `MultiVehicleCar` | "澶氳溅鑱斿姩AGV" | `GhostCar` | 澶氳溅 sync + Qr + 閽昏溅 | +| `DualLiftingCar` | "閿傜數鍙屼妇鍗" | `GhostCar` | 鏃 coder锛堜粎 UI/娴嬭瘯锛 | +| `ArmCar` | "ArmCar" | `GhostCar` | 鏈烘鑷傚姩浣滐紱**渚濊禆 `VDA5050SiteField`**銆佺户鎵 `Kiva*Fields` | +| `DummyCar` | "妯℃嫙杞-鍖呯粶" | `Car` | **鍛藉悕绌洪棿 `AMRScene1`**锛沗Jint` 浠跨湡 | +| `UselessCar` | 锛堝凡娉ㄩ噴锛屾湭娉ㄥ唽锛 | `GhostCar` | 娴嬭瘯娈嬬暀 | + +### 1.4 Mission 缁ф壙浣撶郴锛堝潎娲剧敓鍐呮牳 `Mission`锛屽鑸棤鍏筹級 + +``` +Mission +鈹溾攢 AbstractChainedDeliveryMission / ChainedDeliveryMission(鈫扵ransportDelivery) Chained/ +鈹溾攢 AbstractLoopMission(鈫扡oopMission) Chained/ +鈹溾攢 TrafficInterlockMission / AbstractInterlockMission InterLock/ +鈹 鈹斺攢 AbstractChargeLogiceMission(鈫扴tandardChargeMission) Charge/ +鈹溾攢 HeartBeat/NodeIsEnable/RegionalTrafficControl/SecuritySignal Mission Scheduler/ +鈹溾攢 DoorMission / ButtonMission ExtendDevice/ +鈹斺攢 FactoryTest锛堜骇娴嬶紝鏈鍒犻櫎锛 鏍 +``` + +### 1.5 璁惧椹卞姩浣撶郴锛堢涓夋浜ょ淮搴︼紝鏈鐙珛 dll + 鐑彃鎷旓級 + +- 鍏呯數妗╋細`AbstractChargeStation` 鈫 `FLChargeStation` / `MuXingChargeStation`(鐗ф槦) / `PCBChargeStation`锛沗ChargeStationType` 鏋氫妇锛沗StandardChargeMission` 鎸 `car.fields["MuXing"]` 鍖哄垎 FRLD/MuXing銆 +- 闂細`BasicDoorController` 鈫 `ModbusDoorController`锛沗DoorTypeAttribute`锛堟敞鍐屽绾︼級銆 +- 鎸夐挳鐩掞細`BasicButtonBox` 鈫 `LeegButtonBox`(渚濊禆 `leegKeys-sdk`/`leegiot`) / `AzowieButtonBox`銆 + +### 1.6 VDA5050 瀛愮郴缁燂紙鐙珛 MQTT 鍗忚鏍堬級 + +- `VDA5050Car : Car` + `MasterMQTTCommunication` + `VDA5050Interface/Helper/Commons/Segment/WebApi` + `TextViewer`(绐椾綋)锛涗緷璧 `CommonUsage.Protocols.VDA5050.*` + `MQTTnet`銆 +- VDA5050 杞︾鑷瀵艰埅瀹氫綅锛屼笌纾/浜岀淮鐮/婵鍏夌淮搴︽棤鍏炽 +- 鑰﹀悎鐐癸細`ArmCar` 寮曠敤 `VDA5050SiteField`锛堟媶鍒嗘椂瑙d緷璧栵級銆 + +### 1.7 鍒犻櫎椤癸紙鎷嗗垎鍓嶆竻鐞嗭紝**涓嶈繘 Core/Customer**锛 + +| 鏂囦欢/绫 | 鍘熷洜 | 澶勭疆 | +|---|---|---| +| `FactoryTest.cs` | 浜ф祴涓撶敤锛岄潪鏍囧噯鑳藉姏 | **鍒犻櫎**锛堣瘎瀹″凡纭锛 | +| 鏍 `SongLingDeliveryViewer.Designer.cs` | 鏉剧伒瀹㈡埛娈嬬暀锛屼粎 `.Designer.cs`銆佹棤涓绘枃浠躲佹湭琚 `csproj` 鏀跺綍 | **鍒犻櫎** | +| 鏍 `MultiWheelLifterCar.cs` | 涓 `CarTypes/MultiWheelLifterCar.cs` 閲嶅悕鏃ф枃浠讹紝**鏈 `csproj` 鏀跺綍**锛堟鏂囦欢锛 | **鍒犻櫎** | +| `CarTypes/UselessCar.cs` | `[CarType]` 宸叉敞閲娿佷粎鍙嶅皠绀轰緥 | 鍒犻櫎锛堟垨绉绘祴璇曟牱渚嬶級 | + +--- + +## 2. 鍏抽敭娲炲療锛堢簿璇荤粨璁 鈫 瀵逛笂娓歌璁$殑淇锛夆槄 + +### 2.1 娲炲療 A锛氬鑸槸"杞ㄩ亾/绔欑偣瀛楁 + Coder"锛屼笌杞﹀瀷姝d氦 +- `CarTypes/BasicFields.cs:23` `TagValue //纾佸鑸紝浜岀淮鐮佸硷紝鎴栬卹fid 鍊糮锛堢珯鐐瑰瓧娈碉級銆 +- 纾侊細`Kiva.cs:37`銆乣CarTypes/MultiWheelLifterCar.cs:34` `track.fields["Magnet"]`/`NaiveMagnet`锛堣建閬撳瓧娈碉級銆 +- 浜岀淮鐮侊細杞﹀瀷 coder `useVerb="dst.tag>0 && src.tag>0"` 鈫 `agv.QrGo(...)`銆 +- 婵鍏夐伩闅滐細`useVerb="track.LidarArea != -2"` 鈫 `agv.SwitchLidarArea(...)`銆 +- **缁撹**锛氬悓涓杞﹀瀷鍦ㄤ笉鍚岃建閬撳瓧娈典笅璧颁笉鍚 coder 鍒嗘敮锛涙寜杞﹀瀷鍒 dll 浼氬鍒惰溅鍨嬫垨涓㈣兘鍔涖 + +### 2.2 娲炲療 B锛氬鑸昏緫浠"鐗规"鍐呰仈鍦ㄨ溅鍨嬶紝涓斿瀵艰埅娣锋潅 +- `[TemplateTrackCoderSettings]`/`[ProgramTrackCoderSettings(program=typeof(...))]` 缂栬瘧鏈熺‖缁戝畾鍒拌溅鍨嬬被鍨嬨 +- `Kiva` 鍚屾椂鎸傜(prio19)+浜岀淮鐮(prio30)+閬块殰+鍙栨斁璐с +- **缁撹**锛氬鑸 dll 瑕佸彲鎻掓嫈鍦颁负杞﹀瀷鎻愪緵 coder锛屽繀椤绘妸 coder 澶栫疆骞剁敱鍐呮牳鏀寔**杩愯鏈熸敞鍐**锛埪7.1锛夈 + +### 2.3 娲炲療 C锛氭縺鍏"閬块殰" 鈮 婵鍏"瀵艰埅瀹氫綅" +- 閬块殰锛歚LidarArea`/`SwitchLidarArea`/`ChangeAvoidanceDistance`/`FrontLidarDetect`鈥斺斿嚑涔庢墍鏈夎溅鍨嬮兘鏈夛紝閫氱敤 鈫 Core銆 +- 瀹氫綅锛歚Model/Map.cs:183 LidarMap`(鎷 `127.0.0.1:4321` SLAM 鏍呮牸) + `WebApi.cs:1021 /map/getLidarMap` 鈫 Laser銆 + +### 2.4 娲炲療 D锛氬彲"鎶界涓烘爣鍑嗗姛鑳"鐨勯噸澶嶄唬鐮 +| 閲嶅椤 | 浣嶇疆 | 鎶界鐩爣 | +|---|---|---| +| 纾佸惊杩瑰櫒锛堥夎矾+`MagGo/NaiveMagGo`锛 | `Kiva.cs:32 AllCarMagTrackCoder`銆乣CarTypes/MultiWheelLifterCar.cs:29 MagTrackCoder` | 鍚堝苟涓哄敮涓 `MagneticTrackCoder`(Magnetic dll) | +| `newReset/newTrafficReset` | `Kiva.cs:715`銆乣Forklift.cs:279`銆乣CarTypes/MultiWheelLifterCar.cs:347` | 涓婃彁 Core 杞﹀瀷鍩虹被 | +| `SetDisplayInfo` | 鍚勮溅鍨嬮浄鍚 | Core 鍩虹被榛樿瀹炵幇 | +| `Mstsc/EmergencyStop/ResetClumsy`(HTTP:8008) | 澶氳溅鍨嬮噸澶 | Core 鍩虹被 | +| 閬块殰/IO/绾犲亸 coder 妯℃澘 | 鍚勮溅鍨嬮噸澶嶇矘璐 | Core 閫氱敤 coder 妯℃澘闆 | +| `GetCarStatus` | `Commons` 涓 `MultiWheelForkLifter` 鍚勪竴浠 | 缁熶竴 `Commons` | + +### 2.5 娲炲療 E锛歚WebApi.cs` 鏄佸钩鍙 Nancy 鎺ュ彛锛屾暣浣撻潰涓村簾寮 +- `ApiController : NancyModule`(`WebApi.cs:35`)锛40+ 绔偣锛涘鑸浉鍏充粎浜岀淮鐮 `QrMap`(`:38/:40/:2050`)+`QrSite`(`:2807`) 涓庢縺鍏 `getLidarMap`(`:1021`)銆 +- 鍏惰兘鍔涘湪 SimpleLite 宸茬敱 **EmbedIO**`/projection/*`锛堟姇褰卞揩鐓 + reflection + map-edit + scenes锛岃 MIGU-API.md锛夎鐩栥 +- **澶勭疆锛堟湰鐗堬級**锛氭暣鐞嗗悗**鏆傜暀 Core**锛堟爣 `[Obsolete]`/娉ㄩ噴 deprecated锛夛紱**鍚庢湡鏁翠綋搴熷純**锛岃縼绉荤储寮曡 搂4.7銆 + +### 2.6 娲炲療 F锛氳澶囬┍鍔ㄦ槸绗笁涓浜ょ淮搴︼紙鏈鐙珛 dll + 鐑彃鎷旓級 +- 鍏呯數妗/闂/鎸夐挳鐩掓寜鍘傚晢/鍨嬪彿鎵╁睍锛屼笌瀵艰埅銆佽溅鍨嬮兘姝d氦銆 +- 閫氳繃鏃㈡湁鐗规э紙`ChargeStationType`/`DoorTypeAttribute`/鏂板 ButtonBoxType锛夋敞鍐岋紝缂栬瘧涓虹嫭绔 `Devices.*` dll锛屾敮鎸 SimpleLite `/plugins` 鐑姞杞/鍗歌浇銆 + +### 2.7 娲炲療 G锛歎I 褰㈡侀渶浠 WinForms 杩佸埌 CycleGUI/Web锛坣et8.0 绾︽潫锛 +- SimpleLite = `net8.0`锛堢函锛夈乁I 鐢 **CycleGUI**銆乄ebApi 鐢 **EmbedIO**锛**涓嶄緷璧 `System.Windows.Forms`**銆 +- StandardScene 鐜版湁绐椾綋锛歚DeliveryViewer/LoopViewer/TrafficInterlockViewer/VehicleMonitor/鍚 Charge*Form/Door*/ButtonBox*/TextViewer` + `Commons` 鍐 `MessageBox`銆 +- **缁熶竴 net8.0 鈬 蹇呴』绉婚櫎 WinForms**锛氱獥浣撹縼 **CycleGUI**锛圫impleLite 绔嬪嵆妯″紡 UI锛夋垨骞冲彴 **Web**锛沗MessageBox` 绫绘彁绀烘敼 CycleGUI 寮圭獥 / 骞冲彴閫氱煡銆傝繖鏄湰娆**閲嶈宸ヤ綔閲忎笌椋庨櫓**銆 + +--- + +## 3. 鐩爣鏋舵瀯涓庣洰褰曡鍒 + +### 3.1 鍒嗗眰渚濊禆鍥 + +```mermaid +flowchart TD + SC["SimpleCore (netstandard2.0/net8)
AbstractCar/CarType/Mission/ITrackCoder/Heuristics
+ 瀵艰埅濂戠害 NavKind/INavigationProfile/ISceneContext
+ (鏂板)TrackCoder 杩愯鏈熸敞鍐岃〃"] + SL["SimpleLite (net8.0 瀹夸富)
CycleGUI UI 路 EmbedIO WebApi 路 /plugins 鐑彃鎷 路 /scenes 閫夋嫨鎬у姞杞"] + CORE["StandardScene.Core (net8.0)
瀵艰埅鏃犲叧鍩哄骇锛氳溅鍨嬫湰浣+浠诲姟/鍏呯數/浜掗攣/璋冨害+妯″瀷/宸ュ叿+Commons+閫氱敤閬块殰coder
(鏆傜暀)WebApi.Core(Nancy, deprecated)"] + MAG["StandardScene.Magnetic"] + QR["StandardScene.QrCode"] + LAS["StandardScene.Laser"] + VDA["StandardScene.Protocol.VDA5050"] + DCH["StandardScene.Devices.Charge
(鐑彃鎷)"] + DDR["StandardScene.Devices.Door
(鐑彃鎷)"] + DBT["StandardScene.Devices.ButtonBox
(鐑彃鎷)"] + + SC --> CORE + SC --> SL + CORE --> MAG & QR & LAS & VDA & DCH & DDR & DBT + SL -. 鍔犺浇/鍗歌浇 .-> CORE & MAG & QR & LAS & VDA & DCH & DDR & DBT +``` + +### 3.2 瑙e喅鏂规鐩綍甯冨眬锛堝崟宸ョ▼ 鈫 澶氬伐绋嬶紝缁熶竴 net8.0锛 + +``` +StandardScene/ 瑙e喅鏂规鏍 +鈹溾攢 StandardScene.Core/ net8.0锛屾爣鍑嗗熀搴э紙alwaysLoad锛屼笉鍗曠嫭浣滀负瀵艰埅鍦烘櫙锛 +鈹 鈹溾攢 Navigation/ INavigationProfile/NavKind/ISceneContext/Registry +鈹 鈹溾攢 Cars/ 杞﹀瀷鍩虹被 StandardCarBase + 鍚勮溅鍨嬫湰浣擄紙鏃犲鑸 coder銆佹棤 WinForms锛 +鈹 鈹 鈹斺攢 Sim/ DummyCar锛堜豢鐪燂紝AMRScene1 鍛藉悕绌洪棿鏀舵暃锛 +鈹 鈹溾攢 Coders/ 閫氱敤 coder锛堥伩闅/IO/绾犲亸/鍙栨斁璐фā鏉匡級 +鈹 鈹溾攢 Chained/ Charge/ ChargeStationType(鎶借薄) / InterLock/ Scheduler/ +鈹 鈹溾攢 ExtendDevice/ 璁惧妗嗘灦锛堝熀绫 + 娉ㄥ唽鐗规э級 +鈹 鈹溾攢 Model/ Utils/ TCP/ CommonTools/ +鈹 鈹溾攢 Ui/ CycleGUI 闈㈡澘锛堟浛浠e師 WinForms 绐椾綋锛 +鈹 鈹溾攢 Commons.cs Heuristic.cs LadderLogic.cs StandardCADTool.cs +鈹 鈹斺攢 WebApi.Core.cs 鑰 Nancy API锛坉eprecated锛屾殏鐣欙紝鍚庢湡鍒狅級 +鈹溾攢 StandardScene.Magnetic/ 鈫 plugins/ + scene.json +鈹溾攢 StandardScene.QrCode/ 鈫 plugins/ + scene.json +鈹溾攢 StandardScene.Laser/ 鈫 plugins/ + scene.json +鈹溾攢 StandardScene.Protocol.VDA5050/ 鈫 plugins/ + scene.json +鈹溾攢 StandardScene.Devices.Charge/ 鈫 plugins/锛堢儹鎻掓嫈锛 +鈹溾攢 StandardScene.Devices.Door/ 鈫 plugins/锛堢儹鎻掓嫈锛 +鈹溾攢 StandardScene.Devices.ButtonBox/ 鈫 plugins/锛堢儹鎻掓嫈锛 +鈹斺攢 StandardScene.sln +``` + +### 3.3 鍚 dll 鑱岃矗 + +| dll | 鑱岃矗 | 鍙縺娲/鐑彃鎷 | +|---|---|---| +| `StandardScene.Core` | 瀵艰埅鏃犲叧涓鍒囷細杞﹀瀷鏈綋銆佷换鍔/鍏呯數/浜掗攣/璋冨害銆佹ā鍨/宸ュ叿銆乣Commons`/濂戠害銆侀氱敤閬块殰 coder銆侊紙鏆傜暀锛夎 WebApi | 鍩哄骇锛坄alwaysLoad`锛 | +| `StandardScene.Magnetic` | 缁熶竴 `MagneticTrackCoder` + 纾佹潯瀛楁璇箟 | 鏄 | +| `StandardScene.QrCode` | `QrTrackCoder` + `SyncQrMap` + 浜岀淮鐮佸湴鍥句笅鍙 | 鏄 | +| `StandardScene.Laser` | `LidarMap`/`getLidarMap`锛圫LAM锛 | 鏄 | +| `StandardScene.Protocol.VDA5050` | VDA5050/MQTT 鍗忚鏍 + `VDA5050Car` | 鏄 | +| `StandardScene.Devices.Charge` | 鍏呯數妗 FL/MuXing/PCB 椹卞姩 | **鏄紙鐑彃鎷旓級** | +| `StandardScene.Devices.Door` | 闂 Modbus 椹卞姩 | **鏄紙鐑彃鎷旓級** | +| `StandardScene.Devices.ButtonBox` | 鎸夐挳鐩 Leeg/Azowie 椹卞姩 | **鏄紙鐑彃鎷旓級** | + +--- + +## 4. 鍔熻兘褰掑睘鐭╅樀锛堟枃浠剁骇 / 绫荤骇锛夆槄 + +> 鍔ㄤ綔锛**绉诲姩**/**鎷嗗垎**/**鎶界**/**淇濈暀**/**鍒犻櫎**/**杩乁I**(WinForms鈫扖ycleGUI/Web)/**寰呭鏍**銆 + +### 4.1 杞﹀瀷锛坄CarTypes/`锛 + +| 鐜扮姸 | 鐩爣 | 鍔ㄤ綔 | 璇存槑 | +|---|---|---|---| +| `BasicFields.cs` | Core | 绉诲姩 | 杞﹀瀷鍏叡瀛楁 | +| `Kiva.cs::Kiva` | Core(Cars) | 鎷嗗垎 | 鏈綋鐣 Core锛涚Щ闄ゅ唴鑱旂/Qr coder 鐗规 | +| `Kiva.cs::AllCarMagTrackCoder` | Magnetic | 鎶界 | 骞跺叆鍞竴 `MagneticTrackCoder` | +| `Kiva.cs::KivaCarTrackCoder` | Core(Coders) | 绉诲姩 | 杞集锛屽鑸棤鍏 | +| `Forklift.cs` | Core(Cars) | 绉诲姩 | 閫氱敤閬块殰/鍙栨斁璐 | +| `CarTypes/MultiWheelLifterCar.cs::MultiWheelLifterCar` | Core(Cars) | 鎷嗗垎 | 鏈綋鐣 Core锛涚Щ闄ゅ唴鑱旂/Qr | +| `CarTypes/MultiWheelLifterCar.cs::MagTrackCoder` | Magnetic | 鎶界 | 骞跺叆 `MagneticTrackCoder`锛堝幓閲嶏級 | +| `MultiWheelForkLifter.cs` | Core(Cars) | 绉诲姩 | `GetCarStatus` 骞跺叆 `Commons` | +| `MultiVehicleCar.cs` | Core(Cars) | 鎷嗗垎 | 鑱斿姩鐣 Core锛決r coder 澶栫Щ QrCode | +| `DualLiftingCar.cs` | Core(Cars) | 绉诲姩 | 鏃 coder | +| `ArmCar.cs` | Core(Cars) | 鎷嗗垎/瑙h | 瑙i櫎 `VDA5050SiteField` 渚濊禆 | +| `DummyCar.cs` | Core(Cars/Sim) | 绉诲姩 | `AMRScene1` 鍛藉悕绌洪棿鏀舵暃 | +| `UselessCar.cs` | 鈥 | 鍒犻櫎 | 鏈敞鍐 | +| `VehicleMonitor.cs(.Designer)` | Core(Ui) | **杩乁I** | WinForms 鈫 CycleGUI | + +### 4.2 VDA5050锛坄CarTypes/VDACar/`锛 + +| 鐜扮姸 | 鐩爣 | 鍔ㄤ綔 | +|---|---|---| +| `VDA5050*` + `MasterMQTTCommunication` | `Protocol.VDA5050` | 绉诲姩锛堟暣瀛愮洰褰曪級 | +| `TextViewer.cs(.Designer)` | `Protocol.VDA5050`(Ui) | 杩乁I锛圕ycleGUI锛 | +| `VDA5050SiteField`锛堣 ArmCar 寮曠敤锛 | Core 鍏叡瀛楁 鎴 VDA dll | 寰呭鏍革紙ArmCar 鐣 Core 鍒欎笅娌 Core锛 | + +### 4.3 浠诲姟鏃忥紙`Chained/` `InterLock/` `Scheduler/`锛 + +| 鐜扮姸 | 鐩爣 | 鍔ㄤ綔 | +|---|---|---| +| `Chained/*`銆乣InterLock/*`銆乣Scheduler/*` 閫昏緫 | Core | 绉诲姩锛堝鑸棤鍏筹級 | +| `DeliveryViewer/LoopViewer/TrafficInterlockViewer`(绐椾綋) | Core(Ui) | 杩乁I锛圕ycleGUI/Web锛 | + +### 4.4 鍏呯數锛坄Charge/` `ChargeStationType/`锛 + +| 鐜扮姸 | 鐩爣 | 鍔ㄤ綔 | 璇存槑 | +|---|---|---|---| +| `AbstractChargeLogicMission`銆乣StandardChargeMission` | Core | 绉诲姩 | 鍏呯數閫昏緫 | +| `ChargeStation*`/`ChargeStrategy*`/`Alarm*`/`Communication*`(鏈嶅姟) | Core | 绉诲姩 | 鍏呯數绠$悊/鏈嶅姟 | +| 鍚 `Charge/*Form`(绐椾綋) | Core(Ui) | 杩乁I | CycleGUI/Web | +| `ChargeStationType/AbstractChargeStation.cs` | Core | 绉诲姩 | 鎶借薄 + 娉ㄥ唽濂戠害 | +| `ChargeStationType/{FL,MuXing,PCB}ChargeStation.cs` | **`Devices.Charge`** | 鎷嗗垎 | 鍘傚晢椹卞姩锛岀嫭绔嬬儹鎻掓嫈 dll | + +### 4.5 鎵╁睍璁惧锛坄ExtendDevice/`锛 + +| 鐜扮姸 | 鐩爣 | 鍔ㄤ綔 | 璇存槑 | +|---|---|---|---| +| `Door/BasicDoorController`銆乣DoorMission`銆乣DoorModel`銆乣DoorTypeAttribute` | Core(妗嗘灦) | 绉诲姩 | 闂ㄦ帶妗嗘灦 + 娉ㄥ唽濂戠害 | +| `Door/ModbusDoorController` | **`Devices.Door`** | 鎷嗗垎 | Modbus 椹卞姩锛岀嫭绔嬬儹鎻掓嫈 | +| `Door/DoorManager/DoorMonitor`(绐椾綋) | Core(Ui)/Devices.Door(Ui) | 杩乁I | CycleGUI | +| `ButtonBox/BasicButtonBox`銆乣ButtonMission`銆乣ButtonBoxModel` | Core(妗嗘灦) | 绉诲姩 | 鎸夐挳鐩掓鏋 | +| `ButtonBox/LeegButtonBox`(`leegKeys-sdk`)銆乣AzowieButtonBox` | **`Devices.ButtonBox`** | 鎷嗗垎 | 鍘傚晢椹卞姩锛宍leegKeys-sdk` 闅忛┍鍔ㄨ蛋 | +| `ButtonBox/ButtonBoxManager`(绐椾綋) | 瀵瑰簲 dll(Ui) | 杩乁I | CycleGUI | + +### 4.6 鍏叡 / 濂戠害 / 妯″瀷 / 鍩虹璁炬柦 + +| 鐜扮姸 | 鐩爣 | 鍔ㄤ綔 | 璇存槑 | +|---|---|---|---| +| `Commons.cs::Commons` | Core | 绉诲姩 | 瑙i櫎瀵 `DummyCar` 纭紩鐢紙`GetVehicleStatus`锛 | +| `Commons.cs::CustomOperationsBeforeLoading` | 鍚勫彲婵娲 dll 鍚勪竴浠 | 澶嶅埗/鍒嗗彂 | `MessageBox` 姝婚攣鎻愰啋鏀 CycleGUI 寮圭獥 | +| `Commons.cs::NoReflectionApi`/`ReflectionApiWithParameter` | SimpleCore 鎴 Core | 寰呭鏍 | 澶 dll 鍏辩敤寤鸿涓嬫矇 SimpleCore | +| `Heuristic.cs` | Core | 绉诲姩 | 鍚彂寮忥紙瀹夸富鍙嶅皠娉ㄥ唽锛 | +| `LadderLogic.cs` | Core | 绉诲姩 | 闃叉姈/姊舰閫昏緫 | +| `StandardCADTool.cs`锛堝鏁 `CADTool`锛 | Core | 绉诲姩 | 鍦板浘缂栬緫宸ュ叿 | +| `StandardCADTool.cs::SyncQrMap` | QrCode | 鎷嗗垎 | 浜岀淮鐮佸湴鍥句笅鍙 | +| `Model/Map.cs::Map/ArcHelper` | Core | 绉诲姩 | 鍦板浘搴忓垪鍖 | +| `Model/Map.cs::LidarMap` | Laser | 鎷嗗垎 | SLAM 鏍呮牸鍥 | +| `Model/*`锛堝叾浣欙級 | Core | 绉诲姩 | 鏁版嵁妯″瀷 | +| `Utils/*`銆乣TCP/*`銆乣CommonTools/*` | Core | 绉诲姩 | 鍩虹璁炬柦 | +| `FactoryTest.cs` | 鈥 | **鍒犻櫎** | 璇勫纭 | +| 鏍 `MultiWheelLifterCar.cs`銆佹牴 `SongLingDeliveryViewer.Designer.cs` | 鈥 | **鍒犻櫎** | 姝/娈嬬暀 | + +### 4.7 鑰 WebApi锛坄WebApi.cs`锛夋暣鐞嗕笌杩佺Щ绱㈠紩 + +**鏈増澶勭疆**锛氶氱敤绔偣鏁寸悊涓 `WebApi.Core.cs` 鏆傜暀 Core 骞舵爣 deprecated锛涗簩缁寸爜/婵鍏夌鐐归殢瀵艰埅 dll锛**鍚庢湡鏁翠綋鍒犻櫎**锛屽墠绔敼鐢 SimpleLite 鎺ュ彛銆備笅琛ㄤ负搴熷純杩佺Щ绱㈠紩锛堣 Nancy 鈫 SimpleLite EmbedIO锛屼緷鎹 MIGU-API.md锛夛細 + +| 鑰佺鐐癸紙`WebApi.cs`锛 | 琛屽彿 | SimpleLite 鏇夸唬锛坄/api/sl/projection/...`锛 | +|---|---|---| +| `car_reflection/mission_reflection get_type/methods/fields/execute` | 191鈥537 | `reflection/methods/*`銆乣/fields/*`銆乣/execute/*`銆乣/bundle/*` | +| `car/getAllCars`銆乣api/agv/list` | 1047銆2095 | `projection/cars` | +| `task/getTask` | 1099 | `projection/missions` | +| `map/getMap` | 995 | `projection/sites`+`/tracks`銆佹垨 `map-edit` | +| `car/goSite` | 1070 | `reflection/car/{id}/goto-site` | +| `car/reset/repair/blown/ForceStop/ReStart/...` | 767+ | `reflection/execute/{kind}/{id}/{method}`锛坄[MethodMember]`锛 | +| `car/createTask/carTask/cancelTask` | 542/597/900 | `reflection/execute` + `projection/deliveries/*` | +| `set{Charging,Envelope,TrafficControl,PlanRules}Setting` | 1680+ | `reflection/fields/{kind}/{id}/{field}`锛堝啓瀛楁锛 | +| `api/CreateSite` | 2055 | `map-edit/objects/site` | +| `api/QrMap`锛堜簩缁寸爜锛 | 2050 | 鏆傞殢 QrCode dll锛汼impleLite 渚у緟鏂板锛堟棤鐩存帴瀵瑰簲锛 | +| `map/getLidarMap`锛堟縺鍏夛級 | 1021 | 鏆傞殢 Laser dll锛汼impleLite 渚у緟鏂板锛堟棤鐩存帴瀵瑰簲锛 | + +> 娉細浜岀淮鐮佸湴鍥句笅鍙戙佹縺鍏 SLAM 鍙栧浘鍦 SimpleLite 鐜版湁 MIGU-API 涓**鏃犵洿鎺ュ搴**锛岃縼绉绘椂闇鍦 SimpleLite/鍦烘櫙鎻掍欢渚цˉ鎺ュ彛锛屾晠杩欎袱鍧楅殢瀵艰埅 dll 鍏堣淇濈暀銆 + +--- + +## 5. "鎶界鏍囧噯鍔熻兘"娓呭崟锛堝幓閲嶄笌娌夋穩锛 + +1. **缁熶竴纾佸鑸惊杩瑰櫒**锛歚AllCarMagTrackCoder` + `MagTrackCoder` 鈫 鍞竴 `StandardScene.Magnetic.MagneticTrackCoder`锛堝弬鏁板寲 fields锛夈 +2. **杞﹀瀷鍩虹被 `StandardCarBase`(Core)**锛氫笂鎻 `newReset/newTrafficReset`銆乣SetDisplayInfo`銆乣Mstsc`銆乣EmergencyStop/Release`銆乣ResetClumsy`銆乣GetCarStatus` 榛樿瀹炵幇銆 +3. **閫氱敤 Coder 妯℃澘闆(Core/Coders)**锛氭矇娣閬块殰/IO/绾犲亸/閬块殰灏哄绛夐噸澶 `[TemplateTrackCoderSettings]`銆 +4. **瀵艰埅鑳藉姏鎶借薄 `INavigationProfile`**锛氭瘡涓鑸 dll 瀹炵幇涓涓 Profile锛堝0鏄 `NavKind` + 鎻愪緵鐨 coder/瀛楁璇箟锛夛紝Core/瀹夸富鍙嶅皠鏀堕泦銆 +5. **`Commons` 鍘婚噸**锛氬悎骞 `GetCarStatus`锛沗GetVehicleStatus` 鍘婚櫎瀵 `DummyCar` 鐨勭‖缂栫爜锛堟敼鏍囪鎺ュ彛锛夈 +6. **璁惧娉ㄥ唽鏍囧噯鍖**锛氬厖鐢垫々/闂/鎸夐挳鐩掔粺涓鐗规ф敞鍐岋紝椹卞姩 dll 鍗虫彃鍗崇敤 + 鐑彃鎷斻 +7. **UI 鏍囧噯鍖**锛氬師 WinForms 绐椾綋缁熶竴杩 CycleGUI 闈㈡澘锛堟矇娣灏戦噺閫氱敤闈㈡澘鍩虹被锛夈 + +--- + +## 6. 鍛藉悕绌洪棿涓庝緷璧栨不鐞 + +- **瀹夸富/鍛藉悕绌洪棿杩佺Щ**锛歚SimpleComposer.RCS`/`SimpleComposer.UI` 鈫 `SimpleLite.RCS`/瀵瑰簲锛堜互鍐呮牳鏀归犱负鍑嗭紝鍙︿竴浼氳瘽杩涜涓級锛涘紩鐢 `SimpleComposer.exe` 鈫 `SimpleLite` 绋嬪簭闆 + `SimpleCore` 濂戠害銆 +- **鍛藉悕绌洪棿鏀舵暃**锛歚DummyCar` 鐨 `AMRScene1` 鈫 `StandardScene.Core.Cars.Sim`銆 +- **鍘 WinForms**锛氱Щ闄 `System.Windows.Forms` 寮曠敤锛岀獥浣撹縼 CycleGUI/Web锛宍MessageBox` 鏀 CycleGUI 寮圭獥銆 +- **鍙嶅悜渚濊禆娌荤悊**锛歚Commons.GetVehicleStatus`鈫沗DummyCar`锛沗ArmCar`鈫沗VDA5050SiteField`锛涗簩缁寸爜鐩稿叧闆嗕腑 QrCode dll锛孋ore 浠呯暀涓珛鎵╁睍鐐广 +- **涓夋柟渚濊禆闅忓綊灞**锛歚MQTTnet`鈫扸DA5050锛沗leegKeys-sdk`鈫払uttonBox 椹卞姩锛沗EasyModbusTCP/IoTClient`鈫扗oor/Charge 椹卞姩鎴 Core 宸ュ叿锛沗Nancy`鈫掞紙鏆傜暀鐨 WebApi.Core锛岄獙璇 net8 鍏煎鎴栧榻 SimpleLite 鐨 Nancy 2.0锛夛紱`Jint`鈫掑惈 DummyCar 浠跨湡鐨 Core銆 + +--- + +## 7. 鍏抽敭鎶鏈毦鐐逛笌瀵圭瓥 + +### 7.1 闅剧偣鈶狅細TrackCoder 鐢"缂栬瘧鏈熺壒鎬"鏀逛负"杩愯鏈熸敞鍐" +- 鐜扮姸锛歝oder 閫氳繃鐗规х‖缁戝畾杞﹀瀷绫诲瀷銆 +- 鐩爣锛氬鑸 dll 涓嶆敼 Core 杞﹀瀷婧愮爜鍗冲彲涓鸿溅鍨嬭ˉ coder銆 +- 瀵圭瓥锛氣憼 **鍐呮牳鎵╁睍锛堟帹鑽愶級**鈥斺擿SimpleCore` 澧 coder 娉ㄥ唽琛紝瀵艰埅 dll 鍦 `INavigationProfile.OnActivate` 娉ㄥ唽/鍗歌浇鏃剁Щ闄わ紙濂戝悎 `/plugins` 鐑彃鎷旓級锛涒憽 杞﹀瀷鐣欏崰浣嶃佸鑸 dll 瀹炵幇濮旀淳锛涒憿 **鍏滃簳**鈥斺擟ore 鍐呯疆鍏ㄩ噺 coder锛屽鑸 dll 鍙壙杞 API/鍦板浘/瀛楁/娓呭崟銆 +- **琛屽姩椤**锛氫笌姝e湪鏀瑰唴鏍稿紩鐢ㄧ殑浼氳瘽鍚屾锛岀‘璁ゆ槸鍚︾撼鍏ユ敞鍐岃〃 API銆 + +### 7.2 闅剧偣鈶★細net4.8 鈫 net8.0 杩佺Щ锛堟崲瀹夸富 + 鍘 WinForms锛 +- 瀹夸富锛氬紩鐢 `SimpleComposer.exe`(net4.8) 鈫 `SimpleLite`/`SimpleCore`(net8)锛沗SimpleComposer` 鐙湁绫诲瀷鍦ㄥ唴鏍镐晶琛ラ綈/涓嬫矇锛堜笌鍙︿竴浼氳瘽鍗忎綔锛夈 +- **鍘 WinForms锛堥噸鐐癸級**锛氭墍鏈夌獥浣撹縼 CycleGUI锛圫impleLite 绔嬪嵆妯″紡 UI锛夋垨骞冲彴 Web锛沗MessageBox`鈫扖ycleGUI 寮圭獥/骞冲彴閫氱煡銆傚伐浣滈噺闆嗕腑鍦 `Charge/*Form`銆乣Delivery/Loop/TrafficInterlock Viewer`銆乣VehicleMonitor`銆乣Door/ButtonBox` 绠$悊绐椾綋銆乣VDACar/TextViewer`銆 +- 涓夋柟搴 net8 楠岃瘉锛歚Nancy(1.4.5)`锛堟殏鐣 WebApi锛岄獙璇佹垨鍗 2.0锛夈乣MQTTnet/EasyModbusTCP/IoTClient/Jint/Newtonsoft/DocumentFormat.OpenXml`銆 +- Fody/`LessokajiWeaver`锛氫笌鍐呮牳涓鑷达紙娉ㄦ剰涓婃父 搂13.2 鐜绾︽潫锛夈 + +### 7.3 闅剧偣鈶細鎻掍欢閽╁瓙涓庡 dll 鍒濆鍖 +- `CustomOperationsBeforeLoading.Set()` 瀹夸富鎸 dll 鍙嶅皠璋冪敤锛氭媶鍒嗗悗姣忎釜鍙縺娲 dll 鍚勪繚鐣欎竴浠斤紱鐢 `INavigationProfile.OnActivate` 鍋氬鑸 dll 鑷垵濮嬪寲锛岄伩鍏嶉噸澶嶆敞鍐屽叏灞鍥炶皟銆 + +### 7.4 闅剧偣鈶o細璁惧/瀵艰埅 dll 鐑彃鎷旓紙collectible 鍗歌浇锛 +- 閰嶅悎 SimpleLite `/plugins/reload`銆乣/plugins/{name}/unload` 涓 `/scenes/apply`锛歞ll 椤诲彲琚 collectible ALC 骞插噣鍗歌浇锛堝嵏杞藉墠鏃犲瓨娲 `Car/Mission` 瀹炰緥锛夛紝`OnDeactivate` 娓呯悊娉ㄥ唽鐨 coder/璁惧/鍥炶皟/MQTT 杩炴帴/TCP 瀹㈡埛绔 + +### 7.5 闅剧偣鈶わ細鑰 WebApi 鏆傜暀涓庢渶缁堜笅绾 +- `WebApi.Core`(Nancy) 鏆傜暀 Core 浠呬负杩囨浮锛涙柊鍔熻兘涓寰嬭蛋 SimpleLite EmbedIO銆傛寜 搂4.7 绱㈠紩閫愭杩佺Щ锛岃縼瀹屽嵆鍒狅紱浜岀淮鐮/婵鍏夊彇鍥鹃渶鍏堝湪 SimpleLite/鍦烘櫙鎻掍欢渚цˉ鎺ュ彛銆 + +--- + +## 8. 鍒嗛樁娈靛疄鏂借鍒掞紙鍔″疄璺嚎锛 + +> 鍘熷垯锛氭瘡闃舵閮借兘缂栬瘧銆佽兘鍔犺浇銆佸彲鍥為銆 + +### C0 路 鍑嗗涓庡榻愶紙浣庨闄╋紝鍏堝仛锛 +- 鍒犻櫎锛歚FactoryTest.cs`銆佹牴 `SongLingDeliveryViewer.Designer.cs`銆佹牴 `MultiWheelLifterCar.cs`銆乣UselessCar`銆 +- 鍘婚噸娌夋穩锛埪5锛夛細缁熶竴 `MagTrackCoder`銆乣GetCarStatus`锛涙彁鍙 `StandardCarBase`銆 +- 鍛藉悕绌洪棿鏀舵暃 `AMRScene1`鈫扖ore銆 +- 涓庡唴鏍镐細璇濆榻愶細`SimpleLite`/`SimpleCore` 寮曠敤涓庡懡鍚嶇┖闂淬乧oder 娉ㄥ唽琛紙搂7.1锛夈佸鑸绾︼紙涓婃父 Phase A 宸插氨缁級銆 +- 楠屾敹锛氱紪璇戦氳繃銆佽涓轰笉鍙樸 + +### C1 路 Core 鍩哄骇 net8.0 鍖栵紙鎹㈠涓 + 鍘 WinForms 闂幆锛 +- 鏂板缓 `StandardScene.Core`(net8.0)锛岃縼鍏ュ鑸棤鍏冲叏閮ㄥ唴瀹癸紱寮曠敤鍒囧埌 `SimpleLite`/`SimpleCore`銆 +- **鍘 WinForms**锛氱獥浣撹縼 CycleGUI锛沗MessageBox`鈫扖ycleGUI銆 +- 鏁寸悊鑰 `WebApi`鈫抈WebApi.Core`(deprecated, 鏆傜暀)銆 +- coder 鍏堢敤鍏滃簳锛埪7.1 鏂规鈶級淇濊瘉涓嶅洖褰掋 +- 楠屾敹锛歚StandardScene.Core.dll` 琚 SimpleLite 鍔犺浇銆佽窇閫氭惉杩/鍏呯數/浜ょ銆 + +### C2 路 鎶界瀵艰埅鑳藉姏 dll +- `Magnetic`锛堝敮涓 `MagneticTrackCoder`锛/ `QrCode`锛坄QrTrackCoder`+`SyncQrMap`+`QrMap`锛/ `Laser`锛坄LidarMap`+`getLidarMap`锛夛紝鍚勫甫 `scene.json`銆 +- 鎸 搂7.1 閫夊畾鏂规鎶 coder 鎺ュ埌杞﹀瀷銆 +- 楠屾敹锛氫粎婵娲绘煇瀵艰埅鏃跺搴旇兘鍔涘彲鐢紝鍏朵綑涓嶅姞杞姐 + +### C3 路 璁惧椹卞姩 dll 鍖栵紙蹇呭仛锛岀儹鎻掓嫈锛 +- `Devices.Charge`(FL/MuXing/PCB)銆乣Devices.Door`(Modbus)銆乣Devices.ButtonBox`(Leeg/Azowie)锛沗leegKeys-sdk` 闅忛┍鍔ㄣ +- 楠岃瘉 `/plugins` reload/unload 鐑彃鎷旓紱`OnDeactivate` 娓呯悊杩炴帴銆 +- 楠屾敹锛氳澶囬┍鍔ㄥ彲鐙珛鐑姞杞/鍗歌浇銆 + +### C4 路 VDA5050 鍗忚 dll +- `Protocol.VDA5050`锛堟暣 `VDACar/` 杩佸嚭锛屽惈 MQTT锛夛紱瑙 `ArmCar鈫擵DA5050SiteField`銆 +- 楠屾敹锛氭縺娲诲悗 VDA5050 杞﹀彲鐢ㄣ佸彲鍗歌浇銆 + +### C5 路 鑱旇皟銆乄ebApi 杩佺Щ涓庢枃妗 +- 涓夊鑸 + 璁惧 + 鍗忚缁勫悎鑱旇皟锛涘鎺 `/scenes/apply` + `active-scenes.json`銆 +- 鎸 搂4.7 鎶婂彲杩佺殑 WebApi 绔偣鍒囧埌 SimpleLite锛岄愭鍒 `WebApi.Core`銆 +- 鏇存柊鏈枃浠 搂11 杩涘害 + `scene.json` 瀹為檯娓呭崟 + `README/DEVELOPMENT_GUIDE`銆 + +--- + +## 9. 鎻掍欢娓呭崟濂戠害锛坄scene.json`锛屽鎺ヤ笂娓 搂5.5 涓 SimpleLite `/scenes`锛 + +```json +{ + "id": "scene.magnetic", + "displayName": "纾佸鑸満鏅", + "navKind": "Magnetic", + "assembly": "StandardScene.Magnetic.dll", + "requiresCore": "StandardScene.Core.dll", + "provides": { "trackCoders": ["MagneticTrackCoder"], "carTypeBindings": ["*"] } +} +``` + +- 骞冲彴銆岄厤缃悜瀵笺嶁啋 `WizardController` 鍐欓厤缃 鈫 璋 SimpleLite `POST /api/sl/projection/scenes/apply`锛堝啓 `active-scenes.json` + 澧為噺 reload锛夈 +- 鏁版嵁濂戠害锛歚data/config-deployment.json`锛堝钩鍙帮級/ `plugins/active-scenes.json`锛堝唴鏍革級/ `plugins/.scene.json`锛堟彃浠讹級銆 + +--- + +## 10. 椋庨櫓銆佸緟纭涓庨獙鏀跺彛寰 + +### 宸茬‘璁わ紙v2锛 +- 瀹夸富 = SimpleLite锛涜 SimpleComposer 搴熷純銆 +- 鐩爣妗嗘灦 = `net8.0`锛堝幓 WinForms锛孶I 杩 CycleGUI/Web锛夈 +- 璁惧椹卞姩 = 鐙珛 dll + 鐑彃鎷斻 +- `FactoryTest`銆佹澗鐏垫畫鐣 = 鍒犻櫎銆 +- `WebApi.cs` = 鏁寸悊鍚庢殏鐣 Core銆佸悗鏈熷簾寮冭縼 SimpleLite銆 + +### 寰呯‘璁 +1. **coder 娉ㄥ唽琛**锛氬唴鏍告槸鍚︾撼鍏"杩愯鏈熸妸 TrackCoder 娉ㄥ唽鍒拌溅鍨"鐨 API锛堝惁鍒 C2 鐢ㄥ厹搴曟柟妗堚憿锛夈 +2. **UI 杩佺Щ鑼冨洿涓庤妭濂**锛氱獥浣撲竴娆℃ц縼 CycleGUI锛岃繕鏄寜 dll 鍒嗘壒锛涙槸鍚﹂儴鍒嗕粎淇濈暀骞冲彴 Web 鍏ュ彛銆 +3. **VDA5050** 鐙珛 dll 纭锛堟帹鑽愮嫭绔嬶級銆 +4. **浜岀淮鐮佸湴鍥句笅鍙 / 婵鍏 SLAM 鍙栧浘**锛歋impleLite 渚цˉ鎺ュ彛鐨勫綊灞炰笌鏃堕棿鐐广 +5. **杞﹀瀷鈫斿鑸槧灏**锛氭槸鍚﹀瓨鍦ㄨ溅鍨嬩粎鏀寔鍗曚竴瀵艰埅鐨勭‖绾︽潫銆 + +### 楠屾敹鍙e緞锛堟瘡闃舵閫氱敤锛 +- 缂栬瘧 0 閿欒锛涙惉杩/鐜嚎/鍏呯數/浜ょ涓嶅洖褰掋 +- 閫夋嫨鎬у姞杞 + 鐑彃鎷旓細婵娲婚泦鍚堝唴鑳藉姏鍙敤锛屾湭婵娲讳笉鍔犺浇銆佸彲骞插噣鍗歌浇銆 +- 鍙嶅皠 API锛圫impleLite `/reflection`锛夈佽溅鍨/闈㈡澘闅忓凡婵娲 dll 鑷劧鏀舵暃銆 + +--- + +## 11. 瀹炴柦杩涘害璁板繂锛堣法浼氳瘽鎸佺画鏇存柊锛 + +> 鐘舵侊細猬 鏈紑濮 / 馃煢 杩涜涓 / 鉁 瀹屾垚 / 鈴 鏆傜紦銆 +> **鏈杩戞洿鏂帮細浼氳瘽1锛堢画3锛2026-06-09锛夆斺旇澶囦笁 dll 鍚堝苟涓哄崟涓 `StandardScene.Devices` + `ChangeAvoidanceParam` 鍘婚噸(鏂规 B)锛屽叏瑙 `dotnet build` 0 閿欒銆** + +### 11.0 褰撳墠浠g爜鐜扮姸锛堥噸瑕侊細宸查鍏堟棭鏈熸枃妗o級 + +浼氳瘽1 鎺ユ墜鏃跺彂鐜**浠g爜宸茶鍏堝墠浼氳瘽鎺ㄨ繘**锛屼笌 v2 鏂囨。/浼氳瘽14浜ゆ帴鎽樿鎻忚堪鐨勩宯et4.8 鍗曚綋銆佹湭鍔ㄤ唬鐮併**涓嶄竴鑷**銆備互 `StandardScene.csproj` 瀹為檯涓哄噯锛 + +| 椤 | 鏃╂湡鏂囨。璁拌堪 | **褰撳墠瀹為檯** | +|---|---|---| +| 鐩爣妗嗘灦 | net4.8 | **`net8.0-windows`**锛堜粛 `UseWindowsForms=true`锛岃繃娓℃侊紝**灏氭湭**鍘 WinForms 鍒扮函 net8.0锛 | +| 瀹夸富/濂戠害寮曠敤 | SimpleComposer.exe + RefSimpleCore | **宸插垏 `SimpleLite.dll` + `SimpleCore.dll`(netstandard2.0)** | +| Nancy | 1.4.5 | **宸插榻 `2.0.0`** | +| 姝讳唬鐮 | 鍦ㄤ粨搴 | csproj 宸 `Compile Remove`锛屼細璇1 宸茬墿鐞嗗垹闄 | +| 鍛藉悕绌洪棿 | SimpleComposer.RCS | 杞﹀瀷 using 宸叉槸 `SimpleLite.RCS`锛堝唴鏍告敼閫犱細璇濆凡鎺ㄨ繘锛 | + +**鍙紪璇戝熀绾**锛歚dotnet build`锛坣et8.0-windows锛宒otnet SDK 10.0.300锛= **0 閿欒 / 47 璀﹀憡**锛堝潎鏃犲锛氶噸澶 using銆侀殣钘忔垚鍛 CS0108銆佹湭鐢ㄥ彉閲忋乣Thread.Abort` SYSLIB0006銆丆S4014 鏈 await 绛夛級銆 + +### 11.1 姝ラ鐘舵 + +| 姝ラ | 鍐呭 | 鐘舵 | 璇存槑 | +|---|---|---|---| +| P0 | 绮捐鐜扮姸 + 浜у嚭鎷嗗垎璁″垝 v1 | 鉁 | 閫愭枃浠剁簿璇 | +| P0.1 | 鎸夎瘎瀹″弽棣堟洿鏂 v2 | 鉁 | | +| **璺嚎鍐崇瓥** | 璇勫瀹 **璺嚎涔**锛氬厛銆岀函鎼繍涓嶆敼閫昏緫銆嶇殑缁撴瀯鎷嗗垎锛屽幓閲嶅湪鏂扮粨鏋勯噷浠庡鍋 | 鉁 | 浼氳瘽1 鐢ㄦ埛鎷嶆澘 | +| **C0-a** | 鐗╃悊鍒犻櫎姝讳唬鐮 + 娓呯悊 csproj | 鉁 | 瑙 搂11.3 | +| **C0-b** | 鎶界 **4 涓氱敤(瀵艰埅鏃犲叧) coder** 骞跺湪 4 杞﹀瀷鍒囨崲 | 鉁 | 瑙 搂11.3锛涚紪璇 0 閿欒 | +| C0-c | 鍚堝苟涓や唤纾佸惊杩瑰櫒涓哄敮涓 `MagneticTrackCoder` | 鉁 | 鐢ㄦ埛瀹氾細閲囩敤鍚 `track.Speed` 鐨 `NaiveMagGo`锛汳WL 鏃х被宸插垹銆佸紩鐢ㄦ敼鍚戯紱缂栬瘧 0 閿欒 | +| C0-d | `ChangeAvoidanceParam` 鍘婚噸 | 鉁 | 鏂规 B锛4 鍙 flag 鐗 `AvoidanceParamCoder`(MWL+MultiVehicle) + 2 鍙 L,W 鐗 `AvoidanceParamLWCoder`(Kiva+Forklift)锛涜 搂11.4-2 | +| C0-e | 鎻愬彇 `StandardCarBase`锛坣ewReset/Mstsc/GetCarStatus 绛変笂鎻愶級| 猬 | 鏈惎鍔 | +| C0-f | `AMRScene1` 鍛藉悕绌洪棿鏀舵暃 | 猬 | 寤鸿骞跺叆 C1 寤 Core 宸ョ▼鏃朵竴璧峰仛 | +| 璺嚎涔櫬1 | 宸ョ▼杩佸叆 `StandardScene.Core/` + 閲嶅啓 sln锛堢函鎼繍锛 | 鉁 | 缂栬瘧 0 閿欙紱AssemblyName 浠=`StandardScene`锛屽澶 dll 鍚嶄笉鍙 | +| C1 | 鍘 WinForms + WebApi.Core 鏁寸悊 | 鈴 | 鐢ㄦ埛鏆傜紦锛堢獥浣撳悗缁縼 migu 骞冲彴锛夛紱杩囨浮鏈熷悇宸ョ▼鏆 `net8.0-windows` | +| C2 | Magnetic/QrCode/Laser 涓夊鑸 dll | 鈴 | 鍙楅樆浜庡唴鏍 coder 娉ㄥ唽琛(搂11.4-4) + 鐢ㄦ埛灏嗚嚜琛岄噸鏋 coder | +| **C3** | 璁惧椹卞姩 dll 鍖栵紙Charge/Door/ButtonBox锛岀儹鎻掓嫈锛 | 鉁 | 瑙 搂11.3-E3锛**鍚堜负鍗曚竴 `StandardScene.Devices`**(鐢ㄦ埛瀹氾紝涓嶅垎澶氫釜)锛岀紪璇 0 閿欍乻cene.json 浜у嚭 | +| **C4** | VDA5050 鍗忚 dll | 鉁 | 瑙 搂11.3-E2锛沗VDACar/` 鏁磋縼鍑恒乣VDA5050SiteField` 涓嬫矇 Core | +| C5 | 鑱旇皟 + WebApi 杩佺Щ SimpleLite + 鏂囨。鏀跺熬 | 猬 | 鍚崼鏄 dll鈫掑涓 `plugins/` 閮ㄧ讲 + 鐪熸満鑱旇皟锛堣 搂11.4-5锛 | + +### 11.2 鍏抽敭鍐崇瓥璁板綍 +- **璺嚎涔欙紙浼氳瘽1 纭锛**锛氱粨鏋勬媶鍒嗕紭鍏堛佺函鎼繍涓嶆敼閫昏緫锛涘幓閲嶆矇娣鍦ㄦ柊缁撴瀯鍐呭仛銆傜悊鐢憋細鏈幆澧冩棤娉曡窇鐪熷疄 AGV 鍥炲綊锛岀函鎼繍琛屼负椋庨櫓浣庛佺紪璇戝嵆鍙繚闅溿 +- 瀵艰埅涓庤溅鍨嬫浜わ紱瀵艰埅 coder 澶栫疆涓哄彲鎻掓嫈锛堣繍琛屾湡娉ㄥ唽锛屽唴鏍搁厤鍚堬紱**褰撳墠鐢ㄥ厹搴=Core/鍗曚綋鍐呯疆 coder**锛岀敤鎴疯姹"鍏堟娊绂诲悎閫傜殑閫氱敤 coder"锛夈 +- 婵鍏夐伩闅(LidarArea)鐣 Core锛屼粎 SLAM 鍦板浘(LidarMap/getLidarMap)杩 Laser銆 +- 璁惧椹卞姩鍗曞垪 `Devices.*` 鐙珛 dll + 鐑彃鎷旓紙璇勫纭锛夈 +- 瀹夸富 SimpleComposer鈫扴impleLite锛涙鏋剁粺涓 net8.0锛沀I 鍘 WinForms 杩 CycleGUI/Web锛堣瘎瀹$‘璁わ級銆 +- `FactoryTest`銆佹澗鐏垫畫鐣欏垹闄わ紙璇勫纭锛屼細璇1 宸叉墽琛岋級銆 +- 鑰 `WebApi.cs`(Nancy) 鏆傜暀 Core銆佹爣 deprecated锛屽悗鏈熻縼 SimpleLite EmbedIO锛岃縼绉荤储寮曡 搂4.7锛堣瘎瀹$‘璁わ級銆 + +### 11.3 浼氳瘽1 宸茶惤鍦版敼鍔ㄦ槑缁嗭紙2026-06-09锛 + +**A. C0 姝讳唬鐮佺墿鐞嗗垹闄**锛堢紪璇戣寖鍥翠笉鍙橈紝0 閿欒锛 +- 鍒 `FactoryTest.cs`锛坄[MissionType]` 灞曚細婕旂ず杩涚▼锛屽弽灏勬敞鍐岋紝鏃犲叾瀹冨紩鐢級 +- 鍒 鏍 `MultiWheelLifterCar.cs`锛堜笌 `CarTypes/MultiWheelLifterCar.cs` 閲嶅悕姝绘枃浠讹級 +- 鍒 鏍 `SongLingDeliveryViewer.Designer.cs`锛堟澗鐏垫畫鐣欙紝鏃犱富鏂囦欢锛 +- 鍒 `CarTypes/UselessCar.cs`锛坄[CarType]` 宸叉敞閲娿佹湭娉ㄥ唽锛 +- `csproj`锛氱Щ闄や笂杩 3 鏉 `Compile Remove`锛**淇濈暀** `Chained\AbstractChainedDeliveryMission.cs` 鐨 Remove锛堝绔嬫湭缂栬瘧鏃х増鏈紝鍘荤暀寰呭悗缁牳瀵癸級 + +**B. 鎶界閫氱敤(瀵艰埅鏃犲叧) coder** 鈥斺 鏂板 `Coders/CommonTrackCoders.cs`锛堝懡鍚嶇┖闂 `StandardScene.Coders`锛 +- 鍩虹被 `CommonTemplateTrackCoder : ITrackCoder`锛氬唴閮ㄥ鐢ㄥ唴鏍 `ProgramCoderHelper.PrepareTrackEngine` + Topaz 姹傚硷紝`useVerb`/`templateString` 涓庡師妯℃澘閫愬瓧涓鑷达紱缁熶竴鐢 `BasicXxxFields`锛堣繖浜 coder 浠呭紩鐢 Basic 瀛楁锛岀瓑浠凤級銆俻riority 鐢辫溅鍨 `[ProgramTrackCoderSettings]` 鎸囧畾銆 +- 4 涓氱敤 coder锛 + - `LidarAreaSwitchCoder`锛堥伩闅滐紝`track.LidarArea != -2` 鈫 `SwitchLidarArea`锛 + - `AvoidanceDistanceCoder`锛堟棤 useVerb 鈫 `ChangeAvoidanceDistance(StopDistance,SlowDistance)`锛 + - `IoAreaSwitchCoder`锛坄track.IOArea != -1` 鈫 `SwitchIoArea`锛 + - `TrackingErrThreshCoder`锛坄BiasAlarmThresh>0||DthAlarmThresh>0` 鈫 `ChangeTrackingErrThresh`锛 +- 4 杞﹀瀷宸叉妸瀵瑰簲鍐呰仈 `[TemplateTrackCoderSettings]` 鍒犻櫎锛屾敼涓 `[ProgramTrackCoderSettings]` 寮曠敤锛堜繚鐣欏悇鑷師 priority锛夛細 + +| 杞﹀瀷 | LidarArea | AvoidanceDistance | IoArea | TrackingErrThresh | +|---|---|---|---|---| +| `Kiva` | 17 | 20 | 20 | 20 | +| `MultiWheelLifterCar` | 27 | 20 | 20 | 20 | +| `MultiVehicleCar` | 27 | 20 | 20 | 20 | +| `Forklift` | 20 | 鈥(鍘熸棤) | 20 | 20 | + +- 鍚勮溅鍨嬪姞 `using StandardScene.Coders;`銆 +- **楠岃瘉**锛氭瘡姝 `dotnet build` 鍧 0 閿欒锛圞iva 鍗曞垏鍏堥獙鑼冨紡锛屽啀鎵归噺鎺 MWL/MultiVehicle/Forklift锛夈 + +**C. 浠嶄繚鐣欎负鍐呰仈 Template / 绋嬪簭 coder锛堟湰娆℃湭鍔級** +- 纾佺▼搴 coder锛歚Kiva.AllCarMagTrackCoder`銆乣MultiWheelLifterCar.MagTrackCoder`锛堝緟 C0-c 鍚堝苟锛 +- `ChangeAvoidanceParam`锛堜笁鍙樹綋锛屽緟 C0-d锛 +- `Kiva.KivaCarTrackCoder`(杞集)銆乣CalibrateWheelEncoder`(浠 Kiva) +- 浜岀淮鐮 `QrGo`锛堝鑸笓鐢紝褰 QrCode dll锛夛紱鍚勮溅鍨嬪彇鏀捐揣/閽昏溅/澶规姳/TireFollowing 绛夛紙杞﹀瀷涓撶敤锛 + +**D. 浼氳瘽1锛堢画锛夊幓閲嶈惤鍦**锛堢紪璇 0 閿欒 / 0 lint锛 +- **纾佸惊杩瑰櫒鍚堝苟锛圕0-c锛**锛歚Kiva.AllCarMagTrackCoder` 閲嶅懡鍚嶄负鍞竴 `MagneticTrackCoder`锛堝惈 `${track.Speed}` 鐨 `NaiveMagGo`锛岀敤鎴风‘璁わ級锛沗MultiWheelLifterCar.MagTrackCoder` 鏁寸被鍒犻櫎锛屽叾 `[ProgramTrackCoderSettings(priority=19)]` 鏀瑰悜 `MagneticTrackCoder`銆傦紙鏆傜疆 `Kiva.cs` 鐨 `StandardScene.CarTypes` 鍛藉悕绌洪棿锛孋2 鎶 Magnetic dll 鏃剁墿鐞嗚縼鍑恒傦級 +- **閬块殰 4 鍙傚鍚堝苟锛圕0-d 閮ㄥ垎锛**锛氭柊澧 `Coders.AvoidanceParamCoder`锛坄CommonTemplateTrackCoder` 瀛愮被锛岃鍐欑珯鐐瑰瓧娈佃涓哄惈 `ChangeAvoidanceParam` 鏍囧織浣嶇殑 `AvoidanceParamSiteFields`锛夛紝useVerb/妯℃澘涓庡師 `MWL`/`MultiVehicle` 閫愬瓧涓鑷达紱涓よ溅鍨嬪垹鍐呰仈 4 鍙傛ā鏉裤佹敼 `[ProgramTrackCoderSettings(priority=20)]` 寮曠敤銆傚熀绫绘柊澧炲彲瑕嗗啓 `SiteFieldsType` 閽╁瓙锛堥粯璁 `BasicSiteFields`锛夈 + +**E. 浼氳瘽1锛堢画2锛夌粨鏋勬ф媶鍒嗚惤鍦**锛堣矾绾夸箼路缁撴瀯鎷嗗垎锛涘叏瑙e喅鏂规 `dotnet build` 0 閿欒 / 45 鏃犲璀﹀憡锛 + +*E1. 宸ョ▼杩佸叆 `StandardScene.Core/`锛堢函鎼繍锛* +- 鍘熷崟浣撴暣浣撹縼鍏ュ瓙鐩綍 `StandardScene.Core/`锛沗StandardScene.csproj`鈫抈StandardScene.Core.csproj`锛**AssemblyName 浠=`StandardScene`**锛屽澶 dll 鍚嶄笉鍙橈級锛涢噸鍐 `StandardScene.sln`銆 +- 鏂板 `Properties/InternalsVisibleTo.cs`锛氬 4 鍗槦绋嬪簭闆嗗紑鏀 internal锛坄Protocol.VDA5050`/`Devices.Charge`/`Devices.Door`/`Devices.ButtonBox`锛夛紝浣垮崼鏄熶互"绾惉杩"璁块棶 Core internal锛屽厤鍘诲ぇ閲 internal鈫抪ublic 渚靛叆寮忔敼鍔ㄣ + +*E2. `StandardScene.Protocol.VDA5050`锛圕4锛* +- `CarTypes/VDACar/`锛10 鏂囦欢锛夋暣杩佽嚦鏂板伐绋嬶紱`scene.json`(provides VDA5050Car)锛沜sproj 寮 Core(ProjectReference)+SimpleLite/SimpleCore/CommonUsage/Topaz/Weaver + MQTTnet/Nancy/Jint/Newtonsoft銆 +- 瑙h︼細`VDA5050SiteField` 鐢 `VDACar/VDA5050Car.cs` **涓嬫矇**鑷 Core `CarTypes/ArmCar.cs`锛坄ArmCar` 鐣 Core 涓斿紩鐢ㄥ畠锛屼笉鑳藉弽渚濊禆 VDA dll锛夈 + +*E3. `StandardScene.Devices`锛圕3锛岀敤鎴烽 A锛**闂/鍏呯數妗/鎸夐挳鐩掑悎骞朵负鍗曚竴 dll**锛岀敤鎴峰畾涓嶅垎澶氫釜锛涘唴閮ㄦ寜 `Door/`銆乣Charge/`銆乣ButtonBox/` 瀛愮洰褰曠粍缁囷紝Core 鐨 IVT 浠 `StandardScene.Devices` 涓椤癸級* +- 鎼嚭 6 鍏蜂綋椹卞姩锛**鍛藉悕绌洪棿涓嶅彉**锛屼粛 `StandardScene.ExtendDevice.*` / `StandardScene.ChargeStationType`锛屼互婊¤冻鍙戠幇璋撹瘝 `t.Namespace==鍩虹被.Namespace`锛夛細 + - Door锛歚ModbusDoorController`锛堝熀绫 `BasicDoorController`+`[DoorType]` 鐣 Core锛 + - Charge锛歚FL/PCB/MuXingChargeStation`锛堝熀绫 `AbstractChargeStation`+`ChargeUdpService`/`StandardChargeMission` 鐣 Core锛 + - ButtonBox锛歚Leeg/AzowieButtonBox`锛堝熀绫 `BasicButtonBox`+`ButtonMission`/`ButtonBoxManager` 鐣 Core锛涜 dll 棰濆寮 `Ref/leegKeys-sdk.dll`锛 +- **琛屼负淇濇寔瑙h︼紙2 澶勪笅杞瀷鈫掑熀绫昏櫄鏂规硶锛**锛 + - `ChargeUdpService`锛歚if(cs is PCBChargeStation s) s.IndexReceive=msg[1]` 鈫 `cs.OnUdpMessage(msg)`锛坄AbstractChargeStation` 绌哄疄鐜般乣PCB` override锛夈 + - `ButtonMission`锛歚if(bb is AzowieButtonBox a) a.ClearButtonRegister(i)` 鈫 `bb.ClearButtonRegister(i)`锛坄BasicButtonBox` 绌哄疄鐜般乣Azowie` 鏀 override锛夈 +- **绫诲瀷鍙戠幇鏀硅法绋嬪簭闆嗭紙5 澶勶紝鍏抽敭锛**锛氬師 Core 闄愬畾鎵弿锛坄Type.GetType(绠鍗曞悕)` / `typeof(鍩虹被).Assembly.GetTypes()` / `Assembly.GetExecutingAssembly().GetTypes()`锛夊彧鍦 Core 鍐呮壘绫诲瀷锛岄┍鍔ㄥ绉诲悗**杩愯鏈熷皢鎵句笉鍒**銆傜粺涓鏀圭敤鍐呮牳鍏紑鐨 `SimpleLite.Utils.UiTypeDiscovery.AllTypes()`锛坄AppDomain.GetAssemblies()` 鍏ㄥ煙 + 鍐呯疆 `ReflectionTypeLoadException` 瀹夊叏鏋氫妇锛夛紝璋撹瘝涓嶅彉 鈫 浠婂ぉ鍦 Core 鍐呬粛鎵惧埌鍘熺被鍨(**琛屼负绛変环**)銆佸绉诲悗浜﹁兘鍙戠幇銆傛秹鍙 `StandardChargeMission` / `DoorMission` / `DoorManager` / `ButtonMission` / `ButtonBoxManager`銆 +- 鍏抽敭渚濇嵁锛歋impleLite 鑷韩绫诲瀷鍙戠幇鍗 `UiTypeDiscovery`(AppDomain 鍏ㄥ煙) + `PluginManager`(浠 `./plugins/*.dll` 浠 collectible ALC 鐑姞杞)鈫**鍗槦 dll 璺嚎杩愯鏈熸垚绔**锛堝悓鏃跺洖璇 VDA5050Car 鍙鍙戠幇锛夈 + +### 11.4 寰呯‘璁 / 闃诲椤癸紙褰卞搷 C0 鏀跺熬涓 C2锛 + +1. ~~**纾 `NaiveMagGo` 鍙傛暟宸紓**~~ **銆愪細璇1 宸插喅銆**锛氱敤鎴峰畾閲囩敤鍚 `${track.Speed}` 鐨勭増鏈紙Kiva 鐗堬級銆傚凡鍚堝苟涓哄敮涓 `MagneticTrackCoder`锛沗MultiWheelLifterCar.MagTrackCoder` 鍒犻櫎骞舵敼鍚戝紩鐢ㄣ傜紪璇 0 閿欒銆傦紙鏅 `MagGo` 涓や唤鍘熸湰涓鑷达紱鍚堝苟鍚庣閫昏緫鍏ㄥ钩鍙扮粺涓銆傦級 +2. ~~**`ChangeAvoidanceParam` 缁熶竴鏂瑰紡**~~ **銆愪細璇1缁3 宸插喅锛氭柟妗 B銆**锛氱敤鎴烽 B銆傝惤鍦帮紳4 鍙 flag 鐗 `AvoidanceParamCoder`(MWL+MultiVehicle) + 鏂板 2 鍙 L,W 鐗 `AvoidanceParamLWCoder`(Kiva+Forklift锛岃Е鍙戠粺涓鍙 `dst.CarLength!=-1 && dst.CarWidth!=-1`)锛涗袱杞﹀瀷鍐呰仈 `[TemplateTrackCoderSettings]`鈫抈[ProgramTrackCoderSettings(priority=20)]` 寮曠敤銆侳orklift 琛屼负涓嶅彉锛汯iva 鍘熸棤鏉′欢鈫掔粺涓鍚庢湭閰嶇疆闀垮(-1)鏃朵笉鍐嶄笅鍙 `(-1,-1)`锛堥鏈熷唴瀹夊叏鏀舵暃锛夈傜紪璇 0 閿欍傚師鍒嗘瀽鐣欐。濡備笅锛 + - 鐜扮姸锛歚Kiva`=鏃犳潯浠/2 鍙傦紱`Forklift`=`L鈮-1&&W鈮-1`/2 鍙傦紱`MWL`/`MultiVehicle`=`ChangeAvoidanceParam==true`/4 鍙(鍚腑蹇冪偣)銆 + - 宸茶惤鍦帮紙闆惰涓哄彉鏇达級锛氭妸**閫愬瓧鐩稿悓**鐨 `MWL`+`MultiVehicle` 4 鍙傚鍚堝苟涓 `Coders.AvoidanceParamCoder`銆 + - 鍏抽敭浜嬪疄锛歚CarLength/CarWidth/CarCenterX/CarCenterY` 鍧囧凡鍦 `BasicSiteFields` 瀹氫箟锛堜腑蹇冪偣榛樿 0锛夛紝鏁 4 鍙傛ā鏉垮鍚勮溅鍨嬮兘鍙В鏋愮紪璇戯紱宸紓鍙墿銆岃Е鍙戞潯浠 + 鍙傛暟涓暟銆嶃 + - 閫夐」锛堝凡鍦ㄤ細璇濅腑缁欑敤鎴凤級锛**A**=鐪熉峰崟 coder 鍒嗘敮锛坒lag鈫4 鍙 / 鍚﹀垯 L,W 鏈夋晥鈫2 鍙傦紱鍓綔鐢細Kiva 鏈厤缃珯鐐逛笉鍐嶅彂 `(-1,-1)` + MWL 璁句簡 L,W 浣 flag=false 浼氭柊鍙 2 鍙傦級锛**B**=涓 coder锛4 鍙 flag 鐗堬紜2 鍙 L,W 鐗堬紱浠 Kiva `(-1,-1)` 琛屼负宸紝鏃 MWL 姹℃煋锛夛紱**C**=鍙暀宸插悎骞剁殑鍚屾瀯瀵癸紝Kiva/Forklift 缁存寔鍐呰仈锛堥浂鍙樻洿锛夈**寤鸿 B**锛堥伩闅滄秹瀹夊叏锛屾渶灏忚涓哄樊锛夈 +3. ~~**鍘 WinForms 杩佺Щ绛栫暐**~~ **銆愪細璇1 鏆傚畾銆**锛氱敤鎴"鏆備笉/鍘绘帀"锛屾湰闃舵涓嶅姩绐椾綋锛岀暀寰 C1 涓撻棬澶勭悊銆 +4. ~~**鍐呮牳 coder 杩愯鏈熸敞鍐岃〃**~~ **銆愪細璇1 宸叉牳鏌ャ**锛氬綋鍓嶅唴鏍**鏃犳彃浠跺寲 coder 娉ㄥ唽琛**銆俙SegmentPlan.SetCoders()` 浠呯‖鎺 `TemplateCoderSet`+`ProgramCoderSet` 涓ゅ锛屽潎鎸**杞﹀瀷绫荤壒鎬**(`[*TrackCoderSettings]`)鍙嶅皠銆佸湪**鍚岀▼搴忛泦**鍐 `Activator.CreateInstance`(鏃犲弬鏋勯)瀹炰緥鍖栤斺旀棤澶栭儴 dll 鎵弿/鍔ㄦ佹敞鍐 API銆 + - 鎺ㄨ鈶狅細鏈鎶藉嚭鐨 `StandardScene.Coders.*` 绋嬪簭 coder **鑳借姝g‘鍔犺浇**锛堣溅鍨嬬壒鎬 `typeof()` 寮曠敤 + 鏃犲弬鏋勯狅紝鍛藉悕绌洪棿鏃犲叧锛夛紝鏁 C0-b/c/d 鎴愮珛銆 + - 鎺ㄨ鈶★細**C2銆屽鑸 coder 鎷嗙嫭绔嬪彲鐑彃鎷 dll銆嶅彈闃**鈥斺旈渶鍐呮牳渚э紙鍙︿竴浼氳瘽锛夎ˉ銆岃繍琛屾湡 coder 娉ㄥ唽 / 鎻掍欢绋嬪簭闆嗘壂鎻忋嶆彃妗╋紱鍦ㄦ涔嬪墠**缁х画鍏滃簳锛漜oder 鐣欏湪 Core 鍚岀▼搴忛泦**銆佺敱杞﹀瀷鐗规у紩鐢ㄣ +5. ~~**璁惧椹卞姩澶栫Щ鍚庤繍琛屾湡鍙戠幇**~~ **銆愪細璇1缁2 宸茶В銆**锛歋tandardScene 鑷湁鐨勮澶囩被鍨嬪彂鐜板師涓 **Core 闄愬畾鎵弿**锛堜笌鍐呮牳 `UiTypeDiscovery` 鍏ㄥ煙鍙戠幇涓嶄竴鑷达級锛岀洿鎺ュ绉讳細鏂傚凡鎶 5 澶勭粺涓鏀 `UiTypeDiscovery.AllTypes()`锛堣涓虹瓑浠枫佽法绋嬪簭闆嗭級銆**閬楃暀楠岃瘉椤癸紙杩愯鏈燂紝褰 C5锛**锛氬崼鏄 dll 椤昏 SimpleLite 瀹為檯鍔犺浇鍏 AppDomain锛坄PluginManager` 浠 `./plugins/` 鍔犺浇锛夊彂鐜版柟鐢熸晥鈥斺旈渶灏 `StandardScene.Devices.*.dll`+`scene.json` 閮ㄧ讲鍒板涓 `plugins/` 骞跺仛涓娆$湡鏈/闆嗘垚鑱旇皟銆備笌 coder(搂11.4-4) 涓嶅悓锛氳澶囧彂鐜扮爜鍦**鏈粨搴**銆佸彲鑷富淇敼锛屾晠 C3 涓嶅彈鍐呮牳闃诲銆 + +### 11.5 涓嬩竴浼氳瘽鎺ㄨ崘璧锋墜寮 +1. 璇 搂11.0锛堜唬鐮佺幇鐘讹級+ 搂11.3锛堝凡钀藉湴锛屽惈 **E. 缁撴瀯鎷嗗垎**锛+ 搂11.4锛堝緟纭/宸茶В锛夈 +2. **缁撴瀯鎷嗗垎鐜扮姸**锛歚StandardScene.Core` + 鍗槦 `Protocol.VDA5050`銆乣Devices.{Door,Charge,ButtonBox}` 宸插缓骞跺叏瑙 0 閿欙紱瀵艰埅 dll(C2) 寰呯敤鎴烽噸鏋 coder + 鍐呮牳娉ㄥ唽琛ㄥ悗鍐嶅姩銆 +3. 寰呯敤鎴峰氨 搂11.4-2 鎷嶆澘锛歚ChangeAvoidanceParam` 鏀跺熬锛堝缓璁 B锛夈俢oder 鏁翠綋閲嶆瀯鐢辩敤鎴蜂富瀵笺 +4. C5 楠岃瘉椤癸細鎶 `StandardScene.Devices.*.dll`+`scene.json` 閮ㄧ讲鍒板涓 `plugins/`锛岀湡鏈/闆嗘垚楠岃瘉璁惧涓 VDA 杞﹀瀷鐨**杩愯鏈熷彂鐜**锛埪11.4-5锛夈 +5. 鏍¢獙鎵嬫锛氭瘡姝 `dotnet build StandardScene.sln`锛坣et8.0-windows锛夐』淇濇寔 0 閿欒锛涚湡鏈哄洖褰掔暀寰呮湁鐜鏃躲 + +### 11.6 鍙樻洿鏃ュ織 + +| 鏃堕棿 | 姝ラ | 鏀瑰姩 | 澶囨敞 | +|---|---|---|---| +| 鍒濆 | P0 | 鏂板 `StandardScene鎷嗗垎璁″垝.md`(v1) | 鐜扮姸绮捐 + 褰掑睘鐭╅樀 + 鎶界娓呭崟 + 鎶鏈毦鐐 + 鍒嗛樁娈 | +| v2 | P0.1 | 鍏ㄦ枃鏇存柊 | 瀹夸富 SimpleLite銆佹鏋 net8.0銆佸幓 WinForms銆佽澶囩嫭绔 dll銆佸垹闄ら」銆乄ebApi 鏆傜暀+杩佺Щ绱㈠紩 | +| 浼氳瘽1 | C0-a | 鐗╃悊鍒犻櫎 4 涓浠g爜鏂囦欢 + 娓呯悊 csproj | 缂栬瘧 0 閿欒 | +| 浼氳瘽1 | C0-b | 鏂板 `Coders/CommonTrackCoders.cs`锛4 閫氱敤 coder锛夛紱Kiva/MWL/MultiVehicle/Forklift 鍒囨崲涓 ProgramTrackCoder 寮曠敤 | 缂栬瘧 0 閿欒锛汵aiveMag/AvoidanceParam 鐣欏緟纭 | +| 浼氳瘽1缁 | C0-c | 纾佸惊杩瑰櫒鍘婚噸锛歚AllCarMagTrackCoder`鈫掑敮涓 `MagneticTrackCoder`(鍚 `track.Speed`)锛涘垹 `MagTrackCoder` 鏀瑰悜寮曠敤 | 鐢ㄦ埛纭 track.Speed 鐗堬紱缂栬瘧 0 閿欒 | +| 浼氳瘽1缁 | C0-d | 閬块殰鍚屾瀯瀵瑰幓閲嶏細`MWL`+`MultiVehicle` 4 鍙傛ā鏉库啋`AvoidanceParamCoder`锛涘熀绫诲姞 `SiteFieldsType` 閽╁瓙 | 闆惰涓哄彉鏇达紱Kiva/Forklift 骞舵硶寰呭畾(搂11.4-2) | +| 浼氳瘽1缁 | 鏍告煡 | 鍐呮牳 coder 娉ㄥ唽鏈哄埗鏍告煡锛氭棤鎻掍欢娉ㄥ唽琛紝浠呰溅鍨嬬壒鎬+鍚岀▼搴忛泦鍙嶅皠 | 瑙 搂11.4-4锛汣2 闇鍐呮牳鎻掓々 | +| 浼氳瘽1缁2 | 璺嚎涔櫬1 | 宸ョ▼杩佸叆 `StandardScene.Core/`銆乣.csproj`鈫抈StandardScene.Core.csproj`銆侀噸鍐 `.sln`銆佸姞 `Properties/InternalsVisibleTo.cs`(4 鍗槦) | 绾惉杩愶紱缂栬瘧 0 閿 | +| 浼氳瘽1缁2 | C4 | 鎷 `Protocol.VDA5050`锛歚CarTypes/VDACar/`(10 鏂囦欢)鏁磋縼鍑猴紱`VDA5050SiteField` 涓嬫矇 `ArmCar.cs` 瑙h︼紱scene.json | 缂栬瘧 0 閿 | +| 浼氳瘽1缁2 | C3 | 鎷 `Devices.{Door,Charge,ButtonBox}`锛6 椹卞姩澶栫Щ锛2 澶 `is 鍏蜂綋绫籤鈫掑熀绫昏櫄鏂规硶锛5 澶勭被鍨嬪彂鐜扳啋`UiTypeDiscovery.AllTypes()` 璺ㄧ▼搴忛泦 | 琛屼负绛変环锛涘叏瑙g紪璇 0 閿欍3 dll+scene.json 浜у嚭 | +| 浼氳瘽1缁3 | C3 | 涓 `Devices.*` **鍚堝苟涓哄崟涓 `StandardScene.Devices`**锛堝唴閮 Door/Charge/ButtonBox 瀛愮洰褰曪紱IVT 3鈫1锛涘垹鏃 3 宸ョ▼銆侀噸鍐 sln锛 | 鐢ㄦ埛瀹"涓嶅垎澶氫釜"锛涚紪璇 0 閿欍佸崟 dll+scene.json 浜у嚭 | +| 浼氳瘽1缁3 | C0-d | `ChangeAvoidanceParam` 鍘婚噸鏀跺熬(鏂规 B)锛氭柊澧 2 鍙 `AvoidanceParamLWCoder`锛汯iva/Forklift 鍐呰仈妯℃澘鈫抈[ProgramTrackCoderSettings(priority=20)]` 寮曠敤 | 缂栬瘧 0 閿欙紱Forklift 涓嶅彉銆並iva `(-1,-1)` 瀹夊叏鏀舵暃 | + +--- + +*闄勶細鏈鍒掑紩鐢ㄧ殑绫/鏂囦欢/琛屽彿鍧囨潵鑷綋鍓嶅伐绋 `E:\Work\Core\Simple-FR\StandardSence` 瀹為檯浠g爜璧版煡锛汼impleLite 鎺ュ彛渚濇嵁 `E:\Work\Core\Simple-FR\Simple\SimpleLite\Docs\MIGU-API.md` 涓 `SimpleLite.csproj`锛坣et8.0 / CycleGUI / EmbedIO锛夈* diff --git a/StandardScene鏋舵瀯閲嶆瀯鏂规.md b/StandardScene鏋舵瀯閲嶆瀯鏂规.md new file mode 100644 index 0000000..2bc974d --- /dev/null +++ b/StandardScene鏋舵瀯閲嶆瀯鏂规.md @@ -0,0 +1,267 @@ +# StandardScene 鏋舵瀯閲嶆瀯鏂规 + +> 閰嶅鏂囨。锛氥奡tandardScene浠g爜瀹℃煡鎶ュ憡.md銆嬶紙璐ㄩ噺/缂洪櫡锛夈併奡tandardScene鎷嗗垎璁″垝.md銆嬶紙鎷嗗垎杩涘害锛 +> 鏈枃鑱氱劍**缁撴瀯涓庢灦鏋勫悎鐞嗘**锛氱幇鐘跺叏鏅 鈫 閫愭ā鍧楁繁搴﹀垎鏋 鈫 鐩爣鍒嗗眰 鈫 绋嬪簭闆嗚竟鐣 鈫 net8.0 鍘 Windows 鈫 鍒嗛樁娈佃縼绉昏矾寰勩 +> 鏃ユ湡锛2026-06-09 + +--- + +## 涓銆佺幇鐘舵灦鏋勫叏鏅 + +### 1.1 绋嬪簭闆嗕笌渚濊禆 + +| 绋嬪簭闆 | AssemblyName | TFM | 瑙掕壊 | 渚濊禆 | +|---|---|---|---|---| +| StandardScene.Core | `StandardScene` | net8.0-windows | 鍩哄骇锛氭娊璞+涓氬姟+浠诲姟+璋冨害+WebApi+UI | SimpleLite/SimpleCore/CommonUsage/MDCSToolBox/Topaz + NuGet(MQTTnet/Nancy/Jint/EasyModbus/IoTClient/OpenXml/Newtonsoft) | +| StandardScene.Devices | `StandardScene.Devices` | net8.0-windows | 鍏蜂綋璁惧椹卞姩锛堥棬/鍏呯數妗/鎸夐挳鐩掞級 | 鈫扖ore + SimpleLite/SimpleCore/Topaz + leegKeys-sdk | +| StandardScene.Protocol.VDA5050 | `StandardScene.Protocol.VDA5050` | net8.0-windows | VDA5050 鍗忚杞﹀瀷 | 鈫扖ore + SimpleLite/SimpleCore + NuGet(MQTTnet/Nancy/Jint/Newtonsoft) | + +鎻掍欢娓呭崟 `scene.json`锛圖evices / VDA5050 鍚勪竴浠斤級锛 + +```json +{ "id":"devices", "assembly":"StandardScene.Devices.dll", "requiresCore":"StandardScene.dll", + "provides": { "doorControllers":[...], "chargeStations":[...], "buttonBoxes":[...] } } +``` + +鍙戠幇鏈哄埗锛歚SimpleLite.Utils.UiTypeDiscovery.AllTypes()` 璺ㄧ▼搴忛泦鎵弿 + 绫诲瀷鐗规э紙`[DoorType]`/`[ChargeType]`/`[ButtonBox...]`/`[CarType]`锛夈 + +### 1.2 褰撳墠渚濊禆鏂瑰悜锛堥棶棰樼増锛 + +```mermaid +graph TD + Devices --> Core + VDA5050 --> Core + Core -->|NuGet| MQTTnet + Core -->|NuGet| Nancy + Core -->|NuGet| EasyModbus + Core -->|NuGet| IoTClient + Core -->|NuGet| OpenXml + Core --> WinForms[WinForms 12+ 绐椾綋] + Core --> SimpleLite + subgraph 鍗槦 + Devices + VDA5050 + end +``` + +**鏍稿績缁撴瀯闂**锛欳ore 鏄"涓囪兘鍩哄骇"鈥斺旀棦鏄娊璞″熀搴э紝鍙堝婊′簡鍏蜂綋鍗忚渚濊禆锛圡QTT 灞 VDA5050銆丮odbus/IoTClient 灞炶澶囥丯ancy 灞 WebApi銆丱penXml 灞炴姤琛級锛岃繕鍐呯疆 12+ WinForms 绐椾綋銆傚崼鏄熷彧鑳戒緷璧栬繖涓噧鑲 Core锛屾棤娉曠嫭绔嬫紨杩涖 + +### 1.3 浣撻噺鍒嗗竷锛堥潪 Designer锛屽墠鍒楋級 + +WebApi 2686 / AbstractLoopMission 1858 / VehicleMonitor 1502(UI) / AbstractChainedDeliveryMission 1407(宸 Compile Remove) / ChainedDeliveryMission 1365 / ChargeStationManagementForm 1250(UI) / AbstractChargeLogicMission 1143 / VDA5050Car 938 / DoorMission 932 / ButtonBoxManager 913 / DoorManager 848 / Kiva 827 / ButtonMission 743 / StandardChargeMission 671 / Commons 653 鈥 + +> 12+ 涓 600~2700 琛屽法绫伙紝鏄彲缁存姢鎬х殑涓昏鐭涚浘銆 + +--- + +## 浜屻佺粨鏋勬ч棶棰樿瘖鏂紙鎸夊奖鍝嶆帓搴忥級 + +| # | 闂 | 璇佹嵁 | 褰卞搷 | +|---|---|---|---| +| S1 | **鍒嗗眰姹℃煋**锛欳ore 鑳岃礋鍗忚/璁惧/鎶ヨ〃涓撴湁渚濊禆 | Core.csproj 寮 MQTTnet/EasyModbus/IoTClient/Nancy/OpenXml | 鍗槦鏃犳硶鐦﹁韩锛汣ore 缂栬瘧/閮ㄧ讲閲嶏紱鑱岃矗涓嶆竻 | +| S2 | **WinForms 鍏ㄦā鍧楁笚閫**锛岄樆濉炵函 net8.0 | 12+ `*.Designer.cs`锛圕harge 4 涓 Form銆乂ehicleMonitor銆佸悇 Manager/Viewer锛夛紱涓 csproj 鍧 `UseWindowsForms=true` + `net8.0-windows` | 鏃犳硶 `net8.0` 璺ㄥ钩鍙/鐦﹁繍琛岋紱涓"UI 杩 migu"鐩爣鍐茬獊 | +| S3 | **God-class 娉涙互** | WebApi 2686 / AbstractLoopMission 1858 / ChainedDeliveryMission 1365 / AbstractChargeLogicMission 1143 鈥 | 鏀瑰姩椋庨櫓楂樸佹祴璇曞洶闅俱佸苟鍙戞侀毦鎺ㄧ悊 | +| S4 | **鍏呯數瀛愮郴缁熸湭鐙珛**锛屽嵈宸茶嚜鎴愪綋绯伙紙20 鏂囦欢锛 | `Charge/` 浠诲姟+绔欑偣+閰嶇疆+鏁版嵁鏈嶅姟+閫氫俊+4 琛ㄥ崟 | 搴斾负鐙珛鍗槦锛屽嵈娣卞煁 Core | +| S5 | **鎶借薄涓庡疄鐜板悓灞 Core** | 璁惧鍩虹被/鐗规/Mission/Manager 鍦 Core锛屼粎鍏蜂綋椹卞姩鍦 Devices | 鍗槦浠嶅己渚濊禆 Core 鍐呴儴锛涚儹鎻掓嫈鍙楅檺 | +| S6 | **骞插噣鎶借薄鍙嶅悜鑰﹀悎鍒板法绫** | `Loop/ILoopRules.cs` 椤堕儴 `using static AbstractLoopMission;`锛堜緷璧栧叾宓屽 `LoopPoint`锛 | 濂芥帴鍙h宸ㄧ被缁戞灦锛屾棤娉曠嫭绔嬪鐢 | +| S7 | **鍏ㄥ眬鍙彉闈欐** | `DeliveryCallbackRegistry`锛坰tatic 瀛楀吀锛夈乣Commons` 闈欐佸伐鍏枫佸悇 `static HttpClient` | 闅愬紡鑰﹀悎銆佹祴璇曢殧绂婚毦銆佺敓鍛藉懆鏈熶笉鍙帶 | +| S8 | **鍐呮牳 Coder 娉ㄥ唽琛ㄩ檺瀹氭湰绋嬪簭闆嗗弽灏** | `ProgramCoderSet`/`SegmentPlan.Coder` 鐗规ч┍鍔ㄣ佹寜鍐呮牳绋嬪簭闆嗗弽灏 | 瀵艰埅绫诲崼鏄燂紙纾/浜岀淮鐮/婵鍏夛級鏃犳硶鐑彃鎷旓紙C2 闃诲锛 | +| S9 | **鏋勫缓鍙Щ妞嶆у樊** | csproj 澶氬缁濆 `HintPath`锛圗:\Work鈥︺丏:\MDCS鈥︼級 | 鎹㈡満/CI 鏃犳硶鐩存帴鏋勫缓 | +| S10 | **姝绘枃浠/寮冪敤骞跺瓨** | `AbstractChainedDeliveryMission.cs`(1407) 琚 `Compile Remove` 浠嶅湪鏍 | 璁ょ煡鍣煶銆佽鏀归闄 | +| S11 | **鍛藉悕绌洪棿涓庣▼搴忛泦鍚嶄笉涓鑷** | 涓夌▼搴忛泦 `RootNamespace=StandardScene`锛涚被鍨嬫暎钀 `StandardScene.*` 瀛愬懡鍚嶇┖闂 | 鐗╃悊杈圭晫涓庨昏緫杈圭晫閿欎綅锛岄毦鍒ゆ柇"璋佸睘浜庤皝" | + +--- + +## 涓夈侀愭ā鍧楁繁搴﹀垎鏋 + +> 姣忎釜妯″潡锛**鑱岃矗 / 缁撴瀯 / 渚濊禆涓庤﹀悎 / 涓昏闂 / 鐩爣澶勭疆**銆 + +### M1 CarTypes锛堣溅鍨嬫棌锛11 鏂囦欢锛 +- **鑱岃矗**锛氬畾涔夊悇 AGV 杞﹀瀷锛圞iva 827銆丗orklift 398銆丮ultiWheel*銆丮ultiVehicle銆丏ualLifting銆丄rmCar銆丏ummyCar 666锛+ 瀛楁琚 `BasicFields`(50) + 杞﹁締鐩戞帶 UI `VehicleMonitor`(1502, WinForms)銆 +- **缁撴瀯**锛氬瓧娈佃 `BasicCarFields/SiteFields/TrackFields/PlanFields` 璁捐鍚堢悊锛坄-1` 鍝ㄥ叺璇箟琚 Coder 姝g‘鍒╃敤锛夛紱杞﹀瀷绫绘壙杞介氫俊(HttpClient)銆佺姸鎬佹満銆佽皟搴︺乁I銆佸己鍒舵帶鍒剁瓑澶氳亴璐c +- **鑰﹀悎**锛氳溅鍨嬬洿寮 `Commons`銆丠ttpClient銆乣MessageBox`銆佸唴鏍哥被鍨嬶紱`VehicleMonitor` 鎶 UI 涓庤溅杈嗘ā鍨嬬粦瀹氥 +- **闂**锛氬法绫伙紙Kiva 827锛夈乣async void`+`throw`锛圞iva.ForceStop 619/659锛夈佺┖ catch銆佺‖缂栫爜 IP锛192.168.2.1:8008锛夈 +- **鐩爣澶勭疆**锛氳溅鍨嬩繚鐣欏湪棰嗗煙灞傦紱鍓ョ"閫氫俊/HTTP/UI"涓哄崗浣滆咃紙`ICarTransport`/`ICarStatusView`锛夛紱`VehicleMonitor` 杩 UI 绋嬪簭闆嗐 + +### M2 Coders锛堣建杩圭紪鐮佸櫒锛1 鏂囦欢 + 杞﹀瀷鍐呰仈锛 +- **鑱岃矗**锛歚CommonTrackCoders` 閫氱敤 `ITrackCoder`锛堢瀵艰埅缁熶竴 `MagneticTrackCoder`銆侀伩闅 `AvoidanceParamCoder`4鍙/`AvoidanceParamLWCoder`2鍙傦級銆 +- **缁撴瀯**锛氭湰杞凡鍘婚噸銆佺粨鏋勬竻鏅帮紱`CommonTemplateTrackCoder` 鎻愪緵 `SiteFieldsType` 鎵╁睍鐐广 +- **鑰﹀悎**锛氬彈鍐呮牳 `ProgramTrackCoderSettings`/`TemplateTrackCoderSettings` 鐗规х害鏉燂紙S8锛夈 +- **鐩爣澶勭疆**锛氱户缁妸杞﹀瀷鍐呰仈 Coder 鏀舵暃鑷虫锛涘緟鍐呮牳鏀惧紑娉ㄥ唽琛ㄥ悗锛屽鑸被 Coder 鍙笅娌夊埌瀵艰埅鍗槦銆 + +### M3 Chained锛堥摼寮/寰幆浠诲姟鏃忥紝12+2 鏂囦欢锛 +- **鑱岃矗**锛歚AbstractLoopMission`(1858)銆乣ChainedDeliveryMission`(1365)銆乣TransportMission`(549)銆乣LoopMission`銆佸洖璋冩敞鍐岃〃/闄勭潃鍣ㄣ乣Loop/` 瑙勫垯鎺ュ彛銆佽嫢骞 Viewer(UI)銆 +- **缁撴瀯**锛**涓ら潰鎬**鈥斺擿Loop/ILoopRules`(IEnter/IExit/IJoin/IBranch/ITaskStrategy) 涓 `DeliveryCallbackRegistry` 鏄鑼冪殑绛栫暐/娉ㄥ唽琛ㄦā寮忥紙浜偣锛夛紱浣 `AbstractLoopMission` 鏄 1858 琛屽法绫伙紝涓旀帴鍙 `using static AbstractLoopMission`锛圫6锛夊弽鍚戣﹀悎鍏跺祵濂楃被鍨嬨 +- **鑰﹀悎**锛氳8 `new Thread`+`while`+`Thread.Sleep`锛圥0-1 宸叉敼鍗忎綔寮忓仠姝級锛沀I Viewer 娣峰叆銆 +- **闂**锛氬法绫汇佺姸鎬佺敤瀛楃涓 `status.status`銆乣AbstractChainedDeliveryMission`(1407) 姝绘枃浠讹紙S10锛夈 +- **鐩爣澶勭疆**锛氭妸 `LoopPoint/LoopTask` 绛夐鍩熸ā鍨嬩粠宸ㄧ被**涓婃彁**鍒 Model/Abstractions锛岃 `Loop` 鎺ュ彛鐙珛锛涘法绫绘寜"璋冨害寰幆/浠诲姟缂栨帓/鏄剧ず"鎷嗗垎锛涘垹闄/褰掓。姝绘枃浠躲 + +### M4 InterLock锛堜簰閿侊紝4 鏂囦欢锛 +- **鑱岃矗**锛歚AbstractInterlockMission`(367)銆乣TrafficInterlockMission`銆乂iewer(UI)銆備氦閫氫簰閿侀昏緫銆 +- **鑰﹀悎**锛氫笌璋冨害/浜ら氭帶鍒惰﹀悎锛涘惈 Viewer銆 +- **鐩爣澶勭疆**锛氬綊鍏"浜ら/璋冨害"棰嗗煙瀛愭ā鍧楋紱UI 澶栨彁銆 + +### M5 Scheduler锛堣皟搴﹀悗鍙颁换鍔★紝4 鏂囦欢锛 +- **鑱岃矗**锛歚HeartBeatMission`銆乣NodeIsEnableMission`銆乣SecuritySignalMission`銆乣RegionalTrafficControlMission`(377)銆傚懆鏈熸у悗鍙颁换鍔★紙蹇冭烦/绔欑偣绂佺敤/瀹夊叏淇″彿涓婁紶/鍖哄煙浜ら氾級銆 +- **鑰﹀悎**锛氱洿鍙 HTTP锛堢‖缂栫爜绔偣锛夈乣Console.WriteLine`銆佸師 `while(true)`+`Thread.Abort`锛圥0-1 宸蹭慨涓 `while(started)`/鍗忎綔寮忥級銆 +- **闂**锛氭瘡涓换鍔″悇鍐欎竴濂楃嚎绋嬪惊鐜紙閲嶅锛夛紝鏃犵粺涓鍩虹被銆 +- **鐩爣澶勭疆**锛氭娊 `MissionRunnerBase`锛圕ancellationToken + 鐘舵佹灇涓 + 缁熶竴鏃ュ織锛夛紝鎵鏈夊懆鏈熶换鍔″鐢ㄣ + +### M6 Charge锛堝厖鐢靛瓙绯荤粺锛20 鏂囦欢 + ChargeStationType锛夆槄闇鐙珛 +- **鑱岃矗**锛氫换鍔★紙`StandardChargeMission`671/`AbstractChargeLogicMission`1143锛夈佺珯鐐癸紙`AbstractChargeStation`/`ChargeStation`锛夈侀厤缃紙`ChargeStrategyConfig`/`AlarmConfig`/`ChargingSetting`锛夈佹暟鎹湇鍔★紙3 涓 *DataService锛夈侀氫俊锛坄ChargeUdpService`/`CommunicationMessage(Service)`锛夈丠elper銆**4 涓 WinForms 琛ㄥ崟**銆 +- **缁撴瀯**锛氳嚜鎴愬畬鏁村瓙绯荤粺锛堜换鍔+璁惧+閰嶇疆+鎸佷箙鍖+閫氫俊+UI锛夛紝浣嗗叏鍩 Core銆俙CommunicationMessageService` 鏄**瀹夊叏瑙f瀽鑼冩湰**锛堝厛鏍¢獙闀垮害锛夈 +- **鑰﹀悎**锛氫笌鍏蜂綋鍏呯數妗╅┍鍔紙Devices/Charge锛夊弻鍚戯紙Core 鎸佷换鍔/鎶借薄锛孌evices 鎸 PCB/FL/MuXing 椹卞姩锛夛紱瀹炴椂 UDP 璺緞瓒婄晫锛圥0-3 宸蹭慨锛夈 +- **鐩爣澶勭疆**锛氬崌绾т负**鐙珛鍗槦 `StandardScene.Charge`**锛堝惈 Mission/鎶借薄/閰嶇疆/閫氫俊锛夛紝鍏蜂綋妗╅┍鍔ㄧ暀 `Devices` 鎴栧苟鍏ワ紱4 琛ㄥ崟杩 UI 绋嬪簭闆嗭紱閫氳繃 Abstractions 涓 Core 瑙h︺ + +### M7 ExtendDevice + Devices锛堣澶囷細闂/鎸夐挳鐩掞紝Core 渚 13 + Devices 渚 6锛 +- **鑱岃矗**锛欳ore 渚 = 鍩虹被(`BasicDoorController`/`BasicButtonBox`)+鐗规(`DoorTypeAttribute` 绛)+绠$悊鍣(`DoorManager`848/`ButtonBoxManager`913, 鍚 UI)+浠诲姟(`DoorMission`932/`ButtonMission`743)+妯″瀷/Monitor(UI)锛汥evices 渚 = 鍏蜂綋椹卞姩(`ModbusDoorController`/`Azowie`/`Leeg`/3 鍏呯數妗)銆 +- **缁撴瀯**锛**鏂伴┍鍔ㄨ川閲忎紭绉**锛坄ModbusDoorController`锛欳TS 鍗忎綔鍋滄/閿/鍙樻洿妫娴/閲嶈繛鑺傛祦/缁熶竴鏃ュ織锛夛紱`DoorTypeAttribute` 鐗规у彂鐜拌鑼冦 +- **鑰﹀悎**锛氭娊璞+涓氬姟鍦 Core銆侀┍鍔ㄥ湪 Devices锛圫5锛夛紱Manager 鍚 WinForms銆 +- **闂**锛歚ModbusDoorController` 鐢ㄦ瀽鏋勫嚱鏁板厹搴 `Disconnect`锛圙C 绾跨▼鍙栭攣+Wait锛岄闄╋級鈫掑簲瀹炵幇 `IDisposable`锛汳anager 宸ㄧ被鍚 UI銆 +- **鐩爣澶勭疆**锛氭妸璁惧**鎶借薄+鐗规**涓嬫矇鍒 `StandardScene.Abstractions`锛汳anager 鎷"璁惧鐢熷懡鍛ㄦ湡鏈嶅姟 + UI"锛涢┍鍔ㄧ粺涓 `IDisposable`銆 + +### M8 Protocol.VDA5050锛堝崗璁崼鏄燂紝9 鏂囦欢锛 +- **鑱岃矗**锛歚VDA5050Car`(938)銆乣MasterMQTTCommunication`銆乣VDA5050Interface/Segment/Helper/Commons`銆乣VDA5050WebApi`銆乣TextViewer`(UI)銆 +- **缁撴瀯**锛氬凡鏄嫭绔嬪崼鏄燂紙濂斤級锛涜嚜甯 WebApi 涓 MQTT 鏍堛 +- **闂**锛歚async void`+`throw ex`(240)銆佺‖缂栫爜 `192.168.2.1:8008` 涓旀敞閲婃帀鎸夎溅鍦板潃(150)銆佺┖ catch銆乣Console.WriteLine` 婊″竷銆乣monitor()` 姝诲眬閮ㄥ嚱鏁般 +- **鐩爣澶勭疆**锛氫綔涓哄崗璁崼鏄熻寖鏈紱绔偣閰嶇疆鍖栵紙鎸夎溅 `address`锛夈佸紓姝ヨ鑼冨寲銆佹棩蹇楃粺涓銆乣VDA5050WebApi` 涓 Core WebApi 璧扮粺涓 `ApiResult`/璺敱绾﹀畾銆 + +### M9 WebApi锛堣 Nancy 鎺ュ彛锛2686 琛岋級鈽呮渶楂樹紭鍏堥噸鏋 +- **鑱岃矗**锛氳溅杈/浠诲姟/鍦板浘/閰嶇疆绛 HTTP 鎺ュ彛锛圢ancy 2.0锛夈 +- **缁撴瀯**锛氬崟鏂囦欢涓婂笣璺敱锛涢敊璇搷搴 `new{Success=false,Code=500,...}` 澶嶅埗鍑犲崄澶勶紱鍙嶅皠 execute 绔偣锛圥0-2 宸插姞鐧藉悕鍗曪級銆 +- **鐩爣澶勭疆**锛氭娊 `ApiResult.Ok/Fail` + 鎸夎祫婧愭媶妯″潡锛圕arApi/MissionApi/MapApi鈥︼級锛涚粺涓閴存潈涓棿浠讹紙鏉ユ簮/浠ょ墝锛岄厤缃┍鍔級锛涜佹帴鍙e綊 `WebApi.Core(deprecated)` 瑙勫垝杩佺Щ锛涙渶缁堢嫭绔 `StandardScene.WebApi` 绋嬪簭闆嗭紙闅旂 Nancy 渚濊禆锛夈 + +### M10 Model锛堥鍩/閰嶇疆妯″瀷锛12 鏂囦欢锛 +- **鑱岃矗**锛歚Map/SimpleMap/MapStructure`銆乣SimpleConfig`銆乣TaskModel/LoopTask/MissionState`銆乣VehicleStatus`銆佸悇 `*Setting`銆 +- **缁撴瀯**锛氶鍩熸ā鍨嬩笌閰嶇疆娣峰眳锛沗Map.cs` 鍚‖缂栫爜 `127.0.0.1:4321`銆 +- **鐩爣澶勭疆**锛氭媶"绾鍩熸ā鍨嬶紙鈫扐bstractions锛"涓"閰嶇疆锛堚啋Configuration锛"锛涚鐐归厤缃寲銆 + +### M11 鍩虹璁炬柦锛圱CP 5 / Utils 4 / CommonTools 2锛 +- **鑱岃矗**锛歚AsyncTcpClient`(432, 璐ㄩ噺杈冨ソ)+浜嬩欢args锛沗JsonParser/JsonTool/ModbusClass/WebAPIHelper`锛沗AtomicFileUpdateHelper/SnowflakeIdGenerator`銆 +- **闂**锛歚AsyncTcpClient.Send` 鎶 `InvalidProgramException`锛堢被鍨嬩笉褰擄級銆乣EndWrite` 鏃犲紓甯稿鐞嗐乣uint on` 鏈敤锛沗ModbusClass` 涓庤澶 Modbus 閲嶅鍏虫敞鐐广 +- **鐩爣澶勭疆**锛氬綊鍏 `StandardScene.Infrastructure`锛坣et8.0 绾噣锛屾棤 Windows锛夛紱TCP/IO/搴忓垪鍖/ID 閫氱敤鍖栥 + +### M12 Core 鏍 God-files锛圕ommons 653 / Heuristic / LadderLogic / StandardCADTool / WebApi锛 +- **鑱岃矗**锛歚Commons` 涓囪兘宸ュ叿+璋冨害(`NearestTask`)銆乣Heuristic` 鍚彂寮忋乣LadderLogic` 姊舰閫昏緫銆乣StandardCADTool` CAD锛堢‖缂栫爜鐩樼璺緞銆乣async void`锛夈 +- **闂**锛歚Commons.AddOrUpdateXxxField` 閲嶅 4 浠姐乣CarValue` 蹇界暐 key銆乣GoSite` 鍋囬噸璇曪紙璇﹁瀹℃煡鎶ュ憡 P2-1锛夛紱鏍圭洰褰曞爢鏀炬棤褰掑睘澶ф枃浠躲 +- **鐩爣澶勭疆**锛歚Commons` 鎸夎亴璐f媶锛堝瓧娈垫湇鍔/璋冨害鏈嶅姟/鎺у埗鍙拌緟鍔╋級锛沗StandardCADTool` 璺緞閰嶇疆鍖栥佸綊 CAD 瀛愭ā鍧椼 + +--- + +## 鍥涖佺洰鏍囨灦鏋 + +### 4.1 鍒嗗眰涓庣▼搴忛泦杈圭晫锛堢洰鏍囷級 + +```mermaid +graph TD + subgraph L0[鎶借薄灞 net8.0 绾噣] + Abstractions[StandardScene.Abstractions
鎺ュ彛/鐗规/瀛楁琚/棰嗗煙妯″瀷/ITrackCoder/璁惧濂戠害] + Infra[StandardScene.Infrastructure
TCP/搴忓垪鍖/ID/IO/ILogger] + Config[StandardScene.Configuration
閰嶇疆妯″瀷+璇诲啓] + end + subgraph L1[棰嗗煙灞 net8.0] + Core2[StandardScene.Core
杞﹀瀷/浠诲姟鏃/璋冨害/浜ら/Coders] + end + subgraph L2[鍗槦 net8.0] + Charge2[StandardScene.Charge] + Devices2[StandardScene.Devices] + VDA[StandardScene.Protocol.VDA5050] + Nav[StandardScene.Nav.*锛堢/浜岀淮鐮/婵鍏夛紝寰呭唴鏍告斁寮锛塢 + end + subgraph L3[瀹夸富/鎺ュ叆 net8.0-windows] + Web[StandardScene.WebApi锛圢ancy 闅旂锛塢 + UI[StandardScene.UI.WinForms锛堜复鏃堵峰純鐢紝寰 migu锛塢 + end + Core2 --> Abstractions + Core2 --> Infra + Core2 --> Config + Charge2 --> Abstractions + Devices2 --> Abstractions + VDA --> Abstractions + Nav --> Abstractions + Charge2 -. 鍙楅檺 .-> Core2 + Web --> Core2 + UI --> Core2 + Devices2 -. NuGet .-> Modbus + VDA -. NuGet .-> MQTT + Web -. NuGet .-> Nancy +``` + +**鍏抽敭瑙勫垯**锛 +1. **渚濊禆鍙悜涓**锛氬崼鏄/瀹夸富 鈫 Abstractions(+鍙楅檺 Core)锛**Core 涓嶅緱渚濊禆浠讳綍鍏蜂綋鍗忚/璁惧 NuGet**锛圡QTT/Modbus/IoTClient/OpenXml/Nancy 鍏ㄩ儴涓嬫斁鍒板搴斿崼鏄/瀹夸富锛夈 +2. **鎶借薄鍏堣**锛氭帴鍙c佹敞鍐岀壒鎬с佸瓧娈佃銆佺函棰嗗煙妯″瀷銆乣ITrackCoder`/璁惧濂戠害缁熶竴杩 `Abstractions`锛坣et8.0锛屾棤 Windows锛夛紝鍗槦鍙 Abstractions銆 +3. **UI 涓庡崗璁 = 杈圭紭**锛歐inForms 鍏ㄩ儴鏀跺彛鍒 `UI.WinForms`锛堟爣 deprecated锛屼粎杩囨浮锛岃縼 migu 鍚庡垹锛夛紱Nancy 鏀跺彛鍒 `WebApi`銆傚姝 Abstractions/Infrastructure/Core/鍗槦鍙幓 `-windows`锛屽洖鍒扮函 `net8.0`銆 +4. **鍙戠幇缁熶竴**锛氳澶/杞﹀瀷/鍗忚缁熶竴"`[XxxType]` 鐗规 + `UiTypeDiscovery.AllTypes()`"锛宍scene.json` 澹版槑 `provides`銆 + +### 4.2 杈圭晫鎺ュ彛锛堟渶灏忛泦锛 +- `ILogger`锛堝彇浠 `Console.WriteLine`/鐩磋繛 Diagnosis锛夛細涓氬姟鍙緷璧栨娊璞° +- `IDeviceDriver`/`IDoorController`/`IChargeStation`/`IButtonBox`锛堜笅娌 Abstractions锛夛紝`IDisposable` 閲婃斁銆 +- `MissionRunnerBase`锛圕ancellationToken + 鐘舵佹灇涓 + 缁熶竴寮傚父/鏃ュ織锛夛細缁熶竴鎵鏈夊悗鍙颁换鍔$嚎绋嬫ā鍨嬨 +- `ApiResult`锛堢粺涓 HTTP 鍝嶅簲锛夛紝HTTP 閴存潈涓棿浠躲 +- `IEndpointProvider`/閰嶇疆娉ㄥ叆锛氭秷鐏‖缂栫爜 IP/URL/璺緞銆 +- `ICarTransport`锛堣溅杈嗛氫俊鎶借薄锛夛細鎶 HttpClient 浠庤溅鍨嬬被鍓ョ锛屽崟渚嬪寲銆 + +--- + +## 浜斻乶et8.0 鍘 Windows 渚濊禆璺緞锛堣В S2锛 + +1. **闅旂 UI**锛氭墍鏈 `*Form/*Viewer/*Monitor + *.Designer.cs`锛12+锛夎縼 `StandardScene.UI.WinForms`锛堝敮涓 `net8.0-windows`+`UseWindowsForms`锛夈 +2. **鍘 MessageBox**锛氫笟鍔″眰 `MessageBox.Show`锛圕ommons/Kiva/鍚 Manager锛夋敼涓轰簨浠/`ILogger`锛屽脊绐椾氦 UI 灞傘 +3. **鏍稿 Windows-only API**锛氬幓鎺 CA1416 鎶戝埗鍚庨愰」娑堣В锛圥/Invoke 鎺у埗鍙版樉闅愮瓑鏀跺彛鍒板涓伙級銆 +4. **鍒 TFM**锛欰bstractions/Infrastructure/Core/鍗槦鏀 `net8.0`锛涗粎 UI 涓庯紙濡傞渶锛夊涓讳繚鐣 `-windows`銆 + +--- + +## 鍏佸垎闃舵杩佺Щ璺緞锛堜綆椋庨櫓路姣忛樁娈 build-green路鍙洖褰掞級 + +> 寤剁画鏃㈡湁"璺嚎涔"锛**鍏堢粨鏋勭Щ鍔紙鏃犻昏緫鍙樻洿锛夆啋 鍐嶅幓閲/瑙h**锛屾瘡姝ュ彲鐙珛楠岃瘉銆 + +- **A 鎶借薄灞傚鍩**锛氬缓 `StandardScene.Abstractions`锛**绾Щ鍔**鎺ュ彛/鐗规/瀛楁琚/绾ā鍨嬶紙`ILoopRules`銆乣*TypeAttribute`銆乣BasicFields`銆乣LoopPoint/LoopTask` 绛夛級銆傝В S6/S11銆 +- **B 鍩虹璁炬柦鏀跺彛**锛氬缓 `Infrastructure`锛圱CP/Utils/CommonTools锛+`ILogger`锛涗慨 `AsyncTcpClient` 寮傚父绫诲瀷/EndWrite銆傝В S1锛堥儴鍒嗭級銆 +- **C UI 闅旂**锛氬缓 `UI.WinForms`锛岀Щ璧板叏閮ㄧ獥浣擄紱涓氬姟鍘 `MessageBox`銆傝В S2锛屾墦閫氬幓 `-windows`銆 +- **D 鍗忚/璁惧渚濊禆涓嬫斁**锛歁QTT鈫扸DA5050銆丮odbus/IoTClient鈫扗evices銆丯ancy鈫扺ebApi銆丱penXml鈫掓姤琛ㄦ墍鍦ㄥ崼鏄燂紱Core.csproj 娓呯┖涓撴湁 NuGet銆傝В S1銆 +- **E 鍏呯數鐙珛**锛氭娊 `StandardScene.Charge` 鍗槦锛圡ission/鎶借薄/閰嶇疆/閫氫俊锛夛紝琛ㄥ崟宸插湪 UI 灞傘傝В S4銆 +- **F 浠诲姟绾跨▼缁熶竴**锛氳惤鍦 `MissionRunnerBase`锛岃縼绉 Scheduler/Chained/Charge 鍚庡彴寰幆锛堝湪 P0-1 鍗忎綔寮忓仠姝㈠熀纭涓婏級銆傝В S3锛堝苟鍙戦潰锛夈 +- **G WebApi 閲嶆瀯**锛歚ApiResult`+鎸夎祫婧愭媶鍒+閴存潈涓棿浠讹紱鑰佹帴鍙e綊 deprecated銆傝В S3/S9锛圵ebApi锛夈 +- **H 宸ㄧ被鎷嗗垎**锛欰bstractLoopMission/ChainedDeliveryMission/AbstractChargeLogicMission/Kiva 鎸夎亴璐f媶鍒嗐傝В S3銆 +- **I 鍒 net8.0 + 娓呮鏂囦欢/缁濆璺緞**锛歍FM 鏀舵暃锛涘垹 `AbstractChainedDeliveryMission` 绛夋鏂囦欢锛沗HintPath` 鏀圭浉瀵/鍖呭彉閲忋傝В S2/S9/S10銆 +- **J 瀵艰埅鍗槦锛堜緷璧栧唴鏍革級**锛氬緟鍐呮牳鏀惧紑 Coder 娉ㄥ唽琛紙S8锛夛紝鎶界/浜岀淮鐮/婵鍏夊鑸崼鏄熴 + +姣忛樁娈靛嚭鍙f爣鍑嗭細`dotnet build` 0 閿欒銆佽鍛婁笉澧炪佸叧閿矾寰勫啋鐑熷彲杩囥 + +--- + +## 涓冦佹ā鍧楀缃煩闃碉紙閫熸煡锛 + +| 妯″潡 | 鐜颁綅缃 | 鐩爣浣嶇疆 | 鍏抽敭鍔ㄤ綔 | +|---|---|---|---| +| 鎺ュ彛/鐗规/瀛楁琚/绾ā鍨 | Core 鍚勫 | **Abstractions** | 绾Щ鍔 | +| TCP/Utils/CommonTools | Core | **Infrastructure** | 绉诲姩+`ILogger`+淇 TCP | +| 鍏ㄩ儴绐椾綋/Viewer/Monitor | 鍚勬ā鍧 | **UI.WinForms(deprecated)** | 绉诲姩+鍘 MessageBox | +| 鍏呯數锛堜换鍔/鎶借薄/閰嶇疆/閫氫俊锛 | Core/Charge | **StandardScene.Charge** | 鍗槦鍖 | +| 鍏呯數妗╁叿浣撻┍鍔 | Devices | Devices 鎴栧苟鍏 Charge | 缁熶竴 IDisposable | +| 闂/鎸夐挳鐩 鎶借薄+鐗规 | Core | Abstractions | 涓嬫矇 | +| 闂/鎸夐挳鐩 椹卞姩 | Devices | Devices | IDisposable | +| MQTT/Modbus/IoTClient/Nancy/OpenXml | Core NuGet | 鍚勫崼鏄/WebApi | 渚濊禆涓嬫斁 | +| WebApi | Core 鍗曟枃浠 | **StandardScene.WebApi** | 鎷嗗垎+閴存潈+ApiResult | +| 杞﹀瀷 | Core/CarTypes | Core锛堥鍩燂級 | 鍓ョ閫氫俊/UI 鍗忎綔鑰 | +| Coders | Core/Coders | Core锛堚啋瀵艰埅鍗槦 J 闃舵锛 | 缁х画鏀舵暃 | +| Commons/Heuristic/LadderLogic/CAD | Core 鏍 | 鎸夎亴璐e綊瀛愭ā鍧 | 鎷嗗垎+閰嶇疆鍖 | + +--- + +## 鍏侀闄╀笌绾︽潫 + +1. **鍐呮牳鑰﹀悎锛圫impleLite/SimpleCore锛**锛欳oder 娉ㄥ唽琛ㄦ寜鍐呮牳绋嬪簭闆嗗弽灏勶紙S8锛夆啋瀵艰埅鍗槦鐑彃鎷旈渶鍐呮牳鏀归狅紱`UiTypeDiscovery.AllTypes()` 宸叉敮鎸佽法绋嬪簭闆嗗彂鐜帮紙璁惧/杞﹀瀷鍙锛夈 +2. **鏃 Git 鍩虹嚎**锛氬ぇ閲忔枃浠舵湭绾冲叆鐗堟湰鎺у埗 鈫 **寮虹儓寤鸿鍏堝缓 Git 鍩虹嚎**鍐嶆墽琛 A~J锛屼繚璇佸彲鍥炴粴銆 +3. **琛屼负绛変环**锛氳溅鍨/鍏呯數/VDA5050 鍚澶囧崗璁椂搴忥紝绉诲姩闇淇濇寔鏃跺簭涓庡瓧娈佃涔夛紙娌跨敤"鍏堢Щ鍔ㄥ悗鍘婚噸"锛夈 +4. **缁濆 HintPath/鏈湴 dll**锛氳縼绉绘湡淇濇寔 HintPath 鍙敤锛孖 闃舵缁熶竴鐩稿鍖栵紝閬垮厤涓旀柇閾俱 +5. **migu 骞冲彴 UI**锛歎I.WinForms 浠呰繃娓★紱鎺ュ彛灞傦紙Abstractions/WebApi锛夊簲闈㈠悜 migu 鎻愪緵绋冲畾濂戠害锛孶I 杩佺Щ鍚庢暣鍖呭垹闄ゃ + +--- + +## 涔濄佽繎鏈熷彲绔嬪嵆鎵ц锛堝凡鍏峰鏉′欢锛屼綆椋庨櫓锛 +1. 鍒犻櫎/褰掓。姝绘枃浠 `AbstractChainedDeliveryMission.cs`锛堝凡 Compile Remove锛夈 +2. 鏂板缓 `StandardScene.Abstractions`锛屽厛杩 `BasicFields`銆乣*TypeAttribute`銆乣Loop/ILoopRules`锛堣В S6/S11锛屼笖涓嶆敼閫昏緫锛夈 +3. `MissionRunnerBase` 鎶藉彇锛堟壙鎺 P0-1 鍗忎綔寮忓仠姝㈡垚鏋滐紝缁熶竴 Scheduler 鍥涗换鍔★級銆 +4. `ApiResult` 甯姪鍣紙鍏堝湪 WebApi 鍐呮秷閲嶏紝闆惰涓哄彉鍖栵級銆