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()); } } }