From 3a99feb12a6b785376408511cd033c362d30cb46 Mon Sep 17 00:00:00 2001 From: chenjiangqun Date: Thu, 23 Apr 2026 10:06:24 +0800 Subject: [PATCH] =?UTF-8?q?=E8=A1=A5=E5=85=85=E6=BC=8F=E6=8F=90=E4=BA=A4?= =?UTF-8?q?=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- MesApi/MesApi/MesApi.cpp | 298 +++++++++++++++++++++++++++ MesApi/MesApi/MesApi.h | 35 ++++ MesApi/MesApi/MesApi.vcxproj | 4 +- MesApi/MesApi/MesApi.vcxproj.filters | 4 +- 4 files changed, 337 insertions(+), 4 deletions(-) create mode 100644 MesApi/MesApi/MesApi.cpp create mode 100644 MesApi/MesApi/MesApi.h diff --git a/MesApi/MesApi/MesApi.cpp b/MesApi/MesApi/MesApi.cpp new file mode 100644 index 0000000..994c098 --- /dev/null +++ b/MesApi/MesApi/MesApi.cpp @@ -0,0 +1,298 @@ +#pragma execution_character_set("utf-8") + +#include "MesApi.h" +#include +#include +#include +#include +#include +#include + +#pragma comment(lib, "winhttp.lib") + +// ============ 内部工具函数 ============ + +static std::wstring Utf8ToWide(const std::string& utf8) +{ + if (utf8.empty()) return L""; + int len = MultiByteToWideChar(CP_UTF8, 0, utf8.c_str(), -1, nullptr, 0); + std::wstring wide(len, L'\0'); + MultiByteToWideChar(CP_UTF8, 0, utf8.c_str(), -1, &wide[0], len); + wide.resize(len - 1); + return wide; +} + +static std::string WideToUtf8(const std::wstring& wide) +{ + if (wide.empty()) return ""; + int len = WideCharToMultiByte(CP_UTF8, 0, wide.c_str(), -1, nullptr, 0, nullptr, nullptr); + std::string utf8(len, '\0'); + WideCharToMultiByte(CP_UTF8, 0, wide.c_str(), -1, &utf8[0], len, nullptr, nullptr); + utf8.resize(len - 1); + return utf8; +} + +static int SafeCopyToBuffer(const std::string& src, char* outBuffer, int bufferSize) +{ + if (!outBuffer || bufferSize <= 0) return -3; + if ((int)src.size() >= bufferSize) return -3; + memcpy(outBuffer, src.c_str(), src.size()); + outBuffer[src.size()] = '\0'; + return 0; +} + +static std::string ExtractJsonValue(const std::string& json, const std::string& key) +{ + std::string searchKey = "\"" + key + "\""; + size_t pos = json.find(searchKey); + if (pos == std::string::npos) return ""; + + pos += searchKey.size(); + + while (pos < json.size() && (json[pos] == ' ' || json[pos] == '\t' || json[pos] == '\r' || json[pos] == '\n')) pos++; + if (pos >= json.size() || json[pos] != ':') return ""; + pos++; + + while (pos < json.size() && (json[pos] == ' ' || json[pos] == '\t' || json[pos] == '\r' || json[pos] == '\n')) pos++; + + if (pos >= json.size()) return ""; + + if (json.compare(pos, 4, "null") == 0) + { + return ""; + } + + if (json[pos] == '\"') + { + pos++; + std::string result; + while (pos < json.size()) + { + if (json[pos] == '\\' && pos + 1 < json.size()) + { + char next = json[pos + 1]; + switch (next) + { + case '\"': result += '\"'; break; + case '\\': result += '\\'; break; + case '/': result += '/'; break; + case 'n': result += '\n'; break; + case 'r': result += '\r'; break; + case 't': result += '\t'; break; + default: result += '\\'; result += next; break; + } + pos += 2; + } + else if (json[pos] == '\"') + { + break; + } + else + { + result += json[pos]; + pos++; + } + } + return result; + } + else + { + size_t end = json.find_first_of(",} \t\r\n", pos); + if (end == std::string::npos) end = json.size(); + return json.substr(pos, end - pos); + } +} + +static std::string EscapeJsonString(const char* input) +{ + if (!input) return ""; + std::string output; + output.reserve(strlen(input) + 16); + for (const char* p = input; *p; ++p) + { + switch (*p) + { + case '\"': output += "\\\""; break; + case '\\': output += "\\\\"; break; + case '\b': output += "\\b"; break; + case '\f': output += "\\f"; break; + case '\n': output += "\\n"; break; + case '\r': output += "\\r"; break; + case '\t': output += "\\t"; break; + default: output += *p; break; + } + } + return output; +} + +// ============ HTTP POST(6 个参数) ============ + +static int HttpPostJson( + const std::wstring& host, + int port, + const std::wstring& path, + const std::string& jsonBody, + int timeoutMs, + std::string& responseBody) +{ + HINTERNET hSession = NULL, hConnect = NULL, hRequest = NULL; + int ret = -1; + + hSession = WinHttpOpen(L"MesApi/1.0", + WINHTTP_ACCESS_TYPE_DEFAULT_PROXY, + WINHTTP_NO_PROXY_NAME, + WINHTTP_NO_PROXY_BYPASS, 0); + if (!hSession) goto cleanup; + + WinHttpSetTimeouts(hSession, timeoutMs, timeoutMs, timeoutMs, timeoutMs); + + hConnect = WinHttpConnect(hSession, host.c_str(), (INTERNET_PORT)port, 0); + if (!hConnect) goto cleanup; + + hRequest = WinHttpOpenRequest(hConnect, L"POST", path.c_str(), + NULL, WINHTTP_NO_REFERER, + WINHTTP_DEFAULT_ACCEPT_TYPES, 0); + if (!hRequest) goto cleanup; + + { + const wchar_t* header = L"Content-Type: application/json; charset=utf-8"; + if (!WinHttpAddRequestHeaders(hRequest, header, (DWORD)-1, WINHTTP_ADDREQ_FLAG_ADD)) + goto cleanup; + } + + if (!WinHttpSendRequest(hRequest, + WINHTTP_NO_ADDITIONAL_HEADERS, 0, + (LPVOID)jsonBody.c_str(), (DWORD)jsonBody.size(), + (DWORD)jsonBody.size(), 0)) + goto cleanup; + + if (!WinHttpReceiveResponse(hRequest, NULL)) + goto cleanup; + + { + DWORD bytesAvailable = 0; + DWORD bytesRead = 0; + std::string body; + body.reserve(4096); + char tmpBuf[4096]; + + do + { + bytesAvailable = 0; + if (!WinHttpQueryDataAvailable(hRequest, &bytesAvailable) || bytesAvailable == 0) + break; + + DWORD toRead = (bytesAvailable < sizeof(tmpBuf)) ? bytesAvailable : sizeof(tmpBuf); + bytesRead = 0; + if (WinHttpReadData(hRequest, tmpBuf, toRead, &bytesRead) && bytesRead > 0) + { + body.append(tmpBuf, bytesRead); + } + else + { + break; + } + } while (true); + + // 剥离 UTF-8 BOM + if (body.size() >= 3 && + (unsigned char)body[0] == 0xEF && + (unsigned char)body[1] == 0xBB && + (unsigned char)body[2] == 0xBF) + { + body.erase(0, 3); + } + + responseBody = std::move(body); + ret = 0; + } + +cleanup: + if (hRequest) WinHttpCloseHandle(hRequest); + if (hConnect) WinHttpCloseHandle(hConnect); + if (hSession) WinHttpCloseHandle(hSession); + return ret; +} + +// ============ 导出函数 ============ + +extern "C" +MESAPI_API int MESAPI_CALL UploadResult( + const char* sn, + const char* station, + const char* function, + const char* result, + const char* costtime, + char* outBuffer, + int bufferSize) +{ + try + { + // 1. 构建 JSON + std::ostringstream oss; + oss << "{" + << "\"USERCODE\":\"SAPEDI\"," + << "\"TOKEN\":\"SCBCSMESAPI2021!@#\"," + << "\"METHOD\":\"UploadResult\"," + << "\"PARAS\":{" + << "\"SN\":\"" << EscapeJsonString(sn) << "\"," + << "\"Station\":\"" << EscapeJsonString(station) << "\"," + << "\"FunctionName\":\"" << EscapeJsonString(function) << "\"," + << "\"TestResult\":\"" << EscapeJsonString(result) << "\"," + << "\"CostTime\":\"" << EscapeJsonString(costtime) << "\"," + << "\"Remark\":\"\"" + << "}}"; + + std::string jsonBody = oss.str(); + + // 2. HTTP POST + std::wstring host = L"10.222.16.22"; + int port = 80; + std::wstring path = L"/SMESCommAPI/API/SAPEDI/SMESDataExchage"; + int timeout = 8000; + + std::string responseBody; + int httpRet = HttpPostJson(host, port, path, jsonBody, timeout, responseBody); + + if (httpRet != 0) + { + // ★ 中文字面量现在是 UTF-8(靠文件顶部 pragma 保证) + std::string errMsg = "ERROR:网络无连接"; + return SafeCopyToBuffer(errMsg, outBuffer, bufferSize) == 0 ? -1 : -3; + } + + // 3. 解析响应 + std::string resultField = ExtractJsonValue(responseBody, "Result"); + + if (resultField == "true") + { + std::string ok = "TRUE"; + int copyRet = SafeCopyToBuffer(ok, outBuffer, bufferSize); + return copyRet == 0 ? 0 : -3; + } + else + { + // ★ errorField 来自服务器响应,本身就是 UTF-8 + // ★ 前缀中文也是 UTF-8(靠 pragma),拼接后编码一致,不会乱码 + std::string errorField = ExtractJsonValue(responseBody, "Error"); + std::string dataField = ExtractJsonValue(responseBody, "Data"); + + std::string errMsg; + if (!errorField.empty()) + errMsg = "ERROR:" + errorField; + else if (!dataField.empty()) + errMsg = "ERROR:" + dataField; + else + errMsg = "ERROR:MES返回失败(Result=" + resultField + ")"; + + int copyRet = SafeCopyToBuffer(errMsg, outBuffer, bufferSize); + return copyRet == 0 ? -2 : -3; + } + } + catch (...) + { + std::string errMsg = "ERROR:未知异常"; + SafeCopyToBuffer(errMsg, outBuffer, bufferSize); + return -4; + } +} \ No newline at end of file diff --git a/MesApi/MesApi/MesApi.h b/MesApi/MesApi/MesApi.h new file mode 100644 index 0000000..ce11eed --- /dev/null +++ b/MesApi/MesApi/MesApi.h @@ -0,0 +1,35 @@ +#pragma once + +#ifdef MESAPI_EXPORTS +#define MESAPI_API __declspec(dllexport) +#else +#define MESAPI_API __declspec(dllimport) +#endif + +#if defined(_WIN64) +#define MESAPI_CALL __cdecl +#else +#define MESAPI_CALL __stdcall +#endif + +extern "C" +{ + /// @brief 上传测试结果到 MES 系统 + /// @param sn 产品序列号 + /// @param station 工站名 + /// @param function 功能名称 + /// @param result 测试结果 + /// @param costtime 耗时 + /// @param outBuffer 输出缓冲区(调用方分配) + /// @param bufferSize 输出缓冲区大小 + /// @return 0=成功, -1=网络错误, -2=MES返回失败, -3=缓冲区不足, -4=其他异常 + MESAPI_API int MESAPI_CALL UploadResult( + const char* sn, + const char* station, + const char* function, + const char* result, + const char* costtime, + char* outBuffer, + int bufferSize + ); +} \ No newline at end of file diff --git a/MesApi/MesApi/MesApi.vcxproj b/MesApi/MesApi/MesApi.vcxproj index 671bd38..be176d5 100644 --- a/MesApi/MesApi/MesApi.vcxproj +++ b/MesApi/MesApi/MesApi.vcxproj @@ -147,13 +147,13 @@ - + - + Create Create diff --git a/MesApi/MesApi/MesApi.vcxproj.filters b/MesApi/MesApi/MesApi.vcxproj.filters index 6f42b46..61f0bc9 100644 --- a/MesApi/MesApi/MesApi.vcxproj.filters +++ b/MesApi/MesApi/MesApi.vcxproj.filters @@ -21,7 +21,7 @@ 澶存枃浠 - + 澶存枃浠 @@ -32,7 +32,7 @@ 婧愭枃浠 - + 婧愭枃浠