#include "pch.h"
#include "MESInterface.h"
#include "Loger.h"
extern CGlobalVariant m_GVariant; // 全局变量
extern CGlobalFunction m_GFunction; // 全局函数
extern Loger m_Loger; // 全局Log类
extern CActionFlow m_GActionFlow; // 动作流程
extern CDlgDevError m_dlgError;
MESInterface* MESInterface::m_pInstance = nullptr;
MESInterface * MESInterface::GetInstance()
{
static CCriticalSection cs;
CriticalSectionLock lock(cs); // 使用我们之前定义的 RAII 锁
if (m_pInstance == nullptr) // 错误:m_pInstance 未声明
{
m_pInstance = new MESInterface();
}
return m_pInstance;
}
void MESInterface::PostTask(TaskType type, const CString & param)
{
TaskItem* pItem = new TaskItem();
pItem->type = type;
pItem->param = param;
{
//CriticalSectionLock lock(m_cs); // 使用自定义锁
m_taskList.AddTail(pItem);
}
m_taskEvent.SetEvent(); // 触发事件
}
UINT MESInterface::WorkerThreadProc(LPVOID pParam)
{
MESInterface* pThis = (MESInterface*)pParam;
while (!pThis->m_bStopThread)
{
DWORD dwWait = WaitForSingleObject(pThis->m_taskEvent.m_hObject, INFINITE);
if (dwWait != WAIT_OBJECT_0)
continue;
if (pThis->m_bStopThread)
break;
TaskItem* pItem = nullptr;
{
CriticalSectionLock lock(pThis->m_cs); // 使用自定义锁
if (!pThis->m_taskList.IsEmpty())
{
pItem = pThis->m_taskList.RemoveHead();
}
}
if (pItem)
{
pThis->ProcessTask(pItem);
delete pItem;
}
}
return 0;
}
void MESInterface::ProcessTask(TaskItem * pItem)
{
switch (pItem->type)
{
case TASK_CHECK:
ExecuteCheck(m_GFunction.CString2String(pItem->param));
break;
case TASK_EVENT_DEVICE_STATE_CHANGED:
ExecuteDeviceStop(static_cast<StopCode>(_ttoi(pItem->param)));
break;
}
}
int MESInterface::ExecuteCheck(string sn)
{
CriticalSectionLock lock(m_cs); // 线程安全调用
MesJTInfo info;
info.WP = QString::fromLocal8Bit(m_GVariant.m_MesConfigFile->WP.c_str());
info.JZ = QString::fromLocal8Bit(m_GVariant.m_MesConfigFile->JZ.c_str());
info.User = QString::fromLocal8Bit(m_GVariant.m_MesConfigFile->User.c_str());
info.Line = QString::fromLocal8Bit(m_GVariant.m_MesConfigFile->Line.c_str());
info.Machine = QString::fromLocal8Bit(m_GVariant.m_MesConfigFile->Machine.c_str());
info.OffLine = m_GVariant.m_MesConfigFile->OffLine;
info.Tray = QString::fromLocal8Bit(m_GVariant.m_MesConfigFile->Tray.c_str());
info.Barcodes.append(QString::fromLocal8Bit(sn.c_str()));
QString jsonString = info.toJsonString();
Response res = SendMesJTInfo(static_cast<string>(jsonString.toLocal8Bit()));
if (res.Code == 0 || res.Result == "OK")
{
return 1;
}
else
{
addErrInfo(static_cast<string>(res.Msg.toLocal8Bit()));
return -1;
}
return 0;
}
void MESInterface::ExecuteDeviceStop(StopCode state)
{
CriticalSectionLock lock(m_cs); // 线程安全调用
MesEquStop maState;
string eventDesc = "";
int deviceStatus = 0;
switch (state)
{
case Waiting:
eventDesc = "待机";
deviceStatus = 1;
break;
case Wroking:
eventDesc = "作业";
deviceStatus = 2;
break;
case Faulted:
eventDesc = "故障";
deviceStatus = 3;
break;
case Stopped:
eventDesc = "关机";
deviceStatus = 4;
break;
default:
eventDesc = "开机";
deviceStatus = 0;
break;
}
maState.StopCode = deviceStatus;
maState.StopType = QString::fromLocal8Bit(eventDesc.c_str());
maState.Machine = QString::fromLocal8Bit(m_GVariant.m_MesConfigFile->Machine.c_str());
maState.StartTime = QDateTime::currentDateTime();
maState.JZ = QString::fromLocal8Bit(m_GVariant.m_MesConfigFile->JZ.c_str());
maState.Line = QString::fromLocal8Bit(m_GVariant.m_MesConfigFile->Line.c_str());
maState.User = QString::fromLocal8Bit(m_GVariant.m_MesConfigFile->User.c_str());
QString jsonString = maState.toJsonString();
Response res = SendMesEquStop(static_cast<string>(jsonString.toLocal8Bit()));
if (res.Code == 0 || res.Result == "OK")
{
return;
}
else
{
addErrInfo(static_cast<string>(res.Msg.toLocal8Bit()));
return;
}
}
QString convertSimple(const QString& input)
{
if (input.length() != 17) return input; // 确保长度正确
QString year = input.mid(0, 4);
QString month = input.mid(4, 2);
QString day = input.mid(6, 2);
QString time = input.mid(9, 8); // 提取时间部分
return QString("%1-%2-%3 %4").arg(year, month, day, time);
}
int MESInterface::updataMes(TestData testData)
{
MesMTF mesInfo;
auto MTFvariantMap = testData.toVariantMap(); // 这里拿到了OrderedVariantMap的值
QString MTFString = "{";
for (const QString& key : MTFvariantMap.keys())
{
QVariant variant = MTFvariantMap[key];
QString value = variant.toString();
MTFString.append("\"" + key + "\":\"" + value + "\",");
}
MTFString.chop(1);
MTFString.append("}");
MTF Data;
Data.Barcode = testData.Barcode;
Data.ST = convertSimple(testData.Basic.Time);
if ("PASS" == testData.Result.Aggregate.trimmed() || "BEST" == testData.Result.Aggregate.trimmed())
{
Data.Result = "OK";
}
else
{
Data.Result = "NG";
}
Data.MTF = MTFString;
auto DatavariantMap = Data.toVariantMap();
QString DataString = "[{";
for (const QString& key : DatavariantMap.keys())
{
QVariant variant = DatavariantMap[key];
QString value = variant.toString();
if (key == "MTF")
{
DataString.append("\"" + key + "\":" + value + ",");
}
else
{
DataString.append("\"" + key + "\":\"" + value + "\",");
}
}
DataString.chop(1);
DataString.append("}]");
mesInfo.Num = 1;
mesInfo.JZ = QString::fromLocal8Bit(m_GVariant.m_MesConfigFile->JZ.c_str());
mesInfo.User = QString::fromLocal8Bit(m_GVariant.m_MesConfigFile->User.c_str());
mesInfo.Line = QString::fromLocal8Bit(m_GVariant.m_MesConfigFile->Line.c_str());
mesInfo.Machine = QString::fromLocal8Bit(m_GVariant.m_MesConfigFile->Machine.c_str());
mesInfo.Tray = QString::fromLocal8Bit(m_GVariant.m_MesConfigFile->Tray.c_str());
mesInfo.OffLine = m_GVariant.m_iFrontRunMode == 0 ? 1 : 0;
mesInfo.Data = DataString;
QString jsonString = "{";
auto mesinfo = mesInfo.toVariantMap();
for (const QString& key : mesinfo.keys())
{
QVariant variant = mesinfo[key];
QString value = variant.toString();
if (key == "Data")
{
jsonString.append("\"" + key + "\":" + value + ",");
}
else
{
jsonString.append("\"" + key + "\":\"" + value + "\",");
}
}
jsonString.chop(1);
jsonString.append("}");
Response res = SendMesEquStop(static_cast<string>(jsonString.toLocal8Bit()));
if (res.Code == 0 || res.Result == "OK")
{
return 1;
}
else
{
addErrInfo(static_cast<string>(res.Msg.toLocal8Bit()));
return -1;
}
return 0;
}
void MESInterface::PullMes()
{
CriticalSectionLock lock(m_cs); // ✅ 加锁
sSC_PullMes.Init();
updataRun = true;
while (true)
{
int iRtn = FL_PullMes();
if (-1 == iRtn)
{
updataRun = false;
break;
}
Sleep(5);
}
}
int MESInterface::FL_PullMes()
{
int iRtn = 0;
switch (sSC_PullMes.nStep)
{
case 0:
addInfo(sSC_PullMes, 5, "开始巡检MES目录", true);
return 0;
case 5:
iRtn = checkFolder();
if (-1 == iRtn)
{
addInfo(sSC_PullMes, ST_ERR, "巡检MES目录失败");
}
if (1 == iRtn)
{
sSC_PullMes.nStep = 10;
}
else
{
sSC_PullMes.SetDelay(5, 1000);
}
return 0;
case 10:
if (m_File.checkFileExist(m_destinationFolder, m_currentFileNames))
{
sSC_PullMes.nStep = 15;
}
else
{
addInfo(sSC_PullMes, ST_ERR, "检查MES文件是否存在失败");
}
return 0;
case 15:
if (m_bufferFileNames.size() == 0)
{
sSC_PullMes.nStep = ST_END;
}
else
{
sSC_PullMes.nStep = 20;
}
return 0;
case 20:
if (m_iItemIndex <= m_bufferFileNames.size() - 1)
{
m_qstrRemoveFile = m_bufferFileNames[m_iItemIndex];
QString tempFile = m_destinationFolder + "\\" + m_qstrRemoveFile;
if (deserializeFromJsonFile(tempFile, m_MesInfo))
{
m_GVariant.configItemFile->copyToTestData(m_MesInfo);
if (m_MesInfo.Barcode == "" || m_MesInfo.Barcode.length() < 5)
{
m_iItemIndex = 0;//将索引归零
addInfo(sSC_PullMes, 15, "条码长度小于5:条码为" + (string)m_MesInfo.Barcode.toLocal8Bit());
m_bufferFileNames.removeOne(m_bufferFileNames[m_iItemIndex]);
}
else
{
m_iItemIndex++;
addInfo(sSC_PullMes, 25, "准备上传单角度数据");
}
}
else
{
addInfo(sSC_PullMes, ST_ERR, "文件" + (string)tempFile.toLocal8Bit() + "json序列化失败");
}
}
else
{
addInfo(sSC_PullMes, ST_END, "缓存文件已上传完毕");
}
return 0;
case 25:
iRtn = updataMes(m_MesInfo);
if (-1 == iRtn)
{
sSC_PullMes.nStep = ST_ERR;
}
if (1 == iRtn)
{
//清空列表中的
m_bufferFileNames.removeOne(m_qstrRemoveFile);
m_iItemIndex = 0;//将索引归零
addInfo(sSC_PullMes, 15, "单角度上传完成,开始缓存新MES数据");
}
return 0;
case ST_DELAY:
if (GetTickCount() - sSC_PullMes.dwStart >= sSC_PullMes.dwDelay)
{
sSC_PullMes.nStep = sSC_PullMes.nStepNext;
}
return 0;
case ST_ERR:
return -1;
case ST_END:
sSC_PullMes.ulCountEnd = m_GFunction.GetCPUPerformanceCounter();
sSC_PullMes.dCycleTime = (sSC_PullMes.ulCountEnd - sSC_PullMes.ulCountStart) / 1000000.0;
sSC_PullMes.AddCTRecordEnd(m_GActionFlow.m_iRecordCycleTime, "sSC_PullMes over, CycleTime:", sSC_PullMes.dCycleTime);
sSC_PullMes.Init();
return 1;
}
return 0;
}
void MESInterface::StartWorkerThread()
{
if (m_pWorkerThread == nullptr)
{
m_pWorkerThread = AfxBeginThread(WorkerThreadProc, this);
}
}
void MESInterface::StopWorkerThread()
{
if (m_pWorkerThread != nullptr)
{
m_bStopThread = true;
m_taskEvent.SetEvent(); // 唤醒线程退出
WaitForSingleObject(m_pWorkerThread->m_hThread, INFINITE);
m_pWorkerThread = nullptr;
}
}
MESInterface::MESInterface()
{
updataRun = false;
StartWorkerThread();
}
MESInterface::~MESInterface()
{
StopWorkerThread();
}
// 请求镜头过线情况接口 /// http://10.93.2.17:8083/datasnap/rest/THttp1/MesJTInfo
Response MESInterface::SendMesJTInfo(string info)
{
string host = m_GVariant.m_MesConfigFile->Host;
int port = m_GVariant.m_MesConfigFile->Port;
string path = m_GVariant.m_MesConfigFile->Check;
string username = m_GVariant.m_MesConfigFile->UserName;
string password = m_GVariant.m_MesConfigFile->PassWord;
Response resp = Send(info, host, port, path, username, password);
if (SUCCESS == resp.Code)
{
return ParseRcv(static_cast<string>(resp.Msg.toLocal8Bit()));
}
return resp;
}
// 工序接口- MTF检查 /// http://10.93.2.17:8083/datasnap/rest/THttp1/MesZZ
Response MESInterface::SendMesMTF()
{
string host = m_GVariant.m_MesConfigFile->Host;
int port = m_GVariant.m_MesConfigFile->Port;
string path = m_GVariant.m_MesConfigFile->Save;
string username = m_GVariant.m_MesConfigFile->UserName;
string password = m_GVariant.m_MesConfigFile->PassWord;
string info = "";
Response resp = Send(info, host, port, path, username, password);
if (SUCCESS == resp.Code)
{
return ParseRcv(static_cast<string>(resp.Msg.toLocal8Bit()));
}
return resp;
}
// 其它接口-设备异常上报 /// http://10.93.2.17:8083/datasnap/rest/THttp1/MesEquStop
Response MESInterface::SendMesEquStop(string info)
{
string host = m_GVariant.m_MesConfigFile->Host;
int port = m_GVariant.m_MesConfigFile->Port;
string path = m_GVariant.m_MesConfigFile->DeviceInfoCollerct;
string username = m_GVariant.m_MesConfigFile->UserName;
string password = m_GVariant.m_MesConfigFile->PassWord;
Response resp = Send(info, host, port, path, username, password);
if (SUCCESS == resp.Code)
{
return ParseRcv(static_cast<string>(resp.Msg.toLocal8Bit()));
}
return resp;
}
int MESInterface::addInfo(string & info)
{
if (m_logInfo == info)
{
return 1;
}
m_logInfo = info;
m_Loger.RecordLogMes(m_logInfo);
// 发送消息,传递快照指针
if (NULL != m_GActionFlow.m_hwdMainView)
{
// 使用PostMessage
CString* pMsg = new CString(CA2CT(info.c_str()));
::SendMessage(m_GActionFlow.m_hwdMainView, WM_MESINFO, (WPARAM)pMsg, 0);
}
return 0;
}
int MESInterface::addInfo(StepCtrl & crtl, int next, string info, bool blag)
{
addInfo(info);
crtl.nStep = next;
if (blag)
{
crtl.ulCountStart = m_GFunction.GetCPUPerformanceCounter();
crtl.InitRecordString();
}
if (next == ST_ERR)
{
addErrInfo(info);
}
return 0;
}
int MESInterface::addErrInfo(string info)
{
info = "MES" + info;
m_GActionFlow.LightOpenRed();
m_dlgError.SetError(info);
m_Loger.RecordLogError(info);
//m_gMes.DeviceStateChanged(DeviceState::Faulted); 不能用,MES服务端出现问题后不能再调用,会导致死循环
AfxMessageBox(m_GFunction.String2CString(info), MB_OK | MB_ICONSTOP | MB_SYSTEMMODAL);
m_GActionFlow.LightOpenYellow();
return 0;
}
void MESInterface::Connect()
{
CriticalSectionLock lock(m_cs);
if (m_GVariant.m_MesConfigFile->Enable == 0)
{
m_connectionState = ConnectionState::Disconnected;
return;
}
// 已连接且目标相同则直接返回
if (m_pConnection && m_connectionState == ConnectionState::Connected)
{
if (m_cachedHost == m_GVariant.m_MesConfigFile->Host &&
m_cachedPort == m_GVariant.m_MesConfigFile->Port)
{
return;
}
}
m_connectionState = ConnectionState::Connecting;
// 释放旧连接(如果存在)
if (m_pConnection)
{
delete m_pConnection;
m_pConnection = nullptr;
}
// 设置全局超时参数
HINTERNET hSession = (HINTERNET)m_Session; // ✅ 正确获取 HINTERNET 句柄
if (hSession)
{
DWORD connectTimeout = 5000; // 5 秒
DWORD sendTimeout = m_GVariant.m_MesConfigFile->SendTimeout;
DWORD recvTimeout = m_GVariant.m_MesConfigFile->RecvTimeout;
InternetSetOption(hSession, INTERNET_OPTION_CONNECT_TIMEOUT, &connectTimeout, sizeof(connectTimeout));
InternetSetOption(hSession, INTERNET_OPTION_SEND_TIMEOUT, &sendTimeout, sizeof(sendTimeout));
InternetSetOption(hSession, INTERNET_OPTION_RECEIVE_TIMEOUT, &recvTimeout, sizeof(recvTimeout));
}
try {
// 创建新连接
m_pConnection = m_Session.GetHttpConnection(
m_GFunction.String2CString(m_GVariant.m_MesConfigFile->Host),
m_GVariant.m_MesConfigFile->Port,
_T(""), // username
_T("") // password
);
if (m_pConnection)
{
m_connectionState = ConnectionState::Connected;
// 更新缓存信息
m_cachedHost = m_GVariant.m_MesConfigFile->Host;
m_cachedPort = m_GVariant.m_MesConfigFile->Port;
CString logMsg;
logMsg.Format(_T("MES连接成功: %s:%d"),
m_GFunction.String2CString(m_cachedHost), m_cachedPort);
addInfo(m_GFunction.CString2String(logMsg));
}
else
{
m_connectionState = ConnectionState::Error;
addErrInfo("MES连接失败: 无法创建HTTP连接");
}
}
catch (CInternetException* pEx)
{
TCHAR szError[1024];
pEx->GetErrorMessage(szError, 1024);
m_connectionState = ConnectionState::Error;
addErrInfo("MES连接异常: " + m_GFunction.CString2String(szError));
pEx->Delete();
}
}
Response MESInterface::Send(const string & info, const string & hostname, const int & port,
const string & apiPath, string m_username, string m_password)
{
CriticalSectionLock lock(m_cs);
// 检查连接状态
if (m_connectionState != ConnectionState::Connected)
{
Connect(); // 尝试重连
// 重连后仍然失败
if (m_connectionState != ConnectionState::Connected)
{
Response resp;
resp.Code = NETWORK_ERROR;
resp.Msg = "MES connection not established";
return resp;
}
}
Response resp;
if (!m_pConnection)
{
resp.Code = NETWORK_ERROR;
resp.Msg = "Not connected to server. Call Connect() first.";
return resp;
}
try {
auto deleter = [](CHttpFile* p) { if (p) delete p; };
std::unique_ptr<CHttpFile, decltype(deleter)> pFile(
m_pConnection->OpenRequest(CHttpConnection::HTTP_VERB_POST,
m_GFunction.String2CString(apiPath)), deleter);
// 设置请求头
CString strHeaders = _T("Content-Type: application/json\r\n");
strHeaders += _T("Authorization: Basic ") +
EncodeBasicAuth(m_GFunction.String2CString(m_username),
m_GFunction.String2CString(m_password)) + _T("\r\n");
// 转换请求数据
CString strData = m_GFunction.String2CString(info);
pFile->SendRequest
(
strHeaders,
(DWORD)strHeaders.GetLength(),
(LPVOID)(LPCTSTR)strData,
(DWORD)strData.GetLength()
);
DWORD dwStatusCode;
pFile->QueryInfoStatusCode(dwStatusCode);
CString strResponse;
if (dwStatusCode == HTTP_STATUS_OK)
{
TCHAR szBuffer[1024];
while (pFile->Read(szBuffer, 1023) > 0)
{
strResponse += szBuffer;
}
}
if (dwStatusCode != HTTP_STATUS_OK)
{
resp.Code = HTTP_ERROR;
resp.Msg = QString::number(dwStatusCode);
return resp;
}
resp.Msg = QString::fromLocal8Bit(m_GFunction.CString2String(strResponse).c_str());
pFile->Close();
}
catch (CInternetException* pEx)
{
TCHAR szError[1024];
pEx->GetErrorMessage(szError, 1024);
resp.Msg = QString::fromLocal8Bit(m_GFunction.CString2String(szError).c_str());// 异常信息写入resultMSG
pEx->Delete();
resp.Code = NETWORK_ERROR;
return resp;
}
resp.Code = SUCCESS;
return resp;
}
// 添加连接状态查询方法(公共接口)
ConnectionState MESInterface::GetConnectionState() const
{
return m_connectionState;
}
Response MESInterface::ParseRcv(string & msg)
{
QJsonDocument jsonDoc = QJsonDocument::fromJson(QByteArray::fromStdString(msg));
QJsonObject j = jsonDoc.object();
return Response::fromJson(j);
}
CString MESInterface::EncodeBasicAuth(const CString& username, const CString& password)
{
// 拼接用户名:密码
CString credentials = username + _T(":") + password;
// 使用 Qt 转换宽字符 CString 到 QByteArray(UTF-8)
QString qCredentials = QString::fromWCharArray(credentials.GetString());
QByteArray utf8Credentials = qCredentials.toUtf8();
// Base64 编码
QByteArray base64Data = utf8Credentials.toBase64();
// 将 Base64 数据转为 CString 返回
return CString(base64Data.constData());
}
int MESInterface::checkFolder()
{
CriticalSectionLock lock(m_cs); // ✅ 加锁
m_currentFileNames.clear();
QString m_sourceFolder = QString::fromLocal8Bit(m_GVariant.m_MesConfigFile->Path_SourceTestFile.c_str()) + "\\MES" + QDateTime::currentDateTime().addDays(-1).toString("yyyyMMdd");
m_destinationFolder = QString::fromLocal8Bit(m_GVariant.m_MesConfigFile->Path_DestTestFile.c_str()) + "\\" + QDateTime::currentDateTime().addDays(-1).toString("yyyyMMdd");
bool result1 = m_File.moveAllFiles(m_sourceFolder, m_destinationFolder, m_currentFileNames);
m_sourceFolder = QString::fromLocal8Bit(m_GVariant.m_MesConfigFile->Path_SourceTestFile.c_str()) + "\\MES" + QDateTime::currentDateTime().toString("yyyyMMdd");
m_destinationFolder = QString::fromLocal8Bit(m_GVariant.m_MesConfigFile->Path_DestTestFile.c_str()) + "\\" + QDateTime::currentDateTime().toString("yyyyMMdd");
bool result2 = m_File.moveAllFiles(m_sourceFolder, m_destinationFolder, m_currentFileNames);
if (result1 || result2)
{
if (m_currentFileNames.size() > 0)
{
for (auto item : m_currentFileNames)
{
m_bufferFileNames.append(item);
}
return 1;
}
}
else
{
return -1;
}
return 0;
}
外部多线程调用这个类用来上传MES数据,过线检查和上传设备状态等事件,是否有啥问题