using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Reflection;
using System.Security.Cryptography;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Xml.Linq;
using MDCSToolBox.Clumsy.Movements;
using Nancy;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using SimpleLite;
using SimpleLite.RCS;
using SimpleLite.RCS.CarTypes;
using SimpleLite.CADTools;
using SimpleLite.Props;
using SimpleLite.UI;
using SimpleCore;
using SimpleCore.Library;
using SimpleCore.PropType;
using StandardScene.Chained;
using StandardScene.Charge;
using StandardScene.Model;
using StandardScene.Utils;
using static StandardScene.Chained.ChainedDeliveryMission;
using Map = StandardScene.Model.Map;
using Nancy.Session;
using StandardScene.InterLock;
namespace StandardScene
{
///
/// 【过渡期遗留】老平台 Nancy HTTP 接口(40+ 端点)。导航耦合极弱:仅二维码 QrMap(数据仍存
/// 本类静态字段,由 scene.qrlidar 插件的 SyncQrMap 工具写入)与 getLidarMap。
/// 拆分计划 §4.7:暂留 Core 维持现场兼容,后续逐步迁往 SimpleLite 的 EmbedIO 接口(MIGU-API),
/// 新功能请勿在此追加端点。
///
public class ApiController : NancyModule
{
private static readonly HttpClient SharedHttpClient = new HttpClient();
private static readonly string ApiToken = Environment.GetEnvironmentVariable("STANDARDSCENE_API_TOKEN");
private static List _taskRequests = new List();
public static MyDict QrMap =
new MyDict();
public static string QrMapJson;
private string GetMethods(Type baseClassType)
{
var assembly = Assembly.GetExecutingAssembly();
var derivedTypes = assembly.GetTypes()
.Where(t => t.IsSubclassOf(baseClassType));
var classMethodsDictionary = new Dictionary>();
foreach (var type in derivedTypes)
{
// old-school MethodMember
var methods = type.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)
.Where(m => m.GetCustomAttribute() != null && m.GetCustomAttribute() == null)
.Select(m =>
{
var attr = m.GetCustomAttribute();
var ret = new MethodInfoDetails() { MethodName = m.Name };
var nameField = attr.Name;
if (nameField != null) ret.ButtonName = nameField;
var descField = attr.Description;
if (descField != null) ret.ButtonDescription = descField;
return ret;
})
.ToList();
// Methods with parameters
var paramMethods = type.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)
.Where(m => m.GetCustomAttribute() != null)
.Select(m =>
{
var attr = m.GetCustomAttribute();
var ret = new MethodInfoDetails() { MethodName = m.Name };
var nameField = attr.name;
if (nameField != null) ret.ButtonName = nameField;
var descField = attr.desc;
if (descField != null) ret.ButtonDescription = descField;
var hintField = attr.Hint;
if (hintField != null) ret.ParamsHint = hintField;
ret.ParamsList = m.GetParameters().Select(mp => new ParamInfo(mp.Name, mp.ParameterType)).ToList();
return ret;
})
.ToList();
classMethodsDictionary[type.Name] = methods.Concat(paramMethods).ToList();
}
return JsonConvert.SerializeObject(
new { Success = true, Code = 200, Data = classMethodsDictionary, Message = "Success" },
Formatting.Indented);
}
//获取指定class所有方法的字典
private Dictionary> GetTypeMethods(Type type)
{
string[] shieldMethodButtonNames = new[]
{ "导出小车日志", "前往站点", "重启通信进程", "手动下发指令", "计划重启", "抛出中断", "放行", "显示调试信息", "给逃逸路径的初始化","返厂检修" };
var classMethodsDictionary = new Dictionary>();
// old-school MethodMember
var methods = type.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)
.Where(m => m.GetCustomAttribute() != null && m.GetCustomAttribute() == null)
.Where(m => !shieldMethodButtonNames.Any(n => m.GetCustomAttribute().Name.Contains(n)))
.Select(m =>
{
var attr = m.GetCustomAttribute();
var ret = new MethodInfoDetails() { MethodName = m.Name };
var nameField = attr.Name;
if (nameField != null) ret.ButtonName = nameField;
var descField = attr.Description;
if (descField != null) ret.ButtonDescription = descField;
return ret;
})
.ToList();
// Methods with parameters
var paramMethods = type.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)
.Where(m => m.GetCustomAttribute() != null)
.Select(m =>
{
var attr = m.GetCustomAttribute();
var ret = new MethodInfoDetails() { MethodName = m.Name };
var nameField = attr.name;
if (nameField != null) ret.ButtonName = nameField;
var descField = attr.desc;
if (descField != null) ret.ButtonDescription = descField;
var hintField = attr.Hint;
if (hintField != null) ret.ParamsHint = hintField;
ret.ParamsList = m.GetParameters().Select(mp => new ParamInfo(mp.Name, mp.ParameterType)).ToList();
return ret;
})
.ToList();
classMethodsDictionary[type.Name] = methods.Concat(paramMethods).ToList();
return classMethodsDictionary;
}
///
/// 反射调用白名单:仅允许显式标注为可暴露的方法被 HTTP 反射调用,
/// 与 GetMethods 的暴露口径一致,避免“按名调用任意 public 方法”的远程执行风险。
///
private static bool IsReflectionInvokable(MethodInfo methodInfo)
{
if (methodInfo == null) return false;
if (methodInfo.GetCustomAttribute() != null) return false;
return methodInfo.GetCustomAttribute() != null
|| methodInfo.GetCustomAttribute() != null;
}
private static string ReflectionForbidden(object id, string method)
{
return JsonConvert.SerializeObject(new
{
Success = false,
Code = 403,
Data = "null",
Message = $"method {method} of {id} is not exposed for reflection invocation"
}, Formatting.Indented);
}
private bool IsAuthorized()
{
if (string.IsNullOrEmpty(ApiToken)) return true;
try
{
var q = ((DynamicDictionary)Request.Query).ToDictionary();
return q.TryGetValue("token", out var t) && t != null && t.ToString() == ApiToken;
}
catch
{
return false;
}
}
private static string Unauthorized()
{
return JsonConvert.SerializeObject(new
{
Success = false,
Code = 401,
Data = "null",
Message = "unauthorized: missing or invalid api token"
}, Formatting.Indented);
}
private dynamic CarReflectionExecute(dynamic parameters)
{
try
{
if (!IsAuthorized()) return Unauthorized();
var car = SimpleLib.GetCar((int)parameters.id);
if (car == null)
return JsonConvert.SerializeObject(
new { Success = false, Code = 500, Data = "null", Message = $"no car of id {parameters.id}" }, Formatting.Indented);
var queryParams = ((DynamicDictionary)Request.Query).ToDictionary();
queryParams.Remove("token");
MethodInfo methodInfo = null;
if (queryParams.Keys.Count == 0)
methodInfo = car.GetType().GetMethod((string)parameters.method, new Type[] { });
else
methodInfo = car.GetType().GetMethod((string)parameters.method);
if (methodInfo == null)
return JsonConvert.SerializeObject(
new { Success = false, Code = 500, Data = "null", Message = $"car {parameters.id} has no method {parameters.method}" }, Formatting.Indented);
if (!IsReflectionInvokable(methodInfo))
return ReflectionForbidden(parameters.id, (string)parameters.method);
return ExecuteMethod(methodInfo, car, queryParams);
}
catch (Exception ex)
{
return JsonConvert.SerializeObject(
new { Success = false, Code = 500, Data = "null", Message = ex.Message }, Formatting.Indented);
}
}
private dynamic MissionReflectionExecute(dynamic parameters)
{
try
{
if (!IsAuthorized()) return Unauthorized();
var mission = SimpleProject.proj.Missions.FirstOrDefault(mm => mm.id == (int)parameters.id);
if (mission == null)
return JsonConvert.SerializeObject(
new { Success = false, Code = 500, Data = "null", Message = $"no mission of id {parameters.id}" }, Formatting.Indented);
var methodInfo = mission.GetType().GetMethod((string)parameters.method);
if (methodInfo == null)
return JsonConvert.SerializeObject(
new { Success = false, Code = 500, Data = "null", Message = $"mission {parameters.id} has no method {parameters.method}" }, Formatting.Indented);
var queryParams = ((DynamicDictionary)Request.Query).ToDictionary();
queryParams.Remove("token");
if (!IsReflectionInvokable(methodInfo))
return ReflectionForbidden(parameters.id, (string)parameters.method);
return ExecuteMethod(methodInfo, mission, queryParams);
}
catch (Exception ex)
{
return JsonConvert.SerializeObject(
new { Success = false, Code = 500, Data = "null", Message = ex.Message }, Formatting.Indented);
}
}
private string ExecuteMethod(MethodInfo methodInfo, object instance, Dictionary inputs)
{
var methodParam = methodInfo.GetParameters();
List