Files
StandardSence/StandardScene.Core/Chained/TransportDeliveryCallbacks.cs
T
2026-06-14 11:19:15 +08:00

207 lines
8.3 KiB
C#
Raw 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 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 ctorCLR 保证线程安全)
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-2OnMissionFinished--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;
}
}
}
}