Files
zhaowei.huang a0dc1e6cd0 refactor: 插件 UI 从 WinForms 迁移到 CycleGUI,并修复代码质量问题
将 StandardScene 各插件的配置/监控窗体从 WinForms 迁移到 CycleGUI(删除 .Designer.cs/.resx,重写为 PanelBuilder 立即模式 UI,新增 CycleUiHelper 统一对话框)。

同时修复代码审核中的问题:
- 后台文件写入加锁 + try/catch(ButtonBoxManager / DoorManager,对齐 LoopViewer.SaveTasks 模式)
- CoderFieldsMetadata.cs 启用 #nullable enable,消除 CS8632 警告
- DummyCar 移除已废弃的 rightClickAction()/SetPosition()
- CarRemoteHelper.OpenVehicleWebPage 的 Process.Start 加 try/catch
- 重命名名不副实的 Mstsc()(现为打开网页)
- 统一弃元命名为 _
- TrafficInterlockViewer 改用稳定 Id(GUID)做选择/编辑,替代行索引
- csproj 改用 $(CGUILibDir) 解析 CycleGUI,绝对路径收敛到 Directory.Build.props

构建:dotnet build StandardScene.sln → 0 错误,30 警告(均为历史遗留)。
注:static 单例状态重构(审核第 8 项)暂未处理,留待单独任务。
2026-06-26 15:00:53 +08:00

1067 lines
41 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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 static SimpleLite.RCS.CarTypes.ClumsyCar;
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 已下沉至 CoreArmCar.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 = "";
/// <summary>
/// Total length of base routes sent from master control to AGV.
/// </summary>
[FieldMember] public float BaseLength = 5000;
/// <summary>
/// Total length of horizon routes sent from mater control to AGV.
/// </summary>
[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<VDA5050Car> 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<stateMessage>(UpdateState,"vda5050/state").GetAwaiter().GetResult();
// _mqttCommunication.SubscribeTo<stateMessage>(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<System.Collections.Generic.Dictionary<string, string>>(jsonResponse1);
string jsonResponse1 = await hc.GetStringAsync($"http://{this.address}:8008/getStat");
var data1 = JsonConvert.DeserializeObject<System.Collections.Generic.Dictionary<string, string>>(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<object>(() =>
// {
// 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<int>();
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<RouteCache.Count&& RouteCache[(int)(seg.Item.sequenceId + 1)].State == VDA5050Segment.SegState.Executed)
shouldTriggerFinish = true;
if (shouldTriggerFinish && !seg.FinishTriggered)
{
seg.FinishToken.SetResult(1);
seg.FinishTriggered = true;
}
}
foreach (var item in _receivedLastConfirmedRoute)
{
Console.WriteLine("item.released: " + item.released +
" item.sequenceId:" + item.sequenceId);
if (item.released && RouteCache[(int)item.sequenceId].State ==
VDA5050Segment.SegState.BaseSending)
RouteCache[(int)item.sequenceId].State =
VDA5050Segment.SegState.BaseAcknowledged;
}
}
currentBase = RouteCache.FindLastIndex(rr =>
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<node>().ToArray(),
edges = activeItems.OfType<edge>().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<orderMessage>(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();
/// <summary>
/// Route state stored by master control.
/// </summary>
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<VDA5050Segment> func)
{
return _segments.FindLastIndex(func);
}
public bool Any(Func<VDA5050Segment, bool> func)
{
return _segments.Any(func);
}
public IEnumerable<VDA5050Segment> Where(Func<VDA5050Segment, bool> func)
{
return _segments.Where(func);
}
public IEnumerable<VDA5050Segment> 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<VDA5050Segment> _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;
/// <summary>
/// Route state stored by AGV.
/// </summary>
private List<sequenceItem> _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<sequenceItem>();
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);
}
/// <summary>
/// Called from the MQTT callback to store the latest state message.
/// </summary>
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("请按顺序输入当前多点任务的站点id:");
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("将DI41置为True");
Thread.Sleep(1000);
hc.GetStringAsync($"http://{this.address}:8008/setValue?FieldName=DI41&Value=False");
Console.WriteLine("1秒后将DI41置为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("将AO3置为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秒后将AO3置为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<T>(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;
}
}