207 lines
8.3 KiB
C#
207 lines
8.3 KiB
C#
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
|
||
{
|
||
/// <summary>
|
||
/// 统一管理 TransportDelivery 相关的任务回调方法,并通过回调注册表提供可恢复性。
|
||
/// </summary>
|
||
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);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 显式调用以确保静态构造函数已执行(注册所有默认回调)。
|
||
/// </summary>
|
||
public static void EnsureInitialized()
|
||
{
|
||
// 访问本类,确保静态构造已执行
|
||
if (!_initialized)
|
||
{
|
||
// 触发 static ctor(CLR 保证线程安全)
|
||
System.Runtime.CompilerServices.RuntimeHelpers.RunClassConstructor(typeof(TransportDeliveryCallbacks).TypeHandle);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 配置任务状态回调的完整 URL(例如 http://ip:port/api/v1/MDCS/State)。
|
||
/// 建议在 TransportMission 启动或构造时调用一次。
|
||
/// </summary>
|
||
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<BaseRespose>();
|
||
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:OnMissionFinished--TaskId:{d.TaskId},task:[{d.ToJson()}].taskIdsString:{d.TaskIdsString}",
|
||
"holdCarTag",
|
||
true);
|
||
}
|
||
}
|
||
}
|
||
|
||
DispatchMissionState(d, MissionState.MissionStateEnum.Finished);
|
||
}
|
||
|
||
public static async Task<int> 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<BaseRespose>();
|
||
var result = resp.Code == 200 ? "成功" : "失败";
|
||
return 1;
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Diagnosis.Log($"暂停恢复失败 => ex:{ExceptionFormatter.FormatEx(ex)}");
|
||
return 0;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|