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(CLR 保证线程安全)
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:OnMissionFinished--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;
}
}
}
}