using System; using System.Collections.Generic; using System.IO; using System.Net; using System.Text; using System.Xml; namespace TeamAAS_VP.Core.Sfis { public class SfisWebServiceClient { private readonly SfisConfig _config; private readonly object _invokeSync = new object(); public event Action OnLog; public bool IsLoggedIn { get; private set; } public SfisWebServiceClient(SfisConfig config) { _config = config ?? throw new ArgumentNullException(nameof(config)); } public SfisResponse Login() { var response = Invoke("WTSP_LOGINOUT", new Dictionary { { "programId", _config.ProgramId }, { "programPassword", _config.ProgramPassword }, { "op", _config.OperatorId }, { "password", _config.OperatorPassword ?? string.Empty }, { "device", _config.Device }, { "TSP", _config.TspName }, { "status", 1 } }); if (response.IsSuccess) IsLoggedIn = true; return response; } public SfisResponse Logout() { var response = Invoke("WTSP_LOGINOUT", new Dictionary { { "programId", _config.ProgramId }, { "programPassword", _config.ProgramPassword }, { "op", _config.OperatorId }, { "password", _config.OperatorPassword ?? string.Empty }, { "device", _config.Device }, { "TSP", _config.TspName }, { "status", 2 } }); IsLoggedIn = false; return response; } public SfisResponse CheckRoute(string isn, int type, string checkFlag = null, string checkData = null) { return Invoke("WTSP_CHKROUTE", new Dictionary { { "programId", _config.ProgramId }, { "programPassword", _config.ProgramPassword }, { "ISN", isn }, { "device", _config.Device }, { "checkFlag", checkFlag ?? string.Empty }, { "checkData", checkData ?? string.Empty }, { "type", type } }); } public SfisResponse GetVersion(string isn, string type, string chkData = null, string chkData2 = null) { return Invoke("WTSP_GETVERSION", new Dictionary { { "programId", _config.ProgramId }, { "programPassword", _config.ProgramPassword }, { "ISN", isn }, { "device", _config.Device }, { "type", type ?? string.Empty }, { "ChkData", chkData ?? string.Empty }, { "ChkData2", chkData2 ?? string.Empty } }); } public SfisResponse SendResult(string isn, string data, int status, string errorCode = null) { return Invoke("WTSP_RESULT", new Dictionary { { "programId", _config.ProgramId }, { "programPassword", _config.ProgramPassword }, { "ISN", isn }, { "error", errorCode ?? string.Empty }, { "device", _config.Device }, { "TSP", _config.TspName }, { "data", data }, { "status", status }, { "CPKFlag", string.Empty } }); } public SfisResponse SendInputData(string data, int type) { return Invoke("WTSP_SSD_INPUTDATA", new Dictionary { { "programId", _config.ProgramId }, { "programPassword", _config.ProgramPassword }, { "device", _config.Device }, { "data", data }, { "type", type } }); } public SfisResponse Invoke(string methodName, IDictionary parameters) { lock (_invokeSync) { if (_config.UseDryRun()) return InvokeDryRun(methodName, parameters); ValidateLiveConfig(); string soapAction = _config.SoapNamespace.TrimEnd('/') + "/" + methodName; string envelope = BuildSoapEnvelope(methodName, parameters); OnLog?.Invoke("SFIS 调用 " + methodName); var request = (HttpWebRequest)WebRequest.Create(_config.ServiceUrl); request.Method = "POST"; request.ContentType = "text/xml; charset=utf-8"; request.Headers.Add("SOAPAction", "\"" + soapAction + "\""); request.Timeout = _config.TimeoutMs; request.ReadWriteTimeout = _config.TimeoutMs; byte[] payload = Encoding.UTF8.GetBytes(envelope); request.ContentLength = payload.Length; using (var stream = request.GetRequestStream()) stream.Write(payload, 0, payload.Length); try { using (var response = (HttpWebResponse)request.GetResponse()) using (var reader = new StreamReader(response.GetResponseStream(), Encoding.UTF8)) { string soapResponse = reader.ReadToEnd(); string result = ExtractSoapResult(soapResponse, methodName); var parsed = SfisResponse.Parse(result); OnLog?.Invoke("SFIS " + methodName + " => P_RET=" + parsed.ReturnCode); return parsed; } } catch (WebException ex) { string errorBody = ReadErrorBody(ex); OnLog?.Invoke("SFIS 调用失败: " + ex.Message); if (!string.IsNullOrWhiteSpace(errorBody)) OnLog?.Invoke(errorBody); return new SfisResponse { ReturnCode = 0, Message = string.IsNullOrWhiteSpace(errorBody) ? ex.Message : errorBody, Raw = errorBody }; } } } private void ValidateLiveConfig() { if (string.IsNullOrWhiteSpace(_config.ServiceUrl)) throw new InvalidOperationException("SfisServiceUrl 未配置"); if (string.IsNullOrWhiteSpace(_config.ProgramId)) throw new InvalidOperationException("SfisProgramId 未配置"); if (string.IsNullOrWhiteSpace(_config.Device)) throw new InvalidOperationException("SfisDevice 未配置"); } private static string ReadErrorBody(WebException ex) { if (ex.Response == null) return null; using (var reader = new StreamReader(ex.Response.GetResponseStream())) return reader.ReadToEnd(); } private string BuildSoapEnvelope(string methodName, IDictionary parameters) { var sb = new StringBuilder(); sb.Append(""); sb.Append(""); sb.Append(""); sb.Append("<").Append(methodName).Append(" xmlns=\"").Append(XmlEscape(_config.SoapNamespace)).Append("\">"); foreach (var pair in parameters) { sb.Append("<").Append(pair.Key).Append(">"); sb.Append(XmlEscape(Convert.ToString(pair.Value ?? string.Empty))); sb.Append(""); } sb.Append(""); sb.Append(""); sb.Append(""); return sb.ToString(); } private static string ExtractSoapResult(string soapResponse, string methodName) { if (string.IsNullOrWhiteSpace(soapResponse)) return string.Empty; var doc = new XmlDocument(); doc.LoadXml(soapResponse); string[] candidateNames = { methodName + "Result", methodName + "Response", "return" }; foreach (string name in candidateNames) { XmlNodeList nodes = doc.GetElementsByTagName(name); if (nodes != null && nodes.Count > 0 && nodes[0] != null) return nodes[0].InnerText; } XmlNodeList bodyNodes = doc.GetElementsByTagName("soap:Body"); if (bodyNodes == null || bodyNodes.Count == 0) bodyNodes = doc.GetElementsByTagName("Body"); if (bodyNodes != null && bodyNodes.Count > 0 && bodyNodes[0] != null) return bodyNodes[0].InnerText?.Trim() ?? soapResponse; return soapResponse; } private SfisResponse InvokeDryRun(string methodName, IDictionary parameters) { OnLog?.Invoke("[DryRun] SFIS 调用 " + methodName); switch (methodName) { case "WTSP_LOGINOUT": int status = Convert.ToInt32(parameters["status"]); if (status == 1) { IsLoggedIn = true; return SfisResponse.Parse("1" + SfisDelimiters.Field + "Welcome using Pegatron SFIS" + SfisDelimiters.Field + "DryRun Operator"); } IsLoggedIn = false; return SfisResponse.Parse("1" + SfisDelimiters.Field + "Logout OK"); case "WTSP_CHKROUTE": return SfisResponse.Parse("1" + SfisDelimiters.Field + "[#1][MODEL:DRYRUN]"); case "WTSP_GETVERSION": return SfisResponse.Parse("1" + SfisDelimiters.Field + "DryRun Version"); case "WTSP_RESULT": return SfisResponse.Parse("1" + SfisDelimiters.Field + "RESULT SAVED OK"); case "WTSP_SSD_INPUTDATA": return SfisResponse.Parse("1" + SfisDelimiters.Field + "MO SAVED OK(DryRun);ISN OK!! PASS!-(TSP_SSD_INPUTDATA)"); default: return SfisResponse.Parse("1" + SfisDelimiters.Field + methodName + " DryRun OK"); } } private static string XmlEscape(string value) { if (string.IsNullOrEmpty(value)) return string.Empty; return value.Replace("&", "&").Replace("<", "<").Replace(">", ">") .Replace("\"", """).Replace("'", "'"); } } }