|
|
@@ -29,6 +29,9 @@ void CPosPrinterWorker::Start()
|
|
|
{
|
|
|
m_is_work = true;
|
|
|
|
|
|
+ //建立收银打印端口通道,句柄常驻打开,不再每张票重复开关端口
|
|
|
+ StartShouyinChannels();
|
|
|
+
|
|
|
//处理收银打印
|
|
|
std::thread(&CPosPrinterWorker::HandleShouyinPrinter, this).detach();
|
|
|
|
|
|
@@ -42,6 +45,274 @@ void CPosPrinterWorker::Start()
|
|
|
void CPosPrinterWorker::Stop()
|
|
|
{
|
|
|
m_is_work = false;
|
|
|
+
|
|
|
+ StopShouyinChannels();
|
|
|
+}
|
|
|
+
|
|
|
+void CPosPrinterWorker::RefreshShouyinChannels()
|
|
|
+{
|
|
|
+ //设置变更后重建通道,若打印线程正在等待最慢端口,最多等一个超时周期
|
|
|
+ std::lock_guard<std::mutex> lock(m_shouyin_channels_mutex);
|
|
|
+ StartShouyinChannels();
|
|
|
+}
|
|
|
+
|
|
|
+void CPosPrinterWorker::StartShouyinChannels()
|
|
|
+{
|
|
|
+ StopShouyinChannels();
|
|
|
+
|
|
|
+ std::string printer_leixing = CSetting::GetInstance()->GetParam("setting_printer_leixing");
|
|
|
+
|
|
|
+ //网口不走句柄通道
|
|
|
+ if (printer_leixing == "wangkou")
|
|
|
+ {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ //收集本次要打开的端口路径及其类型
|
|
|
+ std::vector<std::pair<std::wstring, PrinterPortKind>> ports;
|
|
|
+
|
|
|
+ if (printer_leixing == "auto")
|
|
|
+ {
|
|
|
+ //自动模式:同时检测usb、并口、串口
|
|
|
+ m_usb_devices.clear();
|
|
|
+ int nDevice = GetDevicePath((LPGUID)&USB_GUID, m_usb_devices);
|
|
|
+ LOG_INFO("可用的USB打印机数量:" << nDevice);
|
|
|
+
|
|
|
+ std::string setting_biaoqian_printer_usb = CSetting::GetInstance()->GetParam("setting_biaoqian_printer_usb");
|
|
|
+ std::wstring ws_setting_biaoqian_printer_usb = CLewaimaiString::UTF8ToUnicode(setting_biaoqian_printer_usb);
|
|
|
+ bool exclude_biaoqian = (CSetting::GetInstance()->GetParam("setting_is_new_waimai_biaoqian_printer") == "1");
|
|
|
+
|
|
|
+ for (int i = 0; i < nDevice; i++)
|
|
|
+ {
|
|
|
+ if (exclude_biaoqian && ws_setting_biaoqian_printer_usb == m_usb_devices[i])
|
|
|
+ {
|
|
|
+ //设置中心明确设置的标签打印机,过滤掉
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ ports.push_back(std::make_pair(m_usb_devices[i], PrinterPortKind::Usb));
|
|
|
+ }
|
|
|
+
|
|
|
+ m_usb_devices.clear();
|
|
|
+
|
|
|
+ ports.push_back(std::make_pair(L"lpt1", PrinterPortKind::Bingkou));
|
|
|
+
|
|
|
+ CComHelper helper;
|
|
|
+ std::vector<std::wstring> comVector = helper.getComPort();
|
|
|
+ for (std::vector<std::wstring>::iterator it = comVector.begin(); it != comVector.end(); it++)
|
|
|
+ {
|
|
|
+ ports.push_back(std::make_pair(*it, PrinterPortKind::Com));
|
|
|
+ }
|
|
|
+ }
|
|
|
+ else if (printer_leixing == "usb")
|
|
|
+ {
|
|
|
+ std::string printer_usb = CSetting::GetInstance()->GetParam("setting_printer_usb");
|
|
|
+ std::wstring ws_printer_usb = CLewaimaiString::UTF8ToUnicode(printer_usb);
|
|
|
+ if (!ws_printer_usb.empty())
|
|
|
+ {
|
|
|
+ ports.push_back(std::make_pair(ws_printer_usb, PrinterPortKind::Usb));
|
|
|
+ }
|
|
|
+ }
|
|
|
+ else if (printer_leixing == "bingkou")
|
|
|
+ {
|
|
|
+ ports.push_back(std::make_pair(L"lpt1", PrinterPortKind::Bingkou));
|
|
|
+ }
|
|
|
+ else if (printer_leixing == "chuankou")
|
|
|
+ {
|
|
|
+ std::string printer_com = CSetting::GetInstance()->GetParam("setting_printer_com");
|
|
|
+ std::wstring ws_printer_com = CLewaimaiString::UTF8ToUnicode(printer_com);
|
|
|
+ if (!ws_printer_com.empty())
|
|
|
+ {
|
|
|
+ ports.push_back(std::make_pair(ws_printer_com, PrinterPortKind::Com));
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ //每个端口一个通道,打开失败的端口也保留通道,等待热插拔重试
|
|
|
+ for (std::vector<std::pair<std::wstring, PrinterPortKind>>::iterator it = ports.begin(); it != ports.end(); it++)
|
|
|
+ {
|
|
|
+ std::shared_ptr<CPortSendChannel> ch = std::make_shared<CPortSendChannel>();
|
|
|
+ ch->portPath = it->first;
|
|
|
+ ch->portKind = it->second;
|
|
|
+
|
|
|
+ OpenChannelHandle(ch);
|
|
|
+
|
|
|
+ ch->worker = std::thread(&CPosPrinterWorker::ChannelWorker, this, ch);
|
|
|
+ m_shouyin_channels.push_back(ch);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+void CPosPrinterWorker::StopShouyinChannels()
|
|
|
+{
|
|
|
+ for (std::vector<std::shared_ptr<CPortSendChannel>>::iterator it = m_shouyin_channels.begin(); it != m_shouyin_channels.end(); it++)
|
|
|
+ {
|
|
|
+ std::shared_ptr<CPortSendChannel> ch = *it;
|
|
|
+ {
|
|
|
+ std::lock_guard<std::mutex> lock(ch->mtx);
|
|
|
+ ch->stop = true;
|
|
|
+ }
|
|
|
+ ch->cv.notify_one();
|
|
|
+ if (ch->worker.joinable())
|
|
|
+ {
|
|
|
+ ch->worker.join();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ m_shouyin_channels.clear();
|
|
|
+}
|
|
|
+
|
|
|
+void CPosPrinterWorker::ChannelWorker(std::shared_ptr<CPortSendChannel> ch)
|
|
|
+{
|
|
|
+ while (true)
|
|
|
+ {
|
|
|
+ std::string data;
|
|
|
+ {
|
|
|
+ std::unique_lock<std::mutex> lock(ch->mtx);
|
|
|
+ ch->cv.wait(lock, [&ch] { return ch->stop || !ch->queue.empty(); });
|
|
|
+ if (ch->stop)
|
|
|
+ {
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ data = ch->queue.front();
|
|
|
+ ch->queue.pop();
|
|
|
+ }
|
|
|
+
|
|
|
+ //句柄未打开(设备拔掉或被占用),尝试重新打开
|
|
|
+ if (ch->hPort == NULL)
|
|
|
+ {
|
|
|
+ if (!OpenChannelHandle(ch))
|
|
|
+ {
|
|
|
+ LOG_INFO("端口暂不可用,丢弃本次打印数据,Path = " << ch->portPath.c_str());
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ //慢端口超时只卡本通道的线程,不影响其他端口
|
|
|
+ DWORD dwWrite = 0;
|
|
|
+ bool ret = WriteWithTimeout(ch->hPort, data.c_str(), (DWORD)data.length(), 5000, dwWrite);
|
|
|
+
|
|
|
+ if (!ret)
|
|
|
+ {
|
|
|
+ //写失败可能是usb热插拔,关闭旧句柄后按路径重开一次再重试
|
|
|
+ LOG_INFO("端口写入失败,尝试重开句柄,hPort:" << ch->hPort << ", Path = " << ch->portPath.c_str());
|
|
|
+
|
|
|
+ CloseHandle(ch->hPort);
|
|
|
+ ch->hPort = NULL;
|
|
|
+
|
|
|
+ if (OpenChannelHandle(ch))
|
|
|
+ {
|
|
|
+ dwWrite = 0;
|
|
|
+ ret = WriteWithTimeout(ch->hPort, data.c_str(), (DWORD)data.length(), 5000, dwWrite);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!ret || dwWrite != (DWORD)data.length())
|
|
|
+ {
|
|
|
+ LOG_INFO("端口写入失败,hPort:" << ch->hPort << ", dwWrite:" << dwWrite << ", Path = " << ch->portPath.c_str());
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ LOG_INFO("Write Success, hPort:" << ch->hPort << ", dwWrite:" << dwWrite);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (ch->hPort != NULL)
|
|
|
+ {
|
|
|
+ CloseHandle(ch->hPort);
|
|
|
+ ch->hPort = NULL;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+bool CPosPrinterWorker::OpenChannelHandle(std::shared_ptr<CPortSendChannel> ch)
|
|
|
+{
|
|
|
+ HANDLE hPort = NULL;
|
|
|
+
|
|
|
+ if (ch->portKind == PrinterPortKind::Com)
|
|
|
+ {
|
|
|
+ hPort = OpenComPort(ch->portPath);
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ hPort = CreateFile(ch->portPath.c_str(), GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, NULL);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (hPort == INVALID_HANDLE_VALUE || hPort == NULL)
|
|
|
+ {
|
|
|
+ DWORD error = GetLastError();
|
|
|
+
|
|
|
+ if (error == 2)
|
|
|
+ {
|
|
|
+ //没有指定的文件
|
|
|
+ LOG_INFO("没有找到对应的端口,Path = " << ch->portPath.c_str());
|
|
|
+ }
|
|
|
+ else if (error == 5)
|
|
|
+ {
|
|
|
+ LOG_INFO("端口被占用,Path = " << ch->portPath.c_str());
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ LOG_INFO("打开端口失败,Path = " << ch->portPath.c_str() << ", error = " << error);
|
|
|
+ }
|
|
|
+
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ ch->hPort = hPort;
|
|
|
+ LOG_INFO("打开端口成功,hPort:" << hPort << ", Path = " << ch->portPath.c_str());
|
|
|
+
|
|
|
+ return true;
|
|
|
+}
|
|
|
+
|
|
|
+HANDLE CPosPrinterWorker::OpenComPort(const std::wstring& com_path)
|
|
|
+{
|
|
|
+ HANDLE hPort = CreateFile(com_path.c_str(), GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, NULL);
|
|
|
+
|
|
|
+ if (hPort == INVALID_HANDLE_VALUE)
|
|
|
+ {
|
|
|
+ DWORD error = GetLastError();
|
|
|
+
|
|
|
+ if (error == 2)
|
|
|
+ {
|
|
|
+ //没有指定的文件
|
|
|
+ LOG_INFO("没有找到对应的串口 " << com_path.c_str());
|
|
|
+ }
|
|
|
+ else if (error == 5)
|
|
|
+ {
|
|
|
+ LOG_INFO("串口被占用 " << com_path.c_str());
|
|
|
+ }
|
|
|
+
|
|
|
+ return NULL;
|
|
|
+ }
|
|
|
+
|
|
|
+ LOG_INFO(com_path.c_str() << " success, handle:" << hPort);
|
|
|
+
|
|
|
+ //佳博的串口打印机,默认是这个波特率
|
|
|
+ DCB dcb;
|
|
|
+ dcb.DCBlength = sizeof(dcb);
|
|
|
+ GetCommState(hPort, &dcb);
|
|
|
+ dcb.BaudRate = 19200;
|
|
|
+
|
|
|
+ if (!SetCommState(hPort, &dcb))
|
|
|
+ {
|
|
|
+ LOG_INFO("set baudRate failed!");
|
|
|
+ CloseHandle(hPort);
|
|
|
+ return NULL;
|
|
|
+ }
|
|
|
+
|
|
|
+ //设定通讯端口超时参数
|
|
|
+ COMMTIMEOUTS tmouts;
|
|
|
+ tmouts.ReadIntervalTimeout = 100;
|
|
|
+ tmouts.ReadTotalTimeoutMultiplier = 100;
|
|
|
+ tmouts.ReadTotalTimeoutConstant = 100;
|
|
|
+ tmouts.WriteTotalTimeoutConstant = 100;
|
|
|
+ tmouts.WriteTotalTimeoutMultiplier = 100;
|
|
|
+ SetCommTimeouts(hPort, &tmouts);
|
|
|
+
|
|
|
+ //设置端口缓冲
|
|
|
+ SetupComm(hPort, 1024, 1024);
|
|
|
+
|
|
|
+ //清除通讯端口缓存
|
|
|
+ PurgeComm(hPort, PURGE_TXCLEAR | PURGE_RXCLEAR | PURGE_TXABORT | PURGE_RXABORT);
|
|
|
+
|
|
|
+ return hPort;
|
|
|
}
|
|
|
|
|
|
void CPosPrinterWorker::AddShouyinPrinter(std::string chufang_data)
|
|
|
@@ -176,43 +447,7 @@ void CPosPrinterWorker::SendDataToShouyinPirnter(std::string data)
|
|
|
|
|
|
std::string printer_leixing = CSetting::GetInstance()->GetParam("setting_printer_leixing");
|
|
|
|
|
|
- if (printer_leixing == "auto")
|
|
|
- {
|
|
|
- InitUsb();
|
|
|
-
|
|
|
- InitBingkou();
|
|
|
-
|
|
|
- InitCom();
|
|
|
-
|
|
|
- if (m_hShouyinPorts.size() == 0)
|
|
|
- {
|
|
|
- return;
|
|
|
- }
|
|
|
- }
|
|
|
- else if (printer_leixing == "usb")
|
|
|
- {
|
|
|
- std::string printer_usb = CSetting::GetInstance()->GetParam("setting_printer_usb");
|
|
|
- std::wstring ws_printer_usb = CLewaimaiString::UTF8ToUnicode(printer_usb);
|
|
|
-
|
|
|
- bool ret = InitOneUsb(ws_printer_usb, 1);
|
|
|
- if (ret == false)
|
|
|
- {
|
|
|
- return;
|
|
|
- }
|
|
|
- }
|
|
|
- else if (printer_leixing == "bingkou")
|
|
|
- {
|
|
|
- bool ret = InitBingkou();
|
|
|
- if (ret == false)
|
|
|
- {
|
|
|
- return;
|
|
|
- }
|
|
|
- }
|
|
|
- else if (printer_leixing == "chuankou")
|
|
|
- {
|
|
|
- InitCom();
|
|
|
- }
|
|
|
- else if (printer_leixing == "wangkou")
|
|
|
+ if (printer_leixing == "wangkou")
|
|
|
{
|
|
|
//改为网口模式
|
|
|
nType = 2;
|
|
|
@@ -238,20 +473,20 @@ void CPosPrinterWorker::SendDataToShouyinPirnter(std::string data)
|
|
|
//开始发送数据
|
|
|
if (nType == 1)
|
|
|
{
|
|
|
- //USB、串口、并口,走这里
|
|
|
- for (std::vector<HANDLE>::iterator it = m_hShouyinPorts.begin(); it != m_hShouyinPorts.end(); it++)
|
|
|
- {
|
|
|
- HANDLE hPort = *it;
|
|
|
+ //USB、串口、并口走通道非阻塞分发,慢端口在自己的线程里超时,不影响其他端口和下一轮打印
|
|
|
+ std::lock_guard<std::mutex> lock(m_shouyin_channels_mutex);
|
|
|
|
|
|
- //同步写数据
|
|
|
- DWORD dwWrite = 0;
|
|
|
- bool ret = WriteWithTimeout(hPort, data.c_str(), (DWORD)data.length(), 5000, dwWrite);
|
|
|
- if (!ret || dwWrite != (DWORD)data.length())
|
|
|
+ for (std::vector<std::shared_ptr<CPortSendChannel>>::iterator it = m_shouyin_channels.begin(); it != m_shouyin_channels.end(); it++)
|
|
|
+ {
|
|
|
+ std::shared_ptr<CPortSendChannel> ch = *it;
|
|
|
{
|
|
|
- LOG_INFO("WriteWithTimeout Failed or partial write!, hPort:" << hPort << ", dwWrite:" << dwWrite);
|
|
|
- continue;
|
|
|
+ std::lock_guard<std::mutex> ch_lock(ch->mtx);
|
|
|
+ ch->queue.push(data);
|
|
|
}
|
|
|
+ ch->cv.notify_one();
|
|
|
}
|
|
|
+
|
|
|
+ return;
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
@@ -266,15 +501,6 @@ void CPosPrinterWorker::SendDataToShouyinPirnter(std::string data)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- //关闭设备
|
|
|
- for (std::vector<HANDLE>::iterator it = m_hShouyinPorts.begin(); it != m_hShouyinPorts.end(); it++)
|
|
|
- {
|
|
|
- CloseHandle(*it);
|
|
|
- }
|
|
|
-
|
|
|
- //删除之前旧的句柄
|
|
|
- m_hShouyinPorts.clear();
|
|
|
-
|
|
|
if (nType == 2)
|
|
|
{
|
|
|
m_socket.close();
|
|
|
@@ -560,59 +786,14 @@ void CPosPrinterWorker::InitCom()
|
|
|
{
|
|
|
std::wstring com2Str = *it;
|
|
|
|
|
|
- HANDLE hPort = CreateFile(com2Str.c_str(), GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, NULL);
|
|
|
+ HANDLE hPort = OpenComPort(com2Str);
|
|
|
|
|
|
- if (hPort == INVALID_HANDLE_VALUE)
|
|
|
+ if (hPort == NULL)
|
|
|
{
|
|
|
- DWORD error = GetLastError();
|
|
|
-
|
|
|
- if (error == 2)
|
|
|
- {
|
|
|
- //没有指定的文件
|
|
|
- LOG_INFO("没有找到对应的串口 " << com2Str.c_str());
|
|
|
- }
|
|
|
- else if (error == 5)
|
|
|
- {
|
|
|
- LOG_INFO("串口被占用 " << com2Str.c_str());
|
|
|
- }
|
|
|
+ continue;
|
|
|
}
|
|
|
- else
|
|
|
- {
|
|
|
- LOG_INFO(com2Str.c_str() << " success, handle:" << hPort);
|
|
|
-
|
|
|
- //扎到了可用的串口,设置串口波特率
|
|
|
- DCB dcb;
|
|
|
- dcb.DCBlength = sizeof(dcb);
|
|
|
- GetCommState(hPort, &dcb);
|
|
|
-
|
|
|
- //佳博的串口打印机,默认是这个波特率
|
|
|
- dcb.BaudRate = 19200;
|
|
|
-
|
|
|
- if (!SetCommState(hPort, &dcb))
|
|
|
- {
|
|
|
- LOG_INFO("set baudRate failed!");
|
|
|
-
|
|
|
- CloseHandle(hPort);
|
|
|
- continue;
|
|
|
- }
|
|
|
-
|
|
|
- //设定通讯端口超时参数
|
|
|
- COMMTIMEOUTS tmouts;
|
|
|
- tmouts.ReadIntervalTimeout = 100;
|
|
|
- tmouts.ReadTotalTimeoutMultiplier = 100;
|
|
|
- tmouts.ReadTotalTimeoutConstant = 100;
|
|
|
- tmouts.WriteTotalTimeoutConstant = 100;
|
|
|
- tmouts.WriteTotalTimeoutMultiplier = 100;
|
|
|
- SetCommTimeouts(hPort, &tmouts);
|
|
|
-
|
|
|
- //设置端口缓冲
|
|
|
- SetupComm(hPort, 1024, 1024);
|
|
|
|
|
|
- //清除通讯端口缓存
|
|
|
- PurgeComm(hPort, PURGE_TXCLEAR | PURGE_RXCLEAR | PURGE_TXABORT | PURGE_RXABORT);
|
|
|
-
|
|
|
- m_hShouyinPorts.push_back(hPort);
|
|
|
- }
|
|
|
+ m_hShouyinPorts.push_back(hPort);
|
|
|
}
|
|
|
}
|
|
|
|