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 paramList = new List(); if (methodParam.Length == 0) { methodInfo.Invoke(instance, null); } else { if (inputs.Count != methodParam.Length) { return JsonConvert.SerializeObject( new { Success = false, Code = 500, Data = "null", Message = $"method need {methodParam.Length} params , but {inputs.Count}" }, Formatting.Indented); } for (int i = 0; i < methodParam.Length; i++) { paramList.Add(Convert.ChangeType(inputs[methodParam[i].Name], methodParam[i].ParameterType)); } var actualParam = paramList.ToArray(); methodInfo.Invoke(instance, actualParam); } return JsonConvert.SerializeObject( new { Success = true, Code = 200, Data = "null", Message = "Success" }, Formatting.Indented); } public ApiController() { After.AddItemToEndOfPipeline(ctx => { ctx.Response.WithHeader("Access-Control-Allow-Origin", "*") .WithHeader("Access-Control-Allow-Methods", "POST,GET") .WithHeader("Access-Control-Allow-Headers", "Accept, Origin, Content-Type"); }); // 反射接口共识: // 所有simple的调度对象(包括车、进程)只有id是全局唯一的。 // 所有simple的调度对象可能有不同的class,class相同的可能有不同的name(name并非全局唯一)。 // 所有simple的调度对象都有属性(field)、状态(status)、动作(methodMember)。 // 属性是可读可写的字典(用户设定属性,并保存属性作为运行配置)。状态是只读的字典。动作是函数成员。 // 反射接口全部返回json字符串,包括success和result两个字段。 // success为true或者false,分别表示请求成功或失败。 // result为返回内容。success为false时,result是报错内容。 // 返回className Get("/car_reflection/get_type/{id}", parameters => { try { 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); return JsonConvert.SerializeObject( new { Success = true, Code = 200, Data = car.GetType().Name, Message = "Success" }, Formatting.Indented); } catch (Exception ex) { return JsonConvert.SerializeObject( new { success = false, Code = 500, Data = "null", Message = ex.Message }, Formatting.Indented); } }); Get("/car_reflection/get_methods/{id}", parameters => { try { 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 classMethodsDictionary = GetTypeMethods(car.GetType()); if (classMethodsDictionary == null) { return JsonConvert.SerializeObject(new { Success = false, Code = 500, Data = "null", Message = $"未匹配到id:[{parameters.id}] 的车辆" }, Formatting.Indented); } return JsonConvert.SerializeObject(new { Success = true, Code = 200, Data = classMethodsDictionary.Values.FirstOrDefault(), Message = "Success" }, Formatting.Indented); } catch (Exception e) { return JsonConvert.SerializeObject(new { Success = false, Code = 500, Data = "null", Message = e.Message }, Formatting.Indented); } }); // 返回每种className对应有哪些动作 Get("/car_reflection/get_type_methods", _ => GetMethods(typeof(GhostCar))); // 执行动作(支持任意数量参数);执行类操作同时提供 POST 语义化入口,保留 GET 兼容旧前端 Get("/car_reflection/execute/{id}/{method}", parameters => CarReflectionExecute(parameters)); Post("/car_reflection/execute/{id}/{method}", parameters => CarReflectionExecute(parameters)); // 返回(id, name, className)的列表 Get("/mission_reflection/get_mission_list", _ => JsonConvert.SerializeObject(new { Success = true, Code = 200, Data = SimpleProject.proj.Missions.Select(mm => { var mType = (MissionType)mm.GetType().GetCustomAttribute(typeof(MissionType)); return new { id = mm.id, name = mm.name, typeName = mType.Name, state = mm.status.status }; }), Message = "Success" }, Formatting.Indented)); Get("/mission_reflection/get_type_methods", _ => GetMethods(typeof(Mission))); Get("/mission_reflection/execute/{id}/{method}", parameters => MissionReflectionExecute(parameters)); Post("/mission_reflection/execute/{id}/{method}", parameters => MissionReflectionExecute(parameters)); Get("/mission_reflection/get_status/{id}", parameters => { try { 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); return JsonConvert.SerializeObject( new { success = true, Code = 200, Data = mission.status, Message = "Success" }, Formatting.Indented); } catch (Exception ex) { return JsonConvert.SerializeObject( new { Success = false, Code = 500, Data = "null", result = ex.Message }, Formatting.Indented); } }); Get("/mission_reflection/get_fields/{id}", parameters => { try { 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); return JsonConvert.SerializeObject( new { Success = true, Code = 200, Data = mission.fields.Select(x => { return new { Key = x.Key, Vaule = x.Value }; }) .ToList(), Message = "Success" }, Formatting.Indented); } catch (Exception ex) { return JsonConvert.SerializeObject( new { Success = false, Code = 500, Data = "null", result = ex.Message }, Formatting.Indented); } }); Get("/mission_reflection/set_field/{id}/{field}/{value}", parameters => { try { var mission = SimpleProject.proj.Missions.FirstOrDefault(mm => mm.id == (int)parameters.id); if (mission == null) return JsonConvert.SerializeObject( new { success = false, Data = "null", Message = $"no mission of id {parameters.id}" }, Formatting.Indented); var fieldName = (string)parameters.field; var fieldValue = (string)parameters.value; mission.fields[fieldName] = fieldValue; return JsonConvert.SerializeObject( new { Success = true, Code = 200, Data = "null", Message = "Success" }, Formatting.Indented); } catch (Exception ex) { return JsonConvert.SerializeObject( new { Success = false, Code = 500, Data = "null", Message = ex.Message }, Formatting.Indented); } }); Get("/mission_reflection/delete_field/{id}/{field}", parameters => { try { 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 fieldName = (string)parameters.field; if (!mission.fields.ContainsKey(fieldName)) { return JsonConvert.SerializeObject(new { Success = false, Code = 500, Data = "null", Message = $"mission not found field {parameters.field}" }, Formatting.Indented); } mission.fields.Remove(fieldName); return JsonConvert.SerializeObject(new { Success = true, Code = 200, Data = "null", Message = "Success" }, Formatting.Indented); } catch (Exception ex) { return JsonConvert.SerializeObject(new { Success = false, Code = 500, Data = "null", Message = ex.Message }, Formatting.Indented); } }); Get("/mission_reflection/get_mission/{id}", parameters => { try { var classMethodsDictionary = new Dictionary>(); var mission = SimpleProject.proj.Missions.FirstOrDefault(m => m.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 method = mission.GetType() .GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance) .Where(m => m.GetCustomAttribute() != null).Select(m => { var attr = m.GetCustomAttribute(); var param = m.GetParameters(); var ret = new MethodInfoDetails() { MethodName = m.Name }; var name = attr.Name; if (name != null) ret.ButtonName = name; var desc = attr.Description; if (desc != null) ret.ButtonDescription = desc; return ret; }).ToList(); classMethodsDictionary[mission.name] = method; return JsonConvert.SerializeObject( new { Success = true, Code = 200, Data = classMethodsDictionary.Values.FirstOrDefault(), Message = "Success" }); } catch (Exception e) { return JsonConvert.SerializeObject(new { Success = false, Code = 500, Data = "null", Message = e.Message }); } }); // 需讨论如何实现 // Get["/save_parameters"] = _ => { }; #region 创建任务[Fass发布任务] Post("/car/createTask", _ => { Response response; var responseResult = new BaseRespose { Success = true, Code = (int)HttpStatusCode.OK, Message = "success" }; try { var jReq = new System.IO.StreamReader(Request.Body).ReadToEnd(); var req = jReq.JsonTo(); switch (req.MissionType) { case "Transport": CreateTransportTask: ( responseResult.Success, responseResult.Code, responseResult.Message, var _ ) = CreateTransportTask(req, true); break; default: goto CreateTransportTask; } response = Response.AsText( responseResult.ToJson(), "application/json;charset=UTF-8" ); return response; } catch (Exception ex) { Diagnosis.Log( $"起始站点匹配错误,请检查" + ExceptionFormatter.FormatEx(ex) + $"--time:{DateTime.Now.ToString()}", "error", true ); responseResult.Success = false; responseResult.Code = (int)HttpStatusCode.InternalServerError; responseResult.Message = ex.Message; response = Response.AsText( responseResult.ToJson(), "application/json;charset=UTF-8" ); return response; } }); #endregion #region 任务下发[Fass持续下发] Post("/car/carTask", _ => { Response response; var responseResult = new BaseRespose { Success = true, Code = (int)HttpStatusCode.OK, Message = "success" }; try { Stopwatch sw = new Stopwatch(); sw.Start(); string jReq = new System.IO.StreamReader(Request.Body).ReadToEnd(); TaskRequest req = jReq.JsonTo(); switch (req.MissionType) { case "Transport": CheckTransportTask: ( responseResult.Success, responseResult.Code, responseResult.Message, responseResult.Data ) = CreateTransportTask(req, false); break; default: goto CheckTransportTask; } response = Response.AsText( responseResult.ToJson(), "application/json;charset=UTF-8" ); //Diagnosis.Log( // $"carTask_Done:::consumeTime:{sw.ElapsedMilliseconds}", // "Interface", // true //); return response; } catch (Exception ex) { Diagnosis.Log( $"起始站点匹配错误,请检查" + ExceptionFormatter.FormatEx(ex) + $"--time:{DateTime.Now.ToString()}", "error", true ); responseResult.Success = false; responseResult.Code = (int)HttpStatusCode.InternalServerError; responseResult.Message = ex.Message; responseResult.Data = new CarStateInfo(); response = Response.AsText( responseResult.ToJson(), "application/json;charset=UTF-8" ); return response; } }); #endregion #region 状态查询 Post("/car/carState", _ => { Response response; try { Stopwatch sw = new Stopwatch(); sw.Start(); var jReq = new System.IO.StreamReader(Request.Body).ReadToEnd(); var req = jReq.JsonTo(); if (req.CarCode != "0") { var responseResult = new BaseRespose { Success = true, Code = (int)HttpStatusCode.OK, Message = "success" }; var singleCar = SimpleLib .GetAllCars() .OfType() .FirstOrDefault(e => e.id.ToString() == req.CarCode && e.GetLastSite() != -1 ); if (singleCar != null) { responseResult.Data = GetCarStateInfo(req.CarCode); } else { responseResult.Success = false; responseResult.Code = (int)HttpStatusCode.InternalServerError; responseResult.Message = $"车辆carCode:{req.CarCode}不存在或未初始化"; responseResult.Data = new CarStateInfo(); } response = Response.AsText( responseResult.ToJson(), "application/json;charset=UTF-8" ); //Diagnosis.Log( // $"carState_Done:::consumeTime:{sw.ElapsedMilliseconds}", // "Interface", // true //); return response; } else { var responseResult = new BaseRespose> { Success = true, Code = (int)HttpStatusCode.OK, Message = "success" }; var carStateList = new List(); var cars = SimpleLib.GetAllCars().OfType().ToList(); if (cars.Count > 0) { foreach (var car in cars) { var carStateInfo = GetCarStateInfo(car.name); carStateList.Add(carStateInfo); } responseResult.Data = carStateList; } else { responseResult.Success = false; responseResult.Code = (int)HttpStatusCode.InternalServerError; responseResult.Message = "当前系统中无启用调度车辆"; responseResult.Data = carStateList; } response = Response.AsText( responseResult.ToJson(), "application/json;charset=UTF-8" ); //Diagnosis.Log( // $"carState_Done:::consumeTime:{sw.ElapsedMilliseconds}", // "Interface", // true //); return response; } } catch (Exception ex) { Console.WriteLine($@"agv/getCar => ex: {ex.Message}"); var responseResult = new BaseRespose { Success = true, Code = (int)HttpStatusCode.OK, Message = "success" }; responseResult.Success = false; responseResult.Code = (int)HttpStatusCode.InternalServerError; responseResult.Message = ex.Message; responseResult.Data = new CarStateInfo(); response = Response.AsText( responseResult.ToJson(), "application/json;charset=UTF-8" ); return response; } }); #endregion #region 初始化小车 Get("/car/reset/{cartId}", _ => { var resp = new BaseRespose(); try { int cartId = _.cartId; Car car = (Car)SimpleLib.GetCar(cartId); //如果有任务不允许初始化 if (!car.tags.Contains("occupied")) { car.Reset(); car.tags.Add("idle", DateTime.Now.ToString()); } } catch (Exception e) { resp.Success = false; resp.Code = 500; resp.Message = e.Message; } var response = Response.AsText(resp.ToJson(), "application/json;charset=UTF-8"); return response; }); #endregion #region 现场维修 Get("/car/repair/{cartId}", _ => { var resp = new BaseRespose(); try { int cartId = _.cartId; Car car = (Car)SimpleLib.GetCar(cartId); car.Repair(); } catch (Exception e) { resp.Success = false; resp.Code = 500; resp.Message = e.Message; } var response = Response.AsText(resp.ToJson(), "application/json;charset=UTF-8"); return response; }); #endregion #region 返厂维修 Get("/car/blown/{cartId}", _ => { var resp = new BaseRespose(); try { int cartId = _.cartId; Car car = (Car)SimpleLib.GetCar(cartId); var cdm = SimpleProject.proj.Missions.OfType().First(); var deliver = cdm.GetDeliveries() .Find(p => ((TransportDelivery)p).UsingCar.id == car.id); if (deliver != null) { resp.Success = false; resp.Code = 500; resp.Message = "当前小车有任务需等任务结束后返厂"; } else { car.AppendDebug("ui-blown"); Diagnosis.Post($"Car {car.name}({car.id}) blown"); car.NoSchedule(); car.siteID = -1; car.tags.Clear(); car.status.usage.AddUsage( "base", new CarUsage.CarUsageInfo { scheduling = false, refreshing = false } ); car.lstatus = "返厂检修"; } } catch (Exception e) { resp.Success = false; resp.Code = 500; resp.Message = e.Message; } var response = Response.AsText(resp.ToJson(), "application/json;charset=UTF-8"); return response; }); #endregion #region 站点启用 Get("/site/enable/{siteId}", _ => { var resp = new BaseRespose(); try { int siteId = _.siteId; Site site = SimpleLib.GetSite(siteId); if (site.tags.Contains("unavailable")) site.tags.Remove("unavailable"); } catch (Exception e) { resp.Success = false; resp.Code = 500; resp.Message = e.Message; } var response = Response.AsText(resp.ToJson(), "application/json;charset=UTF-8"); return response; }); #endregion #region 站点禁用 Get("/site/disable/{siteId}", _ => { var resp = new BaseRespose(); try { int siteId = _.siteId; Site site = SimpleLib.GetSite(siteId); if (!site.tags.Contains("unavailable")) site.tags.Add("unavailable"); } catch (Exception e) { resp.Success = false; resp.Code = 500; resp.Message = e.Message; } var response = Response.AsText(resp.ToJson(), "application/json;charset=UTF-8"); return response; }); #endregion #region 取消任务 Post("/car/cancelTask", param => { Response response; var responseResult = new BaseRespose { Success = true, Code = (int)HttpStatusCode.OK, Message = "success" }; try { var jReq = new System.IO.StreamReader(Request.Body).ReadToEnd(); var req = jReq.JsonTo(); switch (req.MissionType) { case "Transport": TransportCancel: var cdm = SimpleProject .proj.Missions.OfType() .First(); var deliver = cdm.GetDeliveries() .Find(p => ((TransportDelivery)p).TaskId == req.TaskCode); if (deliver != null) { deliver.Canceled = true; if (!deliver.Cancel()) { responseResult.Success = false; responseResult.Code = 500; responseResult.Message = "任务取消失败"; return responseResult; } else { if (deliver.UsingCar != null) { var car = deliver.UsingCar; new Thread(() => { try { car.AppendDebug("Clumsy Restarted"); car.NoSchedule(true); car.siteID = -1; Commons.DeleteTag(car.tags, "occupied"); if (car.address != null && car.address != "") { SharedHttpClient.GetAsync( $"http://{car.address}:8008/reset" ); } car.AppendDebug("restarting clumsy"); //car.Get("reset"); car.AppendDebug( "wait for any pending task to flush." ); car.status.programs.task.Wait(); //car.Reset(); } catch (Exception) { responseResult.Message = "重启C失败"; } }).Start(); } } } else { responseResult.Success = false; responseResult.Code = 500; responseResult.Message = $"CDM中不存在任务id[{req.TaskCode}]"; } break; default: goto TransportCancel; break; } response = Response.AsText(responseResult.ToJson(), "application/json"); return response; } catch (Exception e) { responseResult.Code = (int)HttpStatusCode.InternalServerError; responseResult.Message = $"任务取消失败! ex:{e.Message}"; response = Response.AsText(responseResult.ToJson(), "application/json"); return response; } }); #endregion #region 获取Simple地图 Get("/map/getMap", _ => { var resp = new BaseRespose() { Success = true, Code = (int)HttpStatusCode.OK, Message = "success" }; try { resp.Data = Map.GetMap(); } catch (Exception e) { resp.Success = false; resp.Code = 500; resp.Message = e.Message; } var response = Response.AsText(resp.ToJson(), "application/json;charset=UTF-8"); return response; }); #endregion #region 获取雷达地图 Get("/map/getLidarMap", _ => { var resp = new BaseRespose() { Success = true, Code = (int)HttpStatusCode.OK, Message = "success" }; try { resp.Data = LidarMap.GetLidarMap().Result; } catch (Exception e) { resp.Success = false; resp.Code = 500; resp.Message = e.Message; } var response = Response.AsText(resp.ToJson(), "application/json;charset=UTF-8"); return response; }); #endregion #region 获取所有小车信息 Get("/car/getAllCars", _ => { var resp = new BaseRespose>(); try { resp.Data = new List(); foreach (var car in SimpleLib.GetAllCars()) { resp.Data.Add(CarBaseInfo.FromCar((Car)car)); } } catch (Exception e) { Console.WriteLine(e.StackTrace); resp.Code = 500; resp.Message = e.Message; } var response = Response.AsText(resp.ToJson(), "application/json;charset=UTF-8"); return response; }); #endregion #region 去某地 Get("/car/goSite", param => { var carCode = Request.Query["carCode"]; var id = Request.Query["id"]; var responseResult = new BaseRespose { Success = true, Code = (int)HttpStatusCode.OK, Message = "success" }; try { if (carCode != 0 && id != 0) { Car car = (Car)SimpleLib.GetCar(carCode); Site site = SimpleLib.GetSite(id); Commons.GoSite(car, site, 1); } var response = Response.AsText(responseResult.ToJson(), "application/json"); return response; } catch (Exception) { return Response.AsText("调用小车去某地失败".ToJson(), "application/json"); } }); #endregion #region 获取任务列表 Get("/task/getTask", _ => { Response response; var resp = new BaseRespose>() { Success = true, Code = (int)HttpStatusCode.OK, Message = "success" }; try { var missions = SimpleProject .proj.Missions.OfType() .ToList(); List deliveryList = new List(); if (missions.Count == 0) return Response.AsText(resp.ToJson(), "application/json;charset=UTF-8"); foreach (var cdm in missions) { var finishTaskList = cdm.GetDeliveries(true, true) .Where(e => e.FinishTime > DateTime.Now.AddMinutes(-30)) .ToList(); //30min内已完成完成任务 var executingTaskList = cdm.GetDeliveries(); //未完成任务 foreach (var delivery in finishTaskList) { var temp = (TransportDelivery)delivery; deliveryList.Add( new TaskRecord { TaskId = temp.TaskId, Name = $"{temp.Src} =>{temp.Dst}", CarId = temp.UsingCar.id.ToString(), CarName = temp.UsingCar.name, State = GetDeliveryStatus(temp), SrcSiteId = temp.Src.ToString(), DestSiteId = temp.Dst.ToString(), Priority = temp.Priority, StartTime = temp.StartTime, EndTime = temp.FinishTime, Created = temp.CreateTime } ); } foreach (var delivery in executingTaskList) { var temp = (TransportDelivery)delivery; deliveryList.Add( new TaskRecord { TaskId = temp.TaskId, Name = $"{temp.Src} =>{temp.Dst}", CarId = temp.UsingCar.id.ToString(), CarName = temp.UsingCar.name, State = GetDeliveryStatus(temp), SrcSiteId = temp.Src.ToString(), DestSiteId = temp.Dst.ToString(), Priority = temp.Priority, StartTime = temp.StartTime, EndTime = temp.FinishTime, Created = temp.CreateTime } ); } resp.Data = deliveryList; } } catch (Exception e) { resp.Success = false; resp.Code = 500; resp.Message = e.Message; } response = Response.AsText(resp.ToJson(), "application/json;charset=UTF-8"); return response; }); #endregion #region 强制结束 Get("/car/ForceStop/{cartId}", _ => { #region /* try { int cartId = _.cartId; var car = (Car)SimpleLib.GetCar(cartId); try { if (car == null) { resp.Success = false; resp.Code = 500; resp.Message = "小车在系统中没找到"; } else { car.AppendDebug("ui-repair"); car.tags.Clear(); car.NoSchedule(makeUnavailable: false); car.siteID = -1; car.lstatus = "结束任务"; var cdm = SimpleProject .proj.Missions.OfType() .First(); var deliver = cdm.GetDeliveries() .Find(p => ((TransportDelivery)p).usingCar.id == car.id); if (deliver != null) { deliver.canceled = true; if (!deliver.Cancel()) { deliver.usingCar.UIIntercept(); resp.Success = false; resp.Code = 500; resp.Message = "任务取消失败"; } } new Thread(() => { try { //car.status.programs.task.Wait(); HttpClient client = new HttpClient(); client.GetAsync($"http://{car.address}:8008/reset"); } catch { resp.Success = false; resp.Code = 500; resp.Message = "重启C失败"; } }).Start(); resp.Success = true; resp.Code = 200; resp.Message = "强制结束任务成功"; } } catch (Exception e) { resp.Success = false; resp.Code = 500; resp.Message = e.Message; } } catch (Exception e) { resp.Success = false; resp.Code = 500; resp.Message = e.Message; }*/ #endregion var resp = new BaseRespose() { Message = "任务取消成功" }; try { int cartId = _.cartId; var car = (Car)SimpleLib.GetCar(cartId); car.NoSchedule(true); car.siteID = -1; Commons.ClearTags(car.tags); car.tags.Clear(); var cdm = SimpleProject.proj.Missions.OfType().First(); var deliver = cdm.GetDeliveries() .Find(p => ((TransportDelivery)p).UsingCar.id == car.id); int count = 0; if (deliver != null) { deliver.Canceled = true; if (!deliver.Cancel()) { resp.Success = false; resp.Code = 400; resp.Message = "任务取消失败"; var res = Response.AsText( resp.ToJson(), "application/json;charset=UTF-8" ); return res; } } new Thread(() => { try { SharedHttpClient.GetAsync($"http://{car.address}:8008/reset"); car.AppendDebug("restarting clumsy"); //car.Get("reset"); car.AppendDebug("wait for any pending task to flush."); car.status.programs.task.Wait(); car.AppendDebug("Clumsy Restarted"); car.NoSchedule(); car.siteID = -1; Commons.DeleteTag(car.tags, "occupied"); // car.Reset(); } catch (Exception) { resp.Message = "重启C失败"; } }) { Name = $"ForceStop:{car.name}({car.id})" }.Start(); } catch (Exception e) { resp.Success = false; resp.Code = 500; resp.Message = e.Message; } var response = Response.AsText(resp.ToJson(), "application/json;charset=UTF-8"); return response; }); #endregion #region 重启电脑 Get("/car/ReStart/{cartId}", _ => { var resp = new BaseRespose(); int cartId = _.cartId; var car = (Car)SimpleLib.GetCar(cartId); var task = Task.Run(() => { try { resp.Success = false; resp.Code = 200; resp.Message = "重启中"; // 创建一个Process对象 Process process = new Process(); // 设置启动信息 process.StartInfo.FileName = "shutdown"; process.StartInfo.Arguments = "/r /t 0"; // 重启电脑,立即执行 process.StartInfo.CreateNoWindow = true; // 不显示窗口 process.StartInfo.UseShellExecute = false; // 不使用操作系统外壳程序启动进程 // 启动进程 process.Start(); } catch (Exception e) { resp.Success = false; resp.Code = 400; resp.Message = e.Message; } }); task.Wait(); var response = Response.AsText(resp.ToJson(), "application/json;charset=UTF-8"); return response; }); #endregion #region 停接任务 Get("/car/stopReceiveTask/{cartId}", _ => { var resp = new BaseRespose(); int cartId = _.cartId; try { var car = (Car)SimpleLib.GetCar(cartId); if (car != null) { if (!car.fields.ContainsKey("StopAccept")) { Commons.AddOrUpdateCarField(car, "StopAccept", "1"); } else { Commons.DeleteCarField(car, "StopAccept"); } } else { resp.Success = false; resp.Code = 500; resp.Message = "找不到对应小车"; } } catch (Exception e) { resp.Success = false; resp.Code = 500; resp.Message = e.Message; } var response = Response.AsText(resp.ToJson(), "application/json;charset=UTF-8"); return response; }); #endregion #region 获取小车信息 Get("/car/carInfo/{cartId}", _ => { var resp = new BaseRespose(); int cartId = _.cartId; try { var car = (Car)SimpleLib.GetCar(cartId); if (car != null) { resp.Data = new CarInfo() { Name = car.name, Address = car.address, Speed = car.status.enums.TryGetValue( "ActualLeftWheelVelocity", out var statusEnum ) ? statusEnum : car.speed.ToString(), Voltage = car.status.enums.TryGetValue("soc", out var soc) ? float.Parse(soc) : -1, Code = car.id.ToString(), siteId = car.GetLastSite() }; } } catch (Exception e) { resp.Success = false; resp.Code = 500; resp.Message = e.Message; } var response = Response.AsText(resp.ToJson(), "application/json;charset=UTF-8"); return response; }); #endregion #region 去待命点 Get("/car/goToStandby", param => { var CarCode = Request.Query["carCode"]; //var id = Request.Query["id"]; Response response; var responseResult = new BaseRespose { Success = true, Code = (int)HttpStatusCode.OK, Message = "success" }; try { var car = (Car)SimpleLib.GetCar(CarCode); if (car != null) { var carGroup = car.fields.TryGetValue("group", out var value) ? value : "all"; var targetPlan = Commons.GetNearestPlan( (Car)car, 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(car, targetPlan.Destination, 1); } } else { responseResult.Success = false; responseResult.Code = 500; responseResult.Message = "没有找到任务小车"; } response = Response.AsText(responseResult.ToJson(), "application/json"); return response; } catch (Exception) { return Response.AsText("调用小车去待命点失败".ToJson(), "application/json"); } }); #endregion #region 去充电点 Get("/car/goToCharge", param => { var CarCode = Request.Query["carCode"]; //var id = Request.Query["id"]; Response response; var responseResult = new BaseRespose { Success = true, Code = (int)HttpStatusCode.OK, Message = "success" }; try { var car = (Car)SimpleLib.GetCar(CarCode); if (car != null) { /* var carGroup = car.fields.TryGetValue("group", out var value) ? value : "all"; var targetPlan = Commons.GetNearestPlan((Car)car, site => site.fields.ContainsKey("charge") && site.fields["charge"] == "true" && carGroup.Equals(site.fields.TryGetValue("group", out var value) ? value : "all") ); if (targetPlan != null) { Diagnosis.Post($"小车路径规划成功目标充电位{targetPlan.Destination}", "去充电",true); Commons.GoSite(car, targetPlan.Destination, 1); } else { Diagnosis.Post("小车路径规划失败", "去充电", true); }*/ Commons.AddOrUpdateTag(car.tags, "shouldCharge", "true"); } else { responseResult.Success = false; responseResult.Code = 500; responseResult.Message = "没有找到任务小车"; } response = Response.AsText(responseResult.ToJson(), "application/json"); return response; } catch (Exception) { return Response.AsText("调用小车去充电电点失败".ToJson(), "application/json"); } }); #endregion #region 暂停恢复 Get("/car/startOrPause", param => { var carCode = Request.Query["CarCode"]; var responseResult = new BaseRespose { Success = true, Code = (int)HttpStatusCode.OK, Message = "success" }; try { if (carCode == 0) { responseResult.Success = false; responseResult.Code = (int)HttpStatusCode.BadRequest; responseResult.Message = "没有找到任务小车"; return Response.AsJson(responseResult); } ClumsyCar car = SimpleLib.GetCar(carCode) as ClumsyCar; if (car == null) { responseResult.Success = false; responseResult.Code = (int)HttpStatusCode.NotFound; responseResult.Message = "未找到对应的小车"; return Response.AsJson(responseResult); } string releaseStatus = Commons.GetCarStatus(car, "ISRelease"); try { ToggleCarReleaseStatus(car, releaseStatus); } catch (Exception ex) { responseResult.Success = false; responseResult.Code = (int)HttpStatusCode.InternalServerError; responseResult.Message = $"暂停恢复失败 => ex:{ExceptionFormatter.FormatEx(ex)}"; Diagnosis.Log($"暂停恢复失败 => ex:{ExceptionFormatter.FormatEx(ex)}"); } return Response.AsJson(responseResult); } catch (Exception ex) { responseResult.Success = false; responseResult.Code = (int)HttpStatusCode.InternalServerError; responseResult.Message = $"暂停恢复失败 => ex:{ExceptionFormatter.FormatEx(ex)}"; return Response.AsJson(responseResult); } }); #endregion #region 暂停任务/恢复任务 Post("/car/stopOrPauseTask", param => { Response response; var responseResult = new BaseRespose { Success = true, Code = (int)HttpStatusCode.OK, Message = "success" }; try { var jReq = new System.IO.StreamReader(Request.Body).ReadToEnd(); var result = jReq.JsonTo(); var taskCode = result.TaskCode; var cdms = SimpleProject .proj.Missions.OfType() .ToList(); if (cdms.Count == 0) { responseResult.Success = false; responseResult.Code = 500; responseResult.Message = "未找到CDM进程"; return Response.AsText(responseResult.ToJson(), "application/json"); } Delivery deliver = null; foreach (var cdm in cdms) { deliver = cdm.GetDeliveries(includeAborted: true) .FirstOrDefault(p => p.TaskId == taskCode); if (deliver != null) break; } if (deliver == null) { responseResult.Success = false; responseResult.Code = 500; responseResult.Message = $"CDM中不存在任务id[{taskCode}]"; return Response.AsText(responseResult.ToJson(), "application/json"); } deliver.Terminated = result.TaskStatus == "1"; var status = 0; if (deliver.UsingCar != null) { status = (deliver.OnTerminated?.Invoke(deliver, result.TaskStatus)).Result; } if (status != 1) { responseResult.Success = false; responseResult.Code = 500; responseResult.Message = deliver.Terminated ? "暂停任务停止小车失败" : "恢复任务失败"; } response = Response.AsText(responseResult.ToJson(), "application/json"); return response; } catch (JsonSerializationException jsonEx) { responseResult.Code = (int)HttpStatusCode.BadRequest; responseResult.Message = $"JSON解析失败! ex:{jsonEx.Message}"; response = Response.AsText(responseResult.ToJson(), "application/json"); return response; } catch (Exception e) { responseResult.Code = (int)HttpStatusCode.InternalServerError; responseResult.Message = $"任务取消失败! ex:{e.Message}"; response = Response.AsText(responseResult.ToJson(), "application/json"); return response; } }); #endregion #region 设置充电参数 Post("/car/setChargingSettings", param => { Response response; var responseResult = new BaseRespose { Success = true, Code = (int)HttpStatusCode.OK, Message = "success" }; try { var jReq = new System.IO.StreamReader(Request.Body).ReadToEnd(); var chargingSetting = jReq.JsonTo(); // 这里可以根据 ChargingSetting 的属性进行具体的业务逻辑处理 // 例如: if (chargingSetting.CarMinBattery < 0 || chargingSetting.CarMaxBattery > 100) { responseResult.Success = false; responseResult.Code = 400; responseResult.Message = "充电电量范围不合法"; } else { // 假设我们有一个方法来应用这些充电设置 ApplyChargingSettings(chargingSetting); } response = Response.AsText(responseResult.ToJson(), "application/json"); return response; } catch (Exception e) { responseResult.Code = (int)HttpStatusCode.InternalServerError; responseResult.Message = $"设置充电参数失败! ex:{e.Message}"; response = Response.AsText(responseResult.ToJson(), "application/json"); return response; } }); #endregion #region 设置包络参数 Post("/car/setEnvelopeSetting", param => { Response response; var responseResult = new BaseRespose { Success = true, Code = (int)HttpStatusCode.OK, Message = "success" }; try { var jreq = new System.IO.StreamReader(Request.Body).ReadToEnd(); var envelopeSetting = jreq.JsonTo(); // 读取本地 JSON 文件 var simpleFilePath = $"{System.AppDomain.CurrentDomain.BaseDirectory}/simple.json"; var simpleJson = File.ReadAllText(simpleFilePath); var simpleConfig = simpleJson.JsonTo(); var jsonFilePath = ""; if (simpleConfig != null && simpleConfig.Autoload != null) { jsonFilePath = $"{System.AppDomain.CurrentDomain.BaseDirectory}/{simpleConfig.Autoload}"; } else { responseResult.Code = (int)HttpStatusCode.BadRequest; responseResult.Message = "修改文件路径不对"; response = Response.AsText(responseResult.ToJson(), "application/json"); return response; } //var jsonFilePath = $"{System.AppDomain.CurrentDomain.BaseDirectory}/0123.json"; var jsonData = File.ReadAllText(jsonFilePath); var jsonObject = JObject.Parse(jsonData); // 更新或添加属性 // 获取或创建 "conf" 节点 var confNode = jsonObject["conf"]; if (confNode == null || confNode.Type != JTokenType.Object) { confNode = new JObject(); jsonObject["conf"] = confNode; } // 更新 JSON 数据 confNode[envelopeSetting.Name] = envelopeSetting.Value; ; // 将修改后的数据写回 JSON 文件 File.WriteAllText(jsonFilePath, jsonObject.ToString()); response = Response.AsText(responseResult.ToJson(), "application/json"); return response; } catch (Exception e) { responseResult.Code = (int)HttpStatusCode.InternalServerError; responseResult.Message = $"设置包络参数失败! ex:{e.Message}"; response = Response.AsText(responseResult.ToJson(), "application/json"); return response; } }); #endregion #region 设置交管参数 Post("/car/setTrafficControlSetting", param => { var responseResult = new BaseRespose { Success = true, Code = (int)HttpStatusCode.OK, Message = "success" }; try { var jreq = new System.IO.StreamReader(Request.Body).ReadToEnd(); var trafficControlSetting = jreq.JsonTo(); // 读取本地 JSON 文件 var simpleFilePath = $"{System.AppDomain.CurrentDomain.BaseDirectory}/simple.json"; var simpleJson = File.ReadAllText(simpleFilePath); var simpleConfig = simpleJson.JsonTo(); var jsonFilePath = ""; if (simpleConfig != null && simpleConfig.Autoload != null) { jsonFilePath = $"{System.AppDomain.CurrentDomain.BaseDirectory}/{simpleConfig.Autoload}"; } else { responseResult.Code = (int)HttpStatusCode.BadRequest; responseResult.Message = "修改文件路径不对"; } // 读取本地 JSON 文件路径 // jsonFilePath = $"{System.AppDomain.CurrentDomain.BaseDirectory}/0123.json"; if (!File.Exists(jsonFilePath)) { throw new FileNotFoundException( $"JSON file not found at path: {jsonFilePath}" ); } // 读取并解析 JSON 文件 var jsonData = File.ReadAllText(jsonFilePath); var jsonObject = JObject.Parse(jsonData); // 获取或创建 "conf" 节点 var confNode = jsonObject["conf"]; if (confNode == null || confNode.Type != JTokenType.Object) { confNode = new JObject(); jsonObject["conf"] = confNode; } // 更新 JSON 数据 confNode[trafficControlSetting.Name] = trafficControlSetting.Value; // 将修改后的数据写回 JSON 文件 File.WriteAllText(jsonFilePath, jsonObject.ToString()); return Response.AsJson(responseResult); } catch (JsonSerializationException jsonEx) { responseResult.Code = (int)HttpStatusCode.BadRequest; responseResult.Message = $"JSON解析失败! ex:{jsonEx.Message}"; } catch (Exception e) { responseResult.Code = (int)HttpStatusCode.InternalServerError; responseResult.Message = $"设置交管控制参数失败! ex:{e.Message}"; } return Response.AsJson(responseResult); }); #endregion #region 删除配置参数 Post("/car/delControlSetting", param => { Response response; var responseResult = new BaseRespose { Success = true, Code = (int)HttpStatusCode.OK, Message = "success" }; try { var jreq = new System.IO.StreamReader(Request.Body).ReadToEnd(); var keysToRemove = jreq.JsonTo>(); // 读取本地 JSON 文件 var simpleFilePath = $"{System.AppDomain.CurrentDomain.BaseDirectory}/simple.json"; var simpleJson = File.ReadAllText(simpleFilePath); var simpleConfig = simpleJson.JsonTo(); var jsonFilePath = ""; if (simpleConfig != null && simpleConfig.Autoload != null) { jsonFilePath = $"{System.AppDomain.CurrentDomain.BaseDirectory}/{simpleConfig.Autoload}"; } else { responseResult.Code = (int)HttpStatusCode.BadRequest; responseResult.Message = "修改文件路径不对"; } if (!File.Exists(jsonFilePath)) { throw new FileNotFoundException( $"JSON file not found at path: {jsonFilePath}" ); } var jsonData = File.ReadAllText(jsonFilePath); var jsonObject = JObject.Parse(jsonData); var confNode = jsonObject["conf"] as JObject; if (confNode == null) { confNode = new JObject(); jsonObject["conf"] = confNode; } if (confNode != null) { foreach (var key in keysToRemove) { if (confNode.ContainsKey(key)) { confNode.Remove(key); } } // 将修改后的数据写回 JSON 文件 File.WriteAllText(jsonFilePath, jsonObject.ToString()); } response = Response.AsText(responseResult.ToJson(), "application/json"); return response; } catch (JsonSerializationException jsonEx) { responseResult.Code = (int)HttpStatusCode.BadRequest; responseResult.Message = $"JSON解析失败! ex:{jsonEx.Message}"; response = Response.AsText(responseResult.ToJson(), "application/json"); return response; } catch (FileNotFoundException fnfEx) { responseResult.Code = (int)HttpStatusCode.NotFound; responseResult.Message = $"文件未找到! ex:{fnfEx.Message}"; response = Response.AsText(responseResult.ToJson(), "application/json"); return response; } catch (Exception e) { responseResult.Code = (int)HttpStatusCode.InternalServerError; responseResult.Message = $"设置控制参数失败! ex:{e.Message}"; response = Response.AsText(responseResult.ToJson(), "application/json"); return response; } }); #endregion #region 删除路径规划参数设置 Post("/car/delPlanRulesControlSetting", param => { var responseResult = new BaseRespose { Success = true, Code = (int)HttpStatusCode.OK, Message = "success" }; try { var jreq = new System.IO.StreamReader(Request.Body).ReadToEnd(); var planRulesSettings = jreq.JsonTo>(); if (planRulesSettings.Count() < 0) { responseResult.Code = (int)HttpStatusCode.BadRequest; responseResult.Message = "没有需要删除的路径参数"; return Response.AsJson(responseResult); } foreach (var planRules in planRulesSettings) { //根据参数,站点ID 找到该站点,如果站点有这个字段则删除,没有不用管 Site site = SimpleLib.GetSite(int.Parse(planRules.NodeId)); if (site == null) { responseResult.Code = (int)HttpStatusCode.BadRequest; responseResult.Message = "需要修改属性的站点不存在"; return Response.AsJson(responseResult); } if (site.fields.TryGetValue(planRules.Name, out var value)) { site.fields.Remove(planRules.Name); } } return Response.AsJson(responseResult); } catch (JsonSerializationException jsonEx) { responseResult.Code = (int)HttpStatusCode.BadRequest; responseResult.Message = $"JSON解析失败! ex:{jsonEx.Message}"; } catch (Exception e) { responseResult.Code = (int)HttpStatusCode.InternalServerError; responseResult.Message = $"删除路径规划参数失败! ex:{e.Message}"; } return Response.AsJson(responseResult); }); #endregion #region 设置路径规划参数 Post("/car/setPlanRulesControlSetting", param => { var responseResult = new BaseRespose { Success = true, Code = (int)HttpStatusCode.OK, Message = "success" }; try { var jreq = new System.IO.StreamReader(Request.Body).ReadToEnd(); var planRulesSetting = jreq.JsonTo(); if (planRulesSetting == null) { responseResult.Code = (int)HttpStatusCode.BadRequest; responseResult.Message = "需要修改属性的站点不存在"; return Response.AsJson(responseResult); } Site site = SimpleLib.GetSite(int.Parse(planRulesSetting.NodeId)); if (site == null) { responseResult.Code = (int)HttpStatusCode.BadRequest; responseResult.Message = "需要修改属性的站点不存在"; return Response.AsJson(responseResult); } //判断站点属性是否存在,存在则修改,不存在则添加 if (site.fields.TryGetValue(planRulesSetting.Name, out var value)) { site.fields[planRulesSetting.Name] = planRulesSetting.Value; } else { site.fields.Add(planRulesSetting.Name, planRulesSetting.Value); } return Response.AsJson(responseResult); } catch (JsonSerializationException jsonEx) { responseResult.Code = (int)HttpStatusCode.BadRequest; responseResult.Message = $"JSON解析失败! ex:{jsonEx.Message}"; } catch (Exception e) { responseResult.Code = (int)HttpStatusCode.InternalServerError; responseResult.Message = $"设置路径规划参数失败! ex:{e.Message}"; } return Response.AsJson(responseResult); }); #endregion Get("/api/QrMap", _ => { return QrMapJson; }); Post("/api/CreateSite", o => { try { string jreq = new System.IO.StreamReader(Request.Body).ReadToEnd(); QrSite site = JsonConvert.DeserializeObject(jreq); var newSite = new UISite() { x = site.X, y = site.Y }; Console.WriteLine($"x:{site.X},y:{site.Y}"); var d = SimpleLib .GetAllSites() .Select(p => LessMath.dist(site.X, site.Y, p.x, p.y)); if (d.Any() && d.Min() < 3) { Console.WriteLine($"重复设置站点?"); return JsonConvert.SerializeObject( new { data = new { success = false, message = "设置站点失败,重复设置站点?" } } ); } SimpleLib.SetSite(newSite); var ss = SimpleLib .GetAllSites() .OrderBy(p => LessMath.dist(site.X, site.Y, p.x, p.y)) .First(); ss.fields["th"] = (site.Th).ToString(); ss.fields["tag"] = (site.Tag).ToString(); return JsonConvert.SerializeObject( new { data = new { success = true, message = "设置站点成功" } } ); } catch (Exception e) { Console.WriteLine(e); return JsonConvert.SerializeObject( new { data = new { success = false, message = "设置站点失败" + e } } ); } }); #region OTA获取车辆列表 Get("/api/agv/list", _ => { List cars = new List(); foreach (var car in SimpleLib.GetAllCars().OfType()) { var station = SimpleLib .GetAllSites() .ToList() .FirstOrDefault(site => site.id == car.status.holdingLocks.FirstOrDefault() && site.name.Contains("TaskStation") ); var statusV = car.status.enums.ContainsKey("actualV") ? float.Parse(car.status.enums["actualV"]) : 0; car.status.enums.TryGetValue("electricCurrent", out var electricCurrent); var agv_status = ""; if (statusV == 0) { agv_status = "停止"; } else if (statusV > 0) { agv_status = "运行中"; } if ( statusV == 0 && electricCurrent != null && electricCurrent != "" && float.Parse(electricCurrent) * 0.01f > 0 ) { agv_status = "充电中"; } cars.Add( new { agv_id = car.id, agv_name = car.name, agv_ip = car.address, //agv_status = car.tags["carStatu"], //agv_status = car.tags.Contains("carStatu") ? car.tags["carStatu"] : "OFFLINE", energy = car.status.enums.ContainsKey("soc") ? float.Parse(car.status.enums["soc"]) : -1, /* x = tPos.Item1, y = tPos.Item2, th = tPos.Item3,*/ x = car.x, y = car.y, th = car.th, speed = Commons.CarValue(car, "sendSpeed"), fault = car.status.enums.ContainsKey("mAlarm") ? car.status.enums["mAlarm"] : "正常", siteId = car.GetLastSite(), // materialinfo =new string[3] { "materialinfo1", "materialinfo2", "materialinfo3" } stationInfo = station != null ? new { stationID = station.id, staionName = station.name } : null, agv_status = agv_status } ); } return JsonConvert.SerializeObject( new { code = 200, message = "ok", data = cars } ); }); #endregion #region 第三方交管 Post("api/agv/traffic", _ => { try { string jreq = new System.IO.StreamReader(Request.Body).ReadToEnd(); var Traffic = JsonConvert.DeserializeObject(jreq); var Area = TrafficInterlockMission.TrafficAreaList.Find(t => t.AreaName == Traffic.AreaName); if (Traffic != null && Area != null) { if (Traffic.IsOccupy && !Area.IsOccupy) { lock (TrafficInterlockMission.TrafficAreaList) { Area.IsOccupy = true; Area.ControllerName = Traffic.ControllerName; } return JsonConvert.SerializeObject(new { data = new { success = true, message = "申请管制区成功" } }); } else if (!Traffic.IsOccupy && Area.ControllerName == Traffic.ControllerName) { lock (TrafficInterlockMission.TrafficAreaList) { Area.IsOccupy = false; Area.ControllerName = string.Empty; } return JsonConvert.SerializeObject(new { data = new { success = true, message = "释放管制区成功" } }); } } return JsonConvert.SerializeObject(new { data = new { success = false, message = "失败" } }); } catch (Exception e) { Console.WriteLine(e); return JsonConvert.SerializeObject(new { data = new { success = false, message = "设置交管失败" + e } }); } }); #endregion } private void ToggleCarReleaseStatus(ClumsyCar car, string currentStatus) { int targetStatus = (currentStatus == "1") ? 0 : 1; string command = $"pilot.ISRelease={targetStatus};"; while (Commons.GetCarStatus(car, "ISRelease") == currentStatus) { car.ImmediateCommand(command); Thread.Sleep(100); } } private void ApplyChargingSettings(ChargingSetting settings) { Console.WriteLine("开始处理充电配置参数的逻辑"); // 这里是应用充电设置的具体逻辑 // 例如,更新设备的充电参数等 var chargeMission = SimpleProject .proj.Missions.OfType() .FirstOrDefault(); if (chargeMission == null) return; Commons.AddOrUpdateMissionField( chargeMission, "mustChargeSoc", settings.CarMinBattery.ToString() ); Commons.AddOrUpdateMissionField( chargeMission, "fullChargeSoc", settings.CarMaxBattery.ToString() ); Commons.AddOrUpdateMissionField( chargeMission, "taskAvailableSoc", settings.TaskAvailableBattery.ToString() ); Commons.AddOrUpdateMissionField( chargeMission, "mustChargeSeconds", settings.CarIdleSecond.ToString() ); Commons.AddOrUpdateMissionField( chargeMission, "idleChargeSoc", settings.CarIdleChargeBattery.ToString() ); } private (bool, int, string, CarStateInfo) CreateTransportTask( TaskRequest req, bool isCreate ) { var transportMission = SimpleProject.proj.Missions.OfType().First(); //校验任务重复 if ( transportMission .GetDeliveries(true) .FindAll(p => ((TransportDelivery)p).TaskId == req.TaskCode) .Count > 0 ) { return ( false, (int)HttpStatusCode.InternalServerError, $"taskCode:{req.TaskCode}已经存在", GetCarStateInfo(req.CarCode) ); } //判定指定车辆是否存在 Car car = null; if (!string.IsNullOrWhiteSpace(req.CarCode)) { car = SimpleLib .GetAllCars() .OfType() .FirstOrDefault(e => e.id.ToString() == req.CarCode); if (car == null || car.status.holdingLocks.FirstOrDefault() == -1) { return ( false, (int)HttpStatusCode.InternalServerError, $"传入车辆code{req.CarCode}不存在或没有初始化", new CarStateInfo() ); } } //判断node的个数 switch (req.Nodes.Count) { case 2: Diagnosis.Log( $"receiveTask:[TaskId ={req.TaskCode},carId = {req.CarCode},src = {req.Nodes[0].Code},dst = {req.Nodes[1].Code}", "task", true ); break; case 1: if (car == null) return ( false, (int)HttpStatusCode.InternalServerError, $"创建单点任务时,未指定车辆", new CarStateInfo() ); Diagnosis.Log( $"receiveTask:[TaskId ={req.TaskCode},carId = {req.CarCode},dst = {req.Nodes[0].Code}", "task", true ); break; default: return ( false, (int)HttpStatusCode.InternalServerError, $"创建任务时,传入站点个数不对", new CarStateInfo() ); } //站点存在判断,默认都是双点任务 var siteCodes = req.Nodes.Select(e => e.Code).ToArray(); if (!Commons.IsSceneSiteByCode(siteCodes)) { return ( false, (int)HttpStatusCode.InternalServerError, $"不存在设定站点 => taskCode:{req.TaskCode}", new CarStateInfo() ); } var src = req.Nodes.Count == 2 ? SimpleLib.GetSite(req.Nodes[0].Code) : SimpleLib.GetSite(car.status.holdingLocks.First()); //如果是单点任务,默认当前未知为起点 var dst = req.Nodes.Count == 2 ? SimpleLib.GetSite(req.Nodes[1].Code) : SimpleLib.GetSite(req.Nodes[0].Code); var d = new TransportDelivery { Src = src.id, Dst = dst.id, CarType = req.CarType, TaskId = req.TaskCode, TaskType = req.TaskType, Material = req.Material, Priority = req.priority, MaterialLength = req.ContainerSize != null ? (float)req.ContainerSize.Length : -1, MaterialWidth = req.ContainerSize != null ? (float)req.ContainerSize.Width : -1, }; if (car != null) { d.UsingCar = car; } d.FetchPlanInfo["MaterialLength"] = d.MaterialLength + ""; d.FetchPlanInfo["MaterialWidth"] = d.MaterialWidth + ""; if (d.Src == d.Dst) { d.FetchPlanInfo["action"] = "/"; d.PutPlanInfo["action"] = "/"; } // 初始化回调配置,确保任务在各阶段触发统一回调 d.ReportOnStarted = true; d.ReportOnFetched = true; d.ReportOnPut = true; d.ReportOnFinished = true; d.ReportOnFailed = true; d.ReportOnTerminated = true; // 根据 ReportOn* 写入 key 列表 if (d.ReportOnStarted) d.OnStartCallbackKeys.Add(TransportDeliveryCallbacks.KeyOnStarted); if (d.ReportOnFetched) d.DoneFetchCallbackKeys.Add(TransportDeliveryCallbacks.KeyOnFetched); if (d.ReportOnPut) d.DonePutCallbackKeys.Add(TransportDeliveryCallbacks.KeyOnPut); if (d.ReportOnFinished) d.DoneMissionCallbackKeys.Add(TransportDeliveryCallbacks.KeyOnFinished); if (d.ReportOnFailed) d.FailedCallbackKeys.Add(TransportDeliveryCallbacks.KeyOnFailed); if (d.ReportOnTerminated) d.OnTerminatedCallbackKeys.Add(TransportDeliveryCallbacks.KeyOnTerminated); // 统一通过 Attacher 挂载回调,便于持久化与恢复 DeliveryCallbackAttacher.AttachAll(d); transportMission.Enqueue(d); Diagnosis.Log($"Add task =>{d.ToJson()},req:{req.ToJson()}"); return (true, (int)HttpStatusCode.OK, "success", GetCarStateInfo(req.CarCode)); } private class GetCar { public string CarCode; } public CarStateInfo GetCarStateInfo(string carCode) { try { var transportMission = SimpleProject .proj.Missions.OfType() .FirstOrDefault(); if (string.IsNullOrEmpty(carCode)) return new CarStateInfo(); var singleCar = SimpleLib .GetAllCars() .OfType() .FirstOrDefault(e => e.id.ToString() == carCode && e.GetLastSite() != -1); if (singleCar == null) return new CarStateInfo(); var runningTask = (TransportDelivery) transportMission .GetDeliveries() .FindAll(p => ( p.GetStatus() == DeliveryStatus.Fetching || p.GetStatus() == DeliveryStatus.Putting ) && p.UsingCar.id == singleCar.id ) .FirstOrDefault(); //获取当前车辆正在运行的任务 var finishedTask = (TransportDelivery) transportMission .GetDeliveries(true) .FindAll(p => p.GetStatus() == DeliveryStatus.Finished && p.UsingCar.id == singleCar.id ) .OrderByDescending(e => e.FinishTime) .FirstOrDefault(); //获取最近完成的任务 var waitingTask = (TransportDelivery) transportMission .GetDeliveries() .FindAll(p => p.GetStatus() == DeliveryStatus.Waiting && p.UsingCar != null && p.UsingCar.id == singleCar.id ) .OrderBy(e => e.CreateTime) .FirstOrDefault(); var taskList = new List(); var actions = new List(); //string carState = SwitchCarState(singleCar); var isOnline = false; string carState = SwitchCarState(singleCar, ref isOnline); //var resNodes = new List(); if (finishedTask != null) { taskList.Add( new TaskInfo() { Code = finishedTask.TaskId, State = "Completed", Nodes = new List() { new() { //Code = Commons.GetSiteCodeById(finishedTask.src) Code = finishedTask.Src.ToString() }, new() { //Code = Commons.GetSiteCodeById(finishedTask.dst) Code = finishedTask.Dst.ToString() } } } ); } if (runningTask != null) { taskList.Add( new TaskInfo() { Code = runningTask.TaskId, State = "Running", Nodes = new List() { new() { //Code = Commons.GetSiteCodeById(runningTask.src) Code = runningTask.Src.ToString() }, new() { //Code = Commons.GetSiteCodeById(runningTask.dst) Code = runningTask.Dst.ToString() } } } ); } if (waitingTask != null) { taskList.Add( new TaskInfo() { Code = waitingTask.TaskId, State = "Distributed", Nodes = new List() { new() { //Code = Commons.GetSiteCodeById(waittingTask.src) Code = waitingTask.Src.ToString() }, new() { //Code = Commons.GetSiteCodeById(waittingTask.dst) Code = waitingTask.Dst.ToString() } } } ); } var carId = singleCar.id; var alarmsList = new List(); if ( singleCar.status.enums.ContainsKey("AlarmInfo") && !string.IsNullOrWhiteSpace(singleCar.status.enums["AlarmInfo"]) ) { string alarmMessage = singleCar.status.enums["AlarmInfo"].TrimEnd(','); string[] alarms = alarmMessage.Split(','); foreach (string alarm in alarms) { string[] info = alarm.Split('.'); if (info.Length == 2) { alarmsList.Add(new CarAlarm() { Code = info[0], Name = info[1] }); } } } var currentSite = SimpleLib.GetSite(singleCar.status.holdingLocks.FirstOrDefault()); // HashSet> blockedBy = singleCar.status.blockedBy; var blockedBy = singleCar.status.blockedBy.ToHashSet(); List blockedByItems = new List(); //var BlockedBy = string.Empty; for (int i = 0; i < blockedBy.Count; i++) { //BlockedBy += $"({blockedBy.ElementAt(i).Item1 + "," + blockedBy.ElementAt(i).Item2});"; blockedByItems.Add( new BlockedByItem { CarCode = blockedBy.ElementAt(i).Item1.ToString(), Type = blockedBy.ElementAt(i).Item2 } ); } //BlockedBy = BlockedBy.TrimEnd(';'); return new CarStateInfo() { Code = carCode, Name = singleCar.name, CurrState = carState, IsOnline = isOnline, Battery = singleCar.status.enums.ContainsKey("Soc") ? int.Parse(singleCar.status.enums["Soc"]) : 100, Voltage = singleCar.status.enums.ContainsKey("Voltage") ? double.Parse(singleCar.status.enums["Voltage"]) : 0, ElectricCurrent = singleCar.status.enums.ContainsKey("ElectricCurrent") ? double.Parse(singleCar.status.enums["ElectricCurrent"]) : 0, X = singleCar.x, Y = singleCar.y, Theta = singleCar.th, Speed = singleCar.status.enums.ContainsKey("ActualLeftWheelVelocity") ? double.Parse( SimpleLib.GetCar(carId).status.enums["ActualLeftWheelVelocity"] ) : singleCar.speed, //CurrNodeCode = currentSite.fields.ContainsKey("code") ? currentSite.fields["code"] : currentSite.id.ToString(), Load = singleCar.status.enums.TryGetValue("loadStatus", out var statusEnum) ? int.Parse(statusEnum) : 0, CurrNodeCode = currentSite.id.ToString(), StartNodeCode = "0", EndNodeCode = "0", StartEdgeCode = "0", EndEdgeCode = "0", HoldingLocks = singleCar.status.holdingLocks.ToList(), PendingLocks = singleCar.status.pendingLocks.ToList(), BlockedBy = blockedByItems, // BlockingTime = singleCar.status.blockingTime.ToString("yyyy-MM-dd HH:mm:ss"),//没有了 TrafficMessage = singleCar.status.TCStat.ToString(), AquiringLock = singleCar.status.aquiringLock, Tasks = taskList, Alarms = alarmsList, StopAccept = singleCar.fields.ContainsKey("StopAccept"), Actions = new List() }; } catch (Exception ex) { Console.WriteLine($"getCarStateInfo => ex: {ex}"); return new CarStateInfo(); } } public string SwitchCarState(Car car, ref bool isOnline) { string state = "Stopping"; if (car.GetLastSite() == -1) { isOnline = false; return state; } if (Commons.GetVehicleStatus(car)!= VehicleStatus.Offline) { if (Commons.GetVehicleStatus(car) is VehicleStatus.Normal or VehicleStatus.NeedInit) state = "Stopping"; //Idle var lp = car.status.programs.latest; if (lp != null) { if ( lp.status.state >= SimpleCore.Compiler.CarProgram.StatusEnum.Programming && lp.status.state <= SimpleCore.Compiler.CarProgram.StatusEnum.Running ) { state = "Running"; //Executing } } //存在报警信息 if (car.status.enums.ContainsKey("AlarmInfo")) { if (!string.IsNullOrEmpty(car.status.enums["AlarmInfo"])) { state = "Faulting"; //Malfunction } } if (car.tags.Contains("charging")) state = "Charging"; isOnline = true; } else { isOnline = false; } return state; } public string GetDeliveryStatus(TransportDelivery delivery) { ChainedDeliveryMission.DeliveryStatus status = (ChainedDeliveryMission.DeliveryStatus)delivery.GetStatus(); if (status == ChainedDeliveryMission.DeliveryStatus.Error) { return "Faulted"; } else if (status == ChainedDeliveryMission.DeliveryStatus.Canceled) { return "Canceled"; } else if (status == ChainedDeliveryMission.DeliveryStatus.Terminated) { return "Terminated"; } else if (status == ChainedDeliveryMission.DeliveryStatus.Finished) { return "Completed"; } else if (status == ChainedDeliveryMission.DeliveryStatus.Putting) { return "Putting"; } else if (status == ChainedDeliveryMission.DeliveryStatus.Fetching) { return "Fetching"; } else if (status == ChainedDeliveryMission.DeliveryStatus.Waiting) { return "Created"; } else { return "UnKnown"; } } } internal class MethodInfoDetails { public string MethodName; public string ButtonName; public string ButtonDescription; public string ParamsHint; public List ParamsList = new(); } internal class ParamInfo { public ParamInfo(string name, Type type) { Name = name; Type = type; } public string Name; public Type Type; } public class HttpPostData { private HttpClient hc = new HttpClient() { Timeout = TimeSpan.FromSeconds(3) }; public void UploadSignal(string content) { try { //var json = JsonConvert.SerializeObject(new { }); //var data = new StringContent(json, Encoding.UTF8, "application/json"); StringContent stringContent = new StringContent( JsonConvert.SerializeObject(content) ); //ApiController._logger.Info($"DeviceNotifyPost:[{stringContent.ToString()}]"); stringContent.Headers.ContentType = new MediaTypeHeaderValue("application/json"); //hc.Timeout = TimeSpan.FromSeconds(30); var task = hc.PostAsync( "http://localhost:20101/api/v1/SecuritySignal/GetSecuritySignal", stringContent ); var responseStr = task.Result.Content.ReadAsStringAsync().Result; ResultMsg response = JsonConvert.DeserializeObject(responseStr); } catch (Exception e) { Diagnosis.Log("DeviceNotifyPost" + ExceptionFormatter.FormatEx(e), $"error", true); } } public void UploadListNode(HashSet contentList) { try { /* if (contentList == null || contentList.Count == 0) { throw new ArgumentException("Content list cannot be null or empty."); }*/ // 序列化List为JSON字符串 var jsonContent = JsonConvert.SerializeObject(contentList); StringContent stringContent = new StringContent( jsonContent, Encoding.UTF8, "application/json" ); // 设置内容类型为application/json stringContent.Headers.ContentType = new MediaTypeHeaderValue("application/json"); // 发送POST请求 var task = hc.PostAsync( "http://localhost:20101/api/v1/Node/NodeListDisable", stringContent ); // 读取响应内容 var responseStr = task.Result.Content.ReadAsStringAsync().Result; // 反序列化响应 ResultMsg response = JsonConvert.DeserializeObject(responseStr); } catch (Exception e) { // 记录异常 Diagnosis.Log("DeviceNotifyPost" + ExceptionFormatter.FormatEx(e), "error", true); } } } public class QrSite { public float X; public float Y; public float Th; public int Tag; } class ResultMsg { public string code { get; set; } public string reqCode { get; set; } public string msg { get; set; } public int statu { get; set; } } public class TrafficRequestModel { /// /// 区域名称 /// public string AreaName { get; set; } /// /// 控制单位 /// public string ControllerName { get; set; } /// /// 申请占用/释放区域,true为占用,false为释放 /// public bool IsOccupy { get; set; } } }