using System; using System.Collections.Generic; using System.Data; using System.Data.SqlClient; using System.Net.Http; using System.Text; using System.Web; using System.Web.Http; using MesServerWork; using MisDataSaveDate.Helper; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using static MisDataSaveDate.Helper.ApiLogHelper; namespace MisDataSaveDate { /// /// TCS插拔任务业务处理。PLC自动任务与Web人工任务共用此入口。 /// public class TCSAGV_BusinessLogic { private const string TaskTypeCombined = "nav_action_program"; private const string TaskTypeProgram = "program"; public static TCSAGV_CallResult DispatchStationTask(string stationCode, string actionType, string portCombination, string source, string operatorCode, string parentExternalCode, bool notifyPlc) { TCSAGV_Config config = ReadConfig(true); TCSAGV_PointConfig point = ReadPointConfig(stationCode); string normalizedAction = NormalizeAction(actionType); string productModel = ""; string combination; if (string.IsNullOrWhiteSpace(portCombination)) { productModel = ReadCurrentProductModel(point.StationCode); combination = ReadModelActionCombination(point.LineCode, productModel, normalizedAction); } else { combination = NormalizePortCombination(portCombination); } JArray parameters = new JArray { new JObject { { "workStation", point.WorkStation } } }; if (combination.Contains("XSD")) { parameters.Add(new JObject { { "programName", normalizedAction == "插" ? point.InsertXsdProgram : point.PullXsdProgram } }); } if (combination.Contains("XSE")) { parameters.Add(new JObject { { "programName", normalizedAction == "插" ? point.InsertXseProgram : point.PullXseProgram } }); } string externalCode = CreateExternalCode(normalizedAction == "插" ? "PLUG" : "PULL"); return CreateAndSendTask(config, point, externalCode, parentExternalCode, normalizedAction, "", productModel, combination, TaskTypeCombined, parameters.ToString(Formatting.None), source, operatorCode, notifyPlc); } public static TCSAGV_CallResult DispatchSingleStep(string stationCode, string programName, string operatorCode, string parentExternalCode) { TCSAGV_Config config = ReadConfig(true); TCSAGV_PointConfig point = ReadPointConfig(stationCode); EnsureRobotAtPoint(config, point); string normalizedProgram = (programName ?? "").Trim(); if (!IsConfiguredProgram(point, normalizedProgram)) { throw new Exception("所选programName不属于该工位配置"); } string action = normalizedProgram.Contains("拔") ? "拔-单步" : "插-单步"; JArray parameters = new JArray { new JObject { { "programName", normalizedProgram } } }; return CreateAndSendTask(config, point, CreateExternalCode("STEP"), parentExternalCode, action, normalizedProgram, "", normalizedProgram.Contains("XSD") ? "XSD" : "XSE", TaskTypeProgram, parameters.ToString(Formatting.None), "MANUAL", operatorCode, false); } public static TCSAGV_CallResult RetryTask(string externalCode, string operatorCode) { DataRow original = ReadTask(externalCode); if (!IsFailureStatus(ToInt(original["任务状态"], -1))) { throw new Exception("仅任务错误状态允许整单重发"); } if (ToBool(original["处理状态"])) { throw new Exception("失败任务已处理,不能再次重发"); } if (!ToBool(original["已复位"])) { throw new Exception("请先复位移动机器人再重发任务"); } TCSAGV_Config config = ReadConfig(true); TCSAGV_PointConfig point = ReadPointConfig(Convert.ToString(original["MES工位号"])); string action = Convert.ToString(original["任务动作"]); string programName = Convert.ToString(original["ProgramName"]); string productModel = Convert.ToString(original["产品型号"]); string combination = Convert.ToString(original["动作组合"]); string taskType = Convert.ToString(original["任务类型"]); string param = Convert.ToString(original["参数"]); return CreateAndSendTask(config, point, CreateExternalCode("RETRY"), externalCode, action, programName, productModel, combination, taskType, param, "RETRY", operatorCode, false); } public static TCSAGV_CallResult CancelTask(string externalCode, string operatorCode) { if (string.IsNullOrWhiteSpace(externalCode)) { throw new Exception("外部任务编号不能为空"); } TCSAGV_Config config = ReadConfig(true); TCSAGV_CallResult result = GetFromTcs(config, "cancelExternalBusinessTask", "/api/cancelExternalBusinessTask", new Dictionary { { "externalCode", externalCode } }); if (result.Success) { SetLocalTaskStatus(externalCode, 3, "已取消", ""); } SaveOperation(externalCode, "", "取消任务", operatorCode, result.RequestText, result.ResponseText, result.Success, result.Message); return result; } public static TCSAGV_CallResult StopPendingTasks(string externalCode, string operatorCode) { TCSAGV_Config config = ReadConfig(true); TCSAGV_CallResult result = GetFromTcs(config, "stopExternalBusinessTask", "/api/stopExternalBusinessTask", null); if (result.Success && !string.IsNullOrWhiteSpace(externalCode)) { DataRow task = ReadTask(externalCode); if (ToInt(task["任务状态"], -1) == 0) { SetLocalTaskStatus(externalCode, 3, "已停止", "停止全部未开始任务"); } } SaveOperation(externalCode, "", "停止未开始任务", operatorCode, result.RequestText, result.ResponseText, result.Success, result.Message); return result; } public static TCSAGV_CallResult ResetRobot(string externalCode, string ip, string operatorCode) { TCSAGV_Config config = ReadConfig(true); string robotIp = string.IsNullOrWhiteSpace(ip) ? config.McrIp : ip.Trim(); if (string.IsNullOrWhiteSpace(robotIp)) { throw new Exception("未配置AGV IP"); } TCSAGV_CallResult result = GetFromTcs(config, "reset", "/api/reset", new Dictionary { { "ip", robotIp } }); if (result.Success && !string.IsNullOrWhiteSpace(externalCode)) { try { SqlParameter[] parameters = { new SqlParameter("@外部任务编号", externalCode.Trim()) }; EnsureBusinessSuccess(ExecuteProcedure("XD_TCS_AGV_任务_复位标记", parameters)); } catch (Exception ex) { SaveOperation(externalCode, "", "移动机器人复位", operatorCode, result.RequestText, result.ResponseText, false, "TCS复位成功,但MES复位标记失败:" + ex.Message); throw; } } SaveOperation(externalCode, "", "移动机器人复位", operatorCode, result.RequestText, result.ResponseText, result.Success, result.Message); return result; } public static TCSAGV_CallResult StopNow(string externalCode, string ip, string operatorCode) { TCSAGV_Config config = ReadConfig(true); string robotIp = string.IsNullOrWhiteSpace(ip) ? config.McrIp : ip.Trim(); if (string.IsNullOrWhiteSpace(robotIp)) { throw new Exception("未配置AGV IP"); } TCSAGV_CallResult result = GetFromTcs(config, "stopNow", "/api/stopNow", new Dictionary { { "ip", robotIp } }); if (result.Success && !string.IsNullOrWhiteSpace(externalCode)) { SetLocalTaskStatus(externalCode, 4, "任务错误", "人工紧急停止"); } SaveOperation(externalCode, "", "紧急停止", operatorCode, result.RequestText, result.ResponseText, result.Success, result.Message); return result; } public static TCSAGV_CallResult SyncTaskStatus(string externalCode, string operatorCode) { TCSAGV_Config config = ReadConfig(true); TCSAGV_CallResult result = GetFromTcs(config, "getExternalBusinessTaskStatus", "/api/getExternalBusinessTaskStatus", new Dictionary { { "externalCode", externalCode } }); if (result.Success) { JObject task = FirstResultObject(result.Response); if (task != null && task["status"] != null) { int status = ToInt(task["status"], -1); if (status >= 0 && status <= 5) { UpdateTaskStatus(externalCode, status, Convert.ToString(task["ip"] ?? ""), Convert.ToString(task["type"] ?? ""), Convert.ToString(task["beginTime"] ?? ""), Convert.ToString(task["endTime"] ?? ""), Convert.ToString(task["remark"] ?? "")); } } } SaveOperation(externalCode, "", "同步任务状态", operatorCode, result.RequestText, result.ResponseText, result.Success, result.Message); return result; } public static TCSAGV_CallResult QueryRobotInfo() { TCSAGV_Config config = ReadConfig(true); return QueryRobotInfo(config); } private static TCSAGV_CallResult QueryRobotInfo(TCSAGV_Config config) { Dictionary query = new Dictionary { { "mcrGroup", config.McrGroup } }; if (!string.IsNullOrWhiteSpace(config.McrIp)) { query.Add("mcrIp", config.McrIp); } return GetFromTcs(config, "getMcrInfo", "/api/getMcrInfo", query); } public static void HandleFailure(string externalCode, string operatorCode, string remark, bool notifyPlc) { DataRow task = ReadTask(externalCode); if (!IsFailureStatus(ToInt(task["任务状态"], -1))) { throw new Exception("仅任务错误状态允许人工处理"); } if (!ToBool(task["已复位"])) { throw new Exception("请先复位移动机器人再完成人工处理"); } if (string.IsNullOrWhiteSpace(remark)) { throw new Exception("处理说明不能为空"); } if (notifyPlc) { WritePlcCompletion(task); } SqlParameter[] parameters = { new SqlParameter("@外部任务编号", externalCode ?? ""), new SqlParameter("@处理人", operatorCode ?? ""), new SqlParameter("@处理说明", remark ?? "") }; EnsureBusinessSuccess(ExecuteProcedure("XD_TCS_AGV_任务_人工处理", parameters)); SaveOperation(externalCode, Convert.ToString(task["MES工位号"]), "失败任务人工处理", operatorCode, "", "", true, remark); } public static string ProcessTaskStatus(string url, [FromBody] JObject jobj) { string requestText = jobj == null ? "" : jobj.ToString(Formatting.None); SaveLog_Interface_Request(url, 2, requestText, out int aid); TCSAGV_ApiResponse response; try { if (jobj == null) { throw new Exception("请求参数不能为空"); } TCSAGV_TaskStatusPushRequest request = jobj.ToObject(); if (request == null || string.IsNullOrWhiteSpace(request.externalCode)) { throw new Exception("外部任务编号不能为空"); } int status = ToInt(request.status, -1); if (status < 0 || status > 5) { throw new Exception("任务状态必须为0至5"); } UpdateTaskStatus(request.externalCode, status, request.ip, request.type, request.beginTime, request.endTime, request.remark); response = TCSAGV_ApiResponse.Ok("接收成功", "接收成功"); } catch (Exception ex) { response = TCSAGV_ApiResponse.Fail(ex.Message); SaveMesLog("TCS任务状态推送", ex.Message, MesLogType.ERROR); } string responseText = JsonConvert.SerializeObject(response); SaveLog_Interface_Response(responseText, aid); return responseText; } public static string ProcessPositionPush(string url, [FromBody] JObject jobj) { string requestText = jobj == null ? "" : jobj.ToString(Formatting.None); SaveLog_Interface_Request(url, 2, requestText, out int aid); TCSAGV_ApiResponse response; try { if (jobj == null) { throw new Exception("请求参数不能为空"); } TCSAGV_PositionPushRequest request = jobj.ToObject(); if (request == null || string.IsNullOrWhiteSpace(request.ip)) { throw new Exception("机器人IP不能为空"); } SqlParameter[] parameters = { new SqlParameter("@机器人IP", request.ip ?? ""), new SqlParameter("@当前站点", request.currentPos ?? ""), new SqlParameter("@目标站点", request.objectivePos ?? ""), new SqlParameter("@原始报文", requestText) }; ExecuteProcedure("XD_TCS_AGV_位置_更新", parameters); response = TCSAGV_ApiResponse.Ok("接收成功", "接收成功"); } catch (Exception ex) { response = TCSAGV_ApiResponse.Fail(ex.Message); SaveMesLog("TCS位置推送", ex.Message, MesLogType.ERROR); } string responseText = JsonConvert.SerializeObject(response); SaveLog_Interface_Response(responseText, aid); return responseText; } private static TCSAGV_CallResult CreateAndSendTask(TCSAGV_Config config, TCSAGV_PointConfig point, string externalCode, string parentExternalCode, string action, string programName, string productModel, string combination, string taskType, string param, string source, string operatorCode, bool notifyPlc) { TCSAGV_TaskRequest request = new TCSAGV_TaskRequest { externalCode = externalCode, type = taskType, priority = config.DefaultPriority, param = param, mcrgroup = config.McrGroup, mcrip = config.McrIp, operatoruserid = config.OperatorUserId }; SqlParameter[] createParameters = { new SqlParameter("@外部任务编号", externalCode), new SqlParameter("@原任务编号", parentExternalCode ?? ""), new SqlParameter("@MES工位号", point.StationCode), new SqlParameter("@任务动作", action), new SqlParameter("@产品型号", productModel ?? ""), new SqlParameter("@动作组合", combination ?? ""), new SqlParameter("@ProgramName", programName ?? ""), new SqlParameter("@任务类型", taskType), new SqlParameter("@参数", param), new SqlParameter("@优先级", config.DefaultPriority), new SqlParameter("@机器人组", config.McrGroup ?? ""), new SqlParameter("@机器人IP", config.McrIp ?? ""), new SqlParameter("@来源", source ?? ""), new SqlParameter("@操作人", operatorCode ?? ""), new SqlParameter("@是否通知PLC", notifyPlc) }; EnsureBusinessSuccess(ExecuteProcedure("XD_TCS_AGV_任务_创建", createParameters)); TCSAGV_CallResult result; try { result = PostToTcs(config, "createExternalBusinessTask", "/api/createExternalBusinessTask", request); } catch (Exception ex) { UpdateDispatchResult(externalCode, JsonConvert.SerializeObject(request), "", false, ex.Message); SaveOperation(externalCode, point.StationCode, "下发" + action + "任务", operatorCode, JsonConvert.SerializeObject(request), "", false, ex.Message); throw; } UpdateDispatchResult(externalCode, result.RequestText, result.ResponseText, result.Success, result.Success ? "" : result.Message); SaveOperation(externalCode, point.StationCode, "下发" + action + "任务", operatorCode, result.RequestText, result.ResponseText, result.Success, result.Message); result.ExternalCode = externalCode; return result; } private static void UpdateTaskStatus(string externalCode, int status, string ip, string type, string beginTime, string endTime, string remark) { SqlParameter[] parameters = { new SqlParameter("@外部任务编号", externalCode ?? ""), new SqlParameter("@任务状态", status), new SqlParameter("@机器人IP", ip ?? ""), new SqlParameter("@任务类型", type ?? ""), new SqlParameter("@开始时间", beginTime ?? ""), new SqlParameter("@结束时间", endTime ?? ""), new SqlParameter("@备注", remark ?? "") }; DataTable result = ExecuteProcedure("XD_TCS_AGV_任务_状态更新", parameters); EnsureBusinessSuccess(result); if (status == 2 && result.Rows.Count > 0 && ToBool(result.Rows[0]["是否通知PLC"]) && !ToBool(result.Rows[0]["PLC已通知"])) { DataRow task = ReadTask(externalCode); WritePlcCompletion(task); SqlParameter[] notified = { new SqlParameter("@外部任务编号", externalCode) }; ExecuteProcedure("XD_TCS_AGV_任务_PLC通知完成", notified); } } private static bool IsFailureStatus(int status) { return status == 4 || status == 5; } private static void WritePlcCompletion(DataRow task) { string stationCode = Convert.ToString(task["MES工位号"]); string action = Convert.ToString(task["任务动作"]); int tagCode = action.StartsWith("插", StringComparison.Ordinal) ? 400 : action.StartsWith("拔", StringComparison.Ordinal) ? 401 : 0; if (tagCode == 0) { throw new Exception("任务动作无法确定PLC完成码"); } if (!MIS_BASE.WritePLC(tagCode, stationCode, true)) { throw new Exception("任务完成但PLC完成信号写入失败"); } SaveMesLog(stationCode, "TCS任务完成,已写入PLC功能码" + tagCode, MesLogType.INFO); } private static TCSAGV_Config ReadConfig(bool requireEnabled) { DataTable table = ExecuteProcedure("XD_TCS_AGV_配置_获取", new SqlParameter[0]); if (table.Rows.Count == 0) { throw new Exception("未配置TCS参数"); } DataRow row = table.Rows[0]; TCSAGV_Config config = new TCSAGV_Config { BaseUrl = Convert.ToString(row["TCS_BaseUrl"]), Enabled = Convert.ToString(row["TCS_Enabled"]) == "1", McrGroup = Convert.ToString(row["McrGroup"]), McrIp = Convert.ToString(row["McrIp"]), OperatorUserId = Convert.ToString(row["TCS_OperatorUserId"]), DefaultPriority = ToInt(row["DefaultPriority"], 1), HttpTimeoutSeconds = ToInt(row["HttpTimeoutSeconds"], 15) }; if (string.IsNullOrWhiteSpace(config.BaseUrl)) { throw new Exception("未配置TCS服务地址"); } if (string.IsNullOrWhiteSpace(config.OperatorUserId)) { throw new Exception("未配置TCS任务状态推送标识"); } if (requireEnabled && !config.Enabled) { throw new Exception("TCS真实下发开关未启用,请先确认现场地址和AGV IP"); } return config; } private static TCSAGV_PointConfig ReadPointConfig(string stationCode) { if (string.IsNullOrWhiteSpace(stationCode)) { throw new Exception("MES工位号不能为空"); } SqlParameter[] parameters = { new SqlParameter("@MES工位号", stationCode.Trim()) }; DataTable table = ExecuteProcedure("XD_TCS_AGV_点位配置_查询", parameters); if (table.Rows.Count == 0) { throw new Exception("MES工位未配置TCS站点和programName"); } DataRow row = table.Rows[0]; return new TCSAGV_PointConfig { LineCode = Convert.ToString(row["产线代码"]), StationCode = Convert.ToString(row["MES工位号"]), PositionName = Convert.ToString(row["现场位置"]), WorkStation = Convert.ToString(row["WorkStation"]), InsertXsdProgram = Convert.ToString(row["插XSDProgramName"]), InsertXseProgram = Convert.ToString(row["插XSEProgramName"]), PullXsdProgram = Convert.ToString(row["拔XSDProgramName"]), PullXseProgram = Convert.ToString(row["拔XSEProgramName"]) }; } private static string ReadCurrentProductModel(string stationCode) { SqlParameter[] parameters = { new SqlParameter("@MES工位号", stationCode) }; DataTable table = ExecuteProcedure("XD_TCS_AGV_库位产品_查询", parameters); string productModel = table.Rows.Count == 0 ? "" : Convert.ToString(table.Rows[0]["产品型号"]); if (string.IsNullOrWhiteSpace(productModel)) { throw new Exception("工位" + stationCode + "未获取到当前产品型号"); } return productModel.Trim(); } private static string ReadModelActionCombination(string lineCode, string productModel, string action) { SqlParameter[] parameters = { new SqlParameter("@产线代码", lineCode), new SqlParameter("@产品型号", productModel), new SqlParameter("@任务动作", action) }; DataTable table = ExecuteProcedure("XD_TCS_AGV_型号动作映射_查询", parameters); if (table.Rows.Count == 0) { throw new Exception("产品型号" + productModel + "未配置" + action + "动作组合"); } return Convert.ToString(table.Rows[0]["动作组合"]); } private static DataRow ReadTask(string externalCode) { if (string.IsNullOrWhiteSpace(externalCode)) { throw new Exception("外部任务编号不能为空"); } SqlParameter[] parameters = { new SqlParameter("@外部任务编号", externalCode.Trim()) }; DataTable table = ExecuteProcedure("XD_TCS_AGV_任务_获取", parameters); if (table.Rows.Count == 0) { throw new Exception("外部任务不存在"); } return table.Rows[0]; } private static void EnsureRobotAtPoint(TCSAGV_Config config, TCSAGV_PointConfig point) { TCSAGV_CallResult result = QueryRobotInfo(config); if (!result.Success) { throw new Exception("查询AGV实时位置失败:" + result.Message); } JObject robot = FirstResultObject(result.Response); if (robot == null) { throw new Exception("未查询到AGV状态,不能执行单步任务"); } if (ToInt(robot["status"], -1) == 4) { throw new Exception("AGV当前未连接,不能执行单步任务"); } string currentPosition = Convert.ToString(robot["currentPos"]); if (string.IsNullOrWhiteSpace(currentPosition)) { throw new Exception("未获取到AGV当前位置,不能执行单步任务"); } if (!string.Equals(currentPosition, point.WorkStation, StringComparison.OrdinalIgnoreCase)) { throw new Exception("AGV当前位置" + currentPosition + "与所选站点" + point.WorkStation + "不一致"); } } private static void UpdateDispatchResult(string externalCode, string requestText, string responseText, bool success, string errorMessage) { SqlParameter[] parameters = { new SqlParameter("@外部任务编号", externalCode ?? ""), new SqlParameter("@请求报文", requestText ?? ""), new SqlParameter("@响应报文", responseText ?? ""), new SqlParameter("@是否成功", success), new SqlParameter("@错误信息", errorMessage ?? "") }; ExecuteProcedure("XD_TCS_AGV_任务_下发结果更新", parameters); } private static void SetLocalTaskStatus(string externalCode, int status, string statusText, string errorMessage) { SqlParameter[] parameters = { new SqlParameter("@外部任务编号", externalCode ?? ""), new SqlParameter("@任务状态", status), new SqlParameter("@状态说明", statusText ?? ""), new SqlParameter("@错误信息", errorMessage ?? "") }; ExecuteProcedure("XD_TCS_AGV_任务_本地状态设置", parameters); } private static void SaveOperation(string externalCode, string stationCode, string operationType, string operatorCode, string requestText, string responseText, bool success, string message) { try { SqlParameter[] parameters = { new SqlParameter("@外部任务编号", externalCode ?? ""), new SqlParameter("@MES工位号", stationCode ?? ""), new SqlParameter("@操作类型", operationType ?? ""), new SqlParameter("@操作人", operatorCode ?? ""), new SqlParameter("@请求报文", requestText ?? ""), new SqlParameter("@响应报文", responseText ?? ""), new SqlParameter("@是否成功", success), new SqlParameter("@操作结果", message ?? "") }; ExecuteProcedure("XD_TCS_AGV_操作记录_增加", parameters); } catch (Exception ex) { SaveMesLog("TCS操作记录", ex.Message, MesLogType.ERROR); } } private static TCSAGV_CallResult PostToTcs(TCSAGV_Config config, string interfaceName, string path, object request) { string url = config.BaseUrl.TrimEnd('/') + path; string requestText = JsonConvert.SerializeObject(request); SaveLog_Interface_Request("TCS_" + interfaceName, 1, requestText, out int aid); string responseText; using (HttpClient client = CreateHttpClient(config)) using (StringContent content = new StringContent(requestText, Encoding.UTF8, "application/json")) { HttpResponseMessage response = client.PostAsync(url, content).GetAwaiter().GetResult(); responseText = response.Content.ReadAsStringAsync().GetAwaiter().GetResult(); if (string.IsNullOrWhiteSpace(responseText) && !response.IsSuccessStatusCode) { responseText = "HTTP " + (int)response.StatusCode; } } SaveLog_Interface_Response(responseText, aid); return ParseCallResult(requestText, responseText); } private static TCSAGV_CallResult GetFromTcs(TCSAGV_Config config, string interfaceName, string path, IDictionary query) { StringBuilder url = new StringBuilder(config.BaseUrl.TrimEnd('/') + path); JObject request = new JObject(); if (query != null && query.Count > 0) { url.Append('?'); bool first = true; foreach (KeyValuePair item in query) { if (!first) url.Append('&'); first = false; url.Append(HttpUtility.UrlEncode(item.Key)); url.Append('='); url.Append(HttpUtility.UrlEncode(item.Value ?? "")); request[item.Key] = item.Value ?? ""; } } string requestText = request.ToString(Formatting.None); bool skipInterfaceLog = string.Equals(interfaceName, "getMcrInfo", StringComparison.OrdinalIgnoreCase); int aid = 0; if (!skipInterfaceLog) { SaveLog_Interface_Request("TCS_" + interfaceName, 1, requestText, out aid); } string responseText; using (HttpClient client = CreateHttpClient(config)) { HttpResponseMessage response = client.GetAsync(url.ToString()).GetAwaiter().GetResult(); responseText = response.Content.ReadAsStringAsync().GetAwaiter().GetResult(); if (string.IsNullOrWhiteSpace(responseText) && !response.IsSuccessStatusCode) { responseText = "HTTP " + (int)response.StatusCode; } } if (!skipInterfaceLog) { SaveLog_Interface_Response(responseText, aid); } return ParseCallResult(requestText, responseText); } private static HttpClient CreateHttpClient(TCSAGV_Config config) { return new HttpClient { Timeout = TimeSpan.FromSeconds(config.HttpTimeoutSeconds > 0 ? config.HttpTimeoutSeconds : 15) }; } private static TCSAGV_CallResult ParseCallResult(string requestText, string responseText) { JObject response; try { response = JObject.Parse(responseText ?? ""); } catch (Exception ex) { return new TCSAGV_CallResult { Success = false, Code = 500, Message = "TCS返回非JSON响应:" + ex.Message, RequestText = requestText, ResponseText = responseText, Response = null }; } bool successFlag = false; bool hasSuccessFlag = response["success"] != null; if (hasSuccessFlag) { bool.TryParse(Convert.ToString(response["success"]), out successFlag); } int code = ToInt(response["code"], successFlag ? 200 : 500); bool success = hasSuccessFlag ? successFlag && code == 200 : code == 200; string message = Convert.ToString(response["message"] ?? response["msg"] ?? response["result"] ?? ""); return new TCSAGV_CallResult { Success = success, Code = code, Message = message, RequestText = requestText, ResponseText = responseText, Response = response }; } private static JObject FirstResultObject(JObject response) { if (response == null || response["result"] == null) return null; JToken result = response["result"]; if (result.Type == JTokenType.Array) { JArray array = (JArray)result; return array.Count > 0 ? array[0] as JObject : null; } return result as JObject; } private static DataTable ExecuteProcedure(string procedureName, SqlParameter[] parameters) { SqlParameter[] procedureParameters = parameters ?? new SqlParameter[0]; DataLinkMesWork2.DataAccess2.ExecuteStoredProcedure(procedureName, ref procedureParameters, out DataTable table, out string errorMessage); if (!string.IsNullOrWhiteSpace(errorMessage)) { throw new Exception(errorMessage); } return table ?? new DataTable(); } private static void EnsureBusinessSuccess(DataTable table) { if (table == null || table.Rows.Count == 0) { throw new Exception("数据库未返回业务结果"); } if (ToInt(table.Rows[0]["result"], 0) != 1) { string message = table.Columns.Contains("msg") ? Convert.ToString(table.Rows[0]["msg"]) : "业务操作失败"; throw new Exception(message); } } private static string NormalizeAction(string actionType) { string value = (actionType ?? "").Trim(); if (value == "插" || value.Equals("plug", StringComparison.OrdinalIgnoreCase)) return "插"; if (value == "拔" || value.Equals("pull", StringComparison.OrdinalIgnoreCase)) return "拔"; throw new Exception("任务动作只支持插或拔"); } private static string NormalizePortCombination(string portCombination) { string value = (portCombination ?? "").Trim().ToUpperInvariant(); if (value == "XSD" || value == "XSE" || value == "XSD+XSE") return value; throw new Exception("动作组合只支持XSD、XSE或XSD+XSE"); } private static bool IsConfiguredProgram(TCSAGV_PointConfig point, string programName) { return programName == point.InsertXsdProgram || programName == point.InsertXseProgram || programName == point.PullXsdProgram || programName == point.PullXseProgram; } private static string CreateExternalCode(string prefix) { return "XD_" + prefix + "_" + DateTime.Now.ToString("yyyyMMddHHmmssfff") + "_" + Guid.NewGuid().ToString("N").Substring(0, 6).ToUpperInvariant(); } private static int ToInt(object value, int defaultValue) { int result; return int.TryParse(Convert.ToString(value), out result) ? result : defaultValue; } private static bool ToBool(object value) { string text = Convert.ToString(value); return text == "1" || text.Equals("true", StringComparison.OrdinalIgnoreCase); } } }