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 已下沉至 Core(ArmCar.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("请按顺序输入当前多点任务的站点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(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;
}
}