You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1030 lines
34 KiB
C++

This file contains invisible Unicode characters!

This file contains invisible Unicode characters that may be processed differently from what appears below. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to reveal hidden characters.

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

#include <iostream>
#include <iomanip>
#include <unistd.h>
#include <sys/ioctl.h>
#include <fstream>
#include "MsgHandler.hpp"
#include "base.h"
#include "strcode.h"
MsgHandler::MsgHandler() : fd(-1) {}
bool MsgHandler::OpenPort(const std::string& port, const std::string& baudrate)
{
return true;
}
void MsgHandler::ClosePort()
{
if (fd != -1)
{
close(fd);
fd = -1;
}
}
bool MsgHandler::SetDevice(const std::string& device)
{
this->device = device;
return true;
}
int MsgHandler::SendDeviceMsg(const std::vector<uint8_t>& data)
{
std::cout << "write to " << this->device << std::endl;
for (int num : data) {
std::cout << std::hex << std::setw(2) << std::setfill('0')
<< num << " ";
}
std::cout << std::endl;
int bytesWritten = SendMsg(data);
return bytesWritten;
}
int MsgHandler::RecvDeviceMsg(std::vector<uint8_t>& data, int timeoutMs)
{
int bytesRead = RecvMsg(data, timeoutMs);
if (bytesRead > 0)
{
std::cout << "read from " << this->device << std::endl;
for (int num : data)
{
std::cout << std::hex << std::setw(2) << std::setfill('0')
<< num << " ";
}
std::cout << std::endl;
}
return bytesRead;
}
int MsgHandler::SendMsg(const std::vector<uint8_t>& data)
{
if (fd == -1)
{
perror("设备未打开");
return -1;
}
// int bytesWritten = write(fd, data.data(), data.size());
// if (bytesWritten < 0)
// {
// perror("发送数据失败");
// }
// return bytesWritten;
// 大块数据分块发送,防止 57600 波特下打印机 UART FIFO 溢出
// 阈值选 512单行/单字段都 < 512 字节,走单次 write
// 超过阈值(就是 12800 字节的章图栅格)按 256 字节切,段间等 3ms。
constexpr size_t CHUNK_THRESHOLD = 512;
constexpr size_t CHUNK_SIZE = 256;
constexpr unsigned int INTER_US = 50000;
if (data.size() < CHUNK_THRESHOLD)
{
int bytesWritten = write(fd, data.data(), data.size());
if (bytesWritten < 0) perror("发送数据失败");
return bytesWritten;
}
size_t totalWritten = 0;
size_t pos = 0;
while (pos < data.size())
{
size_t end = std::min(pos + CHUNK_SIZE, data.size());
ssize_t n = write(fd, data.data() + pos, end - pos);
if (n < 0)
{
perror("发送数据失败");
return -1;
}
totalWritten += static_cast<size_t>(n);
pos = end;
if (pos < data.size()) usleep(INTER_US);
}
return static_cast<int>(totalWritten);
}
int MsgHandler::RecvMsg(std::vector<uint8_t>& data, int timeoutMs)
{
if (fd == -1)
{
perror("设备未打开\n");
return -1;
}
// 使用select实现超时
fd_set readfds;
struct timeval tv;
FD_ZERO(&readfds);
FD_SET(fd, &readfds);
tv.tv_sec = timeoutMs / 1000;
tv.tv_usec = (timeoutMs % 1000) * 1000;
int ret = select(fd + 1, &readfds, NULL, NULL, &tv);
if (ret == -1)
{
perror("select错误");
return -1;
}
else if (ret == 0)
{
//无数据可读
return 0;
}
// 有数据可读
uint8_t bytes;
if (ioctl(fd, FIONREAD, &bytes) < 0)
{
perror("ioctl FIONREAD失败");
return -1;
}
data.resize(bytes);
int bytesRead = read(fd, data.data(), bytes);
if (bytesRead < 0)
{
perror("读取数据失败");
return -1;
}
return bytesRead;
}
// 辅助函数:把 std::vector<uint8_t> 的子区间提取出来
static std::vector<uint8_t> make_range(const std::vector<uint8_t>& src,
size_t begin, size_t end)
{
return std::vector<uint8_t>(src.begin() + begin, src.begin() + end);
}
// 分段发送栅格数据,防止 57600 波特下 UART FIFO 溢出
// chunkSize每段字节数建议 256~51257600 下 256 字节传输耗时≈4.5ms,留 3ms 给打印机消化)
// interChunkUs段间等待微秒数建议 3000~50003~5ms
void MsgHandler::SendDeviceMsgChunked(const std::vector<uint8_t>& data,
size_t chunkSize,
unsigned int interChunkUs)
{
size_t pos = 0;
while (pos < data.size())
{
size_t end = std::min(pos + chunkSize, data.size());
std::vector<uint8_t> chunk = make_range(data, pos, end);
SendDeviceMsg(chunk);
if (end < data.size())
{ // 不是最后一段,才需要等
usleep(interChunkUs);
}
pos = end;
}
}
void MsgHandler::HandleDdsMsg(const std::map<std::string, std::string>& msg)
{
// 添加调试代码
std::cout << "[Print] ====== HandleDdsMsg 收到的 map 内容 ======" << std::endl;
for (const auto& pair : msg)
{
std::cout << "[Print] key=[" << pair.first << "] value=[" << pair.second << "]" << std::endl;
}
std::cout << "[Print] ==========================================" << std::endl;
EncodeMsg(msg);
SendDeviceMsg(m_PrintData);
sleep(2);
//SendDeviceMsg(m_PrintData); // 第 2 次
sleep(4);
//SendDeviceMsg(m_PrintData); // 第 3 次
}
void MsgHandler::EncodeMsg(const std::map<std::string, std::string>& msg)
{
if(device == "NP80A")
{
auto it = msg.find("Title");
std::cout << "[Print] Title in msg: [" << (it != msg.end() ? it->second : "NOT FOUND") << "]" << std::endl;
if(it != msg.end())
{
std::cout << "[Print] comparing: [" << it->second << "] == [贵州渝黔锰业有限公司提货单] ?" << std::endl;
if(it->second == "贵州渝黔锰业有限公司提货单")
{
std::cout << "[Print] ==== 命中贵州渝黔锰业有限公司提货单条件调用Print1_1 ====" << std::endl;
Print1_1(msg);
std::cout << "[Print] DEBUG: Print1_1 done, m_PrintData.size() = " << m_PrintData.size() << std::endl;
}
}
}
}
//行线条生成器
std::vector<char> MsgHandler::BuildLine(int m_max, const std::map<int, std::string>& PointList, int no_line_min, int no_line_max)
{
std::vector<char> m_ba;
for (int i=1;i<m_max;i++)
{
auto it = PointList.find(i);
std::string szTemp;
if (it != PointList.end())
{
szTemp=ot::UTF8ToGB2312(it->second.c_str());
}
else
{
if(i <= no_line_min || i >= no_line_max){
szTemp = ot::UTF8ToGB2312("");
}
else {
szTemp = ot::UTF8ToGB2312(" ");
}
}
m_ba.insert(m_ba.end(), szTemp.begin(), szTemp.end());
}
return m_ba;
}
//行线条生成器
std::vector<char> MsgHandler::BuildStringLine(std::map<int, std::string>& PointList)
{
std::vector<char> m_ba;
for (auto &p : PointList)
{
char s[4]={0};
memcpy(s,&(p.first),2);
std::string szTemp;
szTemp=ot::UTF8ToGB2312(p.second.c_str());
//设置横向绝对位置
m_ba.insert(m_ba.end(), {0x1B, 0x24});
m_ba.insert(m_ba.end(), s, s+2);
m_ba.insert(m_ba.end(), szTemp.begin(), szTemp.end());
}
return m_ba;
}
//行线条生成器
std::vector<char> MsgHandler::BuildStringLine_HSK33(std::map<int, std::string>& PointList)
{
std::vector<char> m_ba;
for (auto &p : PointList)
{
std::string szTemp;
szTemp=ot::UTF8ToGB2312(p.second.c_str());
m_ba.insert(m_ba.end(), szTemp.begin(), szTemp.end());
}
return m_ba;
}
// 行内容生成器(不做任何编码转换,直接按字节写入)
// 适用于标签是已编码好的 GB2312 字节串场景Print1_1 打印标题/字段用)
std::vector<char> MsgHandler::BuildStringLine_RawBytes(const std::map<int, std::string>& PointList)
{
std::vector<char> m_ba;
for (const auto &p : PointList)
{
m_ba.insert(m_ba.end(), p.second.begin(), p.second.end());
}
return m_ba;
}
//打印机
//清空 并设置纸大小
void MsgHandler::Page_ClearAndSet(std::vector<uint8_t>& Data, char * h1h2h3h4)
{
char sbuf[8] = {0x1B, 'D', h1h2h3h4[0], h1h2h3h4[1], h1h2h3h4[2], h1h2h3h4[3], 0x0A, 0x00};
//清空
Data.insert(Data.end(), {0x1B, 'C', 0x0A, 0x00});
//设置纸大小
Data.insert(Data.end(), std::begin(sbuf), std::end(sbuf));
}
//根据起点和结束点的坐标值定义指定宽度的直线/边框
/// 根据起点和结束点的坐标值定义指定宽度的直线/边框,并将此
/// 予编号。即定义线条的格式。
/// 本命令不可以定义斜线。
/// 有效的定义命令同时将在打印映像缓冲区中实现线条数据。映像
/// 命令将无法清除本命令定义的格式。
/// 清除本命令定义的格式应使用页面格式清除指令(ESC C)或线条
/// 令(ESC E)。
/// n1n2: 线条编号
/// x1x2x3x4: 起始点的X方向坐标单位 0.1mm毫米
/// y1y2y3y4: 起始点的Y方向坐标单位 0.1mm毫米
/// x5x6x7x8: 结束点的X方向坐标单位 0.1mm毫米
/// y5y6y7y8: 结束点的Y方向坐标单位 0.1mm毫米
/// d: 线条方向(根据打印机型号不同可定义值不同
/// 规范 A
/// d=48 (“0”) 水平线
/// d=49 (“1”) 垂直线
/// 规范 B
/// d=48 (“0”) 水平线
/// d=49 (“1”) 垂直线
/// d=50 (“2”) 边框
/// w: 以点为单位的线条宽度(1点 = 0.125mm毫米)
/// 水平线(d=”0”)应使用y方向的常数设置(即y1y2y3y4 = y5y
/// 垂直线(d=”1”)应使用x方向的常数设置(即x1x2x3x4 = x5x
/// 例如:当打印区域为水平方向104毫米垂直方向300毫米时
/// X方向 Y方向
/// x1x2x3x4,x5x6x7x8 y1y2y3y4,y5y6y7y8
/// 水平线设置范围“0000”到“1040” “0000”到“2999”
/// 垂直线设置范围“0000”到“1039” “0000”到“3000”
/// 方框可设置范围“0000”到“1040” “0000”到“3000”
void MsgHandler::Page_ESCL (std::vector<uint8_t>& Data, const char *n1n2, const char *x1x2x3x4, const char *y1y2y3y4,
const char *x5x6x7x8, const char *y5y6y7y8, char d, char w)
{
//打印命令
char cbuf[30];
cbuf[0] = 0x1B;
cbuf[1] = 'L';
memcpy(&cbuf[2],n1n2,2);
cbuf[4] = ';';
memcpy(&cbuf[5],x1x2x3x4,4);
cbuf[9] = ',';
memcpy(&cbuf[10],y1y2y3y4,4);
cbuf[14] = ',';
memcpy(&cbuf[15],x5x6x7x8,4);
cbuf[19] = ',';
memcpy(&cbuf[20],y5y6y7y8,4);
cbuf[24] = ',';
cbuf[25] = d;
cbuf[26] = ',';
cbuf[27] = w;
cbuf[28] = 0x0A;
cbuf[29] = 0x00;
Data.insert(Data.end(), std::begin(cbuf), std::end(cbuf));
}
//指定字符串的位置、字体、高度、宽度、字符间距以及字符和字符串
/// <summary>
/// 清除本命令定义的格式应使用页面格式清除指令(ESC C)。
/// 指定字符串的位置、字体、高度、宽度、字符间距以及字符和字符串
/// 的旋转方向,并将此定义赋予编号。即定义字符串的格式
/// n1n2: 字符串编号
/// x1x2x3x4: 字符串起始点的X方向坐标单位 0.1mm毫米
/// y1y2y3y4: 字符串起始点的Y方向坐标单位 0.1mm毫米
/// w: 每个字符的宽度倍数(可设置为1,2,3,4,5,6倍)
/// h: 每个字符的高度倍数(可设置为1,2,3,4,5,6倍)
/// c: 字符的字体定义(不同字体有不同的点尺寸)
/// 1: 小字体(8x16点)/2: 标准字体(16x24点)
/// 3: 汉字(12x24汉字模式字母和数字/24x24汉字
/// 4: 粗体(24x32点)
/// r1: 字符的旋转方向(0: 0°, 1: 90°, 2: 180°, 3: 270°)
/// r2: 字符串的旋转方向(0: 0°, 1: 90°, 2: 180°, 3: 270
/// d1d2: 字符间距(单位: 点/0.125mm毫米)
/// 本参数以及参数前的“,”可以忽略
/// 忽略本参数时使用“ESC Y”命令定义的间距值
/// s1s2: 字符修饰(最终修饰由s1和s2的值叠加)
/// s1=48(“0”)无修饰 s1=49(“1”)字符加粗
/// s2=48(“0”)无修饰 s2=49(“1”)下划线 s2=50(“2”)反白
/// a: 打印颜色(仅在打印机设置为双色打印时有效)
/// a=48(“0”) 黑色
/// a=49(“1”) 红色
/// 参数“,d1d2,s1s2,a”可忽略(如果忽略则设置为黑色,无字符修饰.使用 ESC Y
/// 设置字符间距)
/// 旋转是以顺时针方向围绕参照点为中心进行。
/// 字符放大(宽度和高度倍数放大)时字符间距不进行放大
/// 指定字体为汉字或光学识别字符(OCR)时国际字符集定义无效
/// 指定字体为粗体或光学识别字符(OCR)时日文假名无效(日文版本
/// 指定字体为汉字时根据打印机型号会有不同的处理方式(语言版本
void MsgHandler::Page_ESCPC(std::vector<uint8_t>& Data, const char *n1n2, const char *x1x2x3x4, const char *y1y2y3y4,
char w, char h, char c, const char * r1r2, const char * s1s2)
{
//命令
char cbuf[34];
cbuf[0] = 0x1B;
cbuf[1] = 'P';
cbuf[2] = 'C';
memcpy(&cbuf[3],n1n2,2);
cbuf[5] = ';';
memcpy(&cbuf[6],x1x2x3x4,4);
cbuf[10] = ',';
memcpy(&cbuf[11],y1y2y3y4,4);
cbuf[15] = ',';
cbuf[16] = w;
cbuf[17] = ',';
cbuf[18] = h;
cbuf[19] = ',';
cbuf[20] = c;
cbuf[21] = ',';
cbuf[22] = r1r2[0];
cbuf[23] = r1r2[1];
cbuf[24] = ',';
cbuf[25] = '0';
cbuf[26] = '2';
cbuf[27] = ',';
cbuf[28] = s1s2[0];
cbuf[29] = s1s2[1];
cbuf[30] = ',';
cbuf[31] = '0';
cbuf[32] = 0x0A;
cbuf[33] = 0x00;
Data.insert(Data.end(), std::begin(cbuf), std::end(cbuf));
}
//为使用字符串格式设置命令定义的字符串赋以内容。
/// <summary>
/// 为使用字符串格式设置命令定义的字符串赋以内容。
/// 新定义将覆盖以前输出映像缓冲区中的内容。
/// 因此可不重新定义格式仅重新定义字符串内容。旧的即以前定义的字
/// 符串内容将被删除,然后生成新字符串的内容映像。
/// 页面格式清除指令(ESC C)清除本命令定义的内容。
/// 清除输出映像缓冲区指令(ESC X)清除本命令定义的内容。
/// n1n2: 字符串编号
/// a1a2……an: 打印字符串内容(最多100个字符/50个汉字)
/// </summary>
/// <param name="n1n2">字符串编号</param>
/// <param name="str">打印字符串内容(最多100个字符/50个汉字)</param>
void MsgHandler::Page_ESCRC(std::vector<uint8_t>& Data, const char *n1n2, const char *str)
{
//命令
char cbuf[6] = {0x1B, 'R', 'C', n1n2[0], n1n2[1], ';'};
Data.insert(Data.end(), std::begin(cbuf), std::end(cbuf));
int len = strlen(str);
if (len >100)
len = 100;
int ct = 0;
for(int i=0;i<len;)
{
//发特定长度
ct = len - i;
if (ct>8)
ct = 8;
Data.insert(Data.end(), str+i, str+i+ct);
i+=ct;
}
Data.insert(Data.end(), {0x0A, 0x00});
}
/// 将位图文件(BMP文件)作为图像数据写入输出映像缓冲区。
/// 可选择覆盖(:冒号)/叠加(;分号)方式。
/// 页面格式清除指令(ESC C)清除本命令定义的内容。
/// 清除输出映像缓冲区指令(ESC X)清除本命令定义的内容。
/// x1x2x3x4: 位图打印起始点的X方向坐标单位 0.1mm毫米
/// y1y2y3y4: 位图打印起始点的Y方向坐标单位 0.1mm毫米
/// n: 位图写入的模式。
/// :(冒号)覆盖方式
/// ;(分号)叠加方式
/// BMP文件数据: BMP文件的所有内容。
/// 要求是黑白格式的非压缩BMP文件。因此并非所有的
/// BMP文件可以直接以此方式作为图像发送到打印机。
/// 图像最大为 64k
void MsgHandler::Page_ESCH(std::vector<uint8_t>& Data, char n, char *x1x2x3x4, char *y1y2y3y4, char *bmp, int bmpsize)
{
//命令
char cbuf[13];
cbuf[0] = 0x1B;
cbuf[1] = 'H';
cbuf[2] = n;
memcpy(&cbuf[3],x1x2x3x4,4);
cbuf[7] = ',';
memcpy(&cbuf[8],y1y2y3y4,4);
cbuf[12] = ',';
Data.insert(Data.end(), std::begin(cbuf), std::end(cbuf));
int ct = 0;
for(int i=0;i<bmpsize;)
{
//特发长度
ct = bmpsize - i;
if (ct>8)
ct = 8;
Data.insert(Data.end(), bmp+i, bmp+i+ct);
i+=ct;
}
Data.insert(Data.end(), {',' ,0x0A, 0x00});
}
//切纸
void MsgHandler::Page_Cut(std::vector<uint8_t>& Data)
{
//打印并出纸
Data.insert(Data.end(), {0x1B, 'B' ,0x0A, 0x00});
}
//结束
void MsgHandler::Page_End(std::vector<uint8_t>& Data)
{
//打印并出纸
Data.insert(Data.end(), {0x1B, 'I' ,0x0A, 0x00});
}
//获取长度字符串
void MsgHandler::GtstrLength(int iVal, char *p)
{
std::string szTemp = std::to_string(iVal);
szTemp = szTemp.substr(szTemp.length() - 4);
memcpy(p, szTemp.c_str(), 4);
}
// 判断是否为中文字符(包括中文标点)
bool MsgHandler::is_chinese_char(const std::string& str, size_t start_byte) {
if (start_byte >= str.length()) return false;
uint8_t c = static_cast<uint8_t>(str[start_byte]);
// ASCII字符不是中文
if (c < 0x80) return false;
// 获取Unicode码点
uint32_t code_point = 0;
int bytes = 0;
if ((c & 0xF8) == 0xF0 && start_byte + 3 < str.length()) {
code_point = ((c & 0x07) << 18) |
((static_cast<uint8_t>(str[start_byte+1]) & 0x3F) << 12) |
((static_cast<uint8_t>(str[start_byte+2]) & 0x3F) << 6) |
(static_cast<uint8_t>(str[start_byte+3]) & 0x3F);
bytes = 4;
}
else if ((c & 0xF0) == 0xE0 && start_byte + 2 < str.length()) {
code_point = ((c & 0x0F) << 12) |
((static_cast<uint8_t>(str[start_byte+1]) & 0x3F) << 6) |
(static_cast<uint8_t>(str[start_byte+2]) & 0x3F);
bytes = 3;
}
else if ((c & 0xE0) == 0xC0 && start_byte + 1 < str.length()) {
code_point = ((c & 0x1F) << 6) |
(static_cast<uint8_t>(str[start_byte+1]) & 0x3F);
bytes = 2;
}
if (bytes == 0) return false;
// 中文汉字范围
if ((code_point >= 0x4E00 && code_point <= 0x9FFF) ||
(code_point >= 0x3400 && code_point <= 0x4DBF) ||
(code_point >= 0x20000 && code_point <= 0x2A6DF) ||
(code_point >= 0x2A700 && code_point <= 0x2B73F) ||
(code_point >= 0x2B740 && code_point <= 0x2B81F) ||
(code_point >= 0x2B820 && code_point <= 0x2CEAF) ||
(code_point >= 0x2CEB0 && code_point <= 0x2EBEF) ||
(code_point >= 0x30000 && code_point <= 0x3134F)) {
return true;
}
// 中文标点范围
if ((code_point >= 0x3000 && code_point <= 0x303F) ||
(code_point >= 0xFF00 && code_point <= 0xFFEF)) {
return true;
}
return false;
}
// 获取单个字符的显示宽度
int MsgHandler::get_utf8_char_width(const std::string& str, size_t start_byte) {
if (start_byte >= str.length()) return 1;
uint8_t c = static_cast<uint8_t>(str[start_byte]);
// ASCII字符占1个位置
if (c < 0x80) {
return 1;
}
// 判断是否为中文字符
if (is_chinese_char(str, start_byte)) {
return 2; // 中文字符占2个位置
}
// 其他宽字符如全角字符、emoji等
return 2; // 默认按2个位置处理
}
// 获取整个字符串的显示宽度
size_t MsgHandler::get_utf8_display_width(const std::string& str) {
size_t width = 0;
for (size_t i = 0; i < str.length(); ) {
uint8_t c = static_cast<uint8_t>(str[i]);
size_t char_len = 1;
if ((c & 0x80) == 0x00) {
char_len = 1;
} else if ((c & 0xE0) == 0xC0) {
if (i + 1 >= str.length()) break;
char_len = 2;
} else if ((c & 0xF0) == 0xE0) {
if (i + 2 >= str.length()) break;
char_len = 3;
} else if ((c & 0xF8) == 0xF0) {
if (i + 3 >= str.length()) break;
char_len = 4;
}
width += get_utf8_char_width(str, i);
i += char_len;
}
return width;
}
// 按显示宽度分割字符串
std::vector<std::string> MsgHandler::split_utf8_by_width(const std::string& str, size_t max_width) {
std::vector<std::string> result;
if (str.empty() || max_width == 0) return result;
size_t byte_pos = 0;
while (byte_pos < str.length()) {
size_t line_start = byte_pos;
size_t line_width = 0;
size_t last_valid_pos = byte_pos;
// 收集一行的字符
while (byte_pos < str.length() && line_width < max_width) {
uint8_t c = static_cast<uint8_t>(str[byte_pos]);
size_t char_len = 1;
if ((c & 0x80) == 0x00) {
char_len = 1;
} else if ((c & 0xE0) == 0xC0) {
if (byte_pos + 1 >= str.length()) break;
char_len = 2;
} else if ((c & 0xF0) == 0xE0) {
if (byte_pos + 2 >= str.length()) break;
char_len = 3;
} else if ((c & 0xF8) == 0xF0) {
if (byte_pos + 3 >= str.length()) break;
char_len = 4;
}
int char_width = get_utf8_char_width(str, byte_pos);
// 检查是否还能放下这个字符
if (line_width + char_width > max_width) {
break;
}
last_valid_pos = byte_pos + char_len - 1;
byte_pos += char_len;
line_width += char_width;
}
// 确保至少有一个字符
if (last_valid_pos < line_start && byte_pos < str.length()) {
uint8_t c = static_cast<uint8_t>(str[byte_pos]);
size_t char_len = 1;
if ((c & 0xE0) == 0xC0) char_len = 2;
else if ((c & 0xF0) == 0xE0) char_len = 3;
else if ((c & 0xF8) == 0xF0) char_len = 4;
last_valid_pos = byte_pos + char_len - 1;
byte_pos = last_valid_pos + 1;
}
// 提取这一行
if (last_valid_pos >= line_start) {
result.push_back(str.substr(line_start, last_valid_pos - line_start + 1));
}
byte_pos = last_valid_pos + 1;
}
return result;
}
bool MsgHandler::check_odd(const std::string date)
{
if (!date.empty())
{
char lastChar = date.back(); // 获取最后一个字符
if (std::isdigit(lastChar))
{
int lastDigit = lastChar - '0'; // 将字符转换为数字
if (lastDigit % 2 == 0)
{
return false;
}
else
{
return true;
}
}
return false;
}
return false;
}
// ===== 新增:章图读取(与 PrintTest/main.c::load_stamp_image 一致) =====
// 把 hex ASCII 文本文件(如 "FF00A5...")转成二进制位图字节流
// 失败返回 falseout 被清空
bool MsgHandler::LoadStampHexImage(const std::string& path, std::vector<uint8_t>& out)
{
out.clear();
std::ifstream fp(path);
if (!fp.is_open()) {
std::cerr << "[Print] 无法打开章图文件: " << path << std::endl;
return false;
}
// 读取整个文件为字符串
std::string hex_str((std::istreambuf_iterator<char>(fp)),
std::istreambuf_iterator<char>());
fp.close();
// 转换 hex → binary跳过空白字符
out.reserve(hex_str.size() / 2);
size_t i = 0;
while (i < hex_str.size()) {
char c = hex_str[i];
if (c == ' ' || c == '\n' || c == '\r' || c == '\t') {
i++;
continue;
}
if (i + 1 >= hex_str.size()) break;
char hi = hex_str[i];
char lo = hex_str[i + 1];
auto hex_val = [](char ch) -> int {
if (ch >= '0' && ch <= '9') return ch - '0';
if (ch >= 'a' && ch <= 'f') return 10 + (ch - 'a');
if (ch >= 'A' && ch <= 'F') return 10 + (ch - 'A');
return -1;
};
int h = hex_val(hi);
int l = hex_val(lo);
if (h < 0 || l < 0) {
i++;
continue;
}
out.push_back(static_cast<uint8_t>((h << 4) | l));
i += 2;
}
if (out.empty()) {
std::cerr << "[Print] 章图文件为空或解析失败: " << path << std::endl;
return false;
}
return true;
}
// ===== 新增:章图最近邻放大(与 PrintTest/main.c::print_stamp_image 中 scale>1 部分一致) =====
bool MsgHandler::EnlargeStampNearest(const std::vector<uint8_t>& src,int src_w, int src_h, int scale,std::vector<uint8_t>& dst)
{
if (scale <= 1) {
dst = src;
return true;
}
int dst_w = src_w * scale;
int dst_h = src_h * scale;
int dst_bytes_per_line = (dst_w + 7) / 8;
int src_bytes_per_line = (src_w + 7) / 8;
dst.assign(dst_bytes_per_line * dst_h, 0);
for (int y = 0; y < src_h; y++) {
for (int x = 0; x < src_w; x++) {
int orig_byte = y * src_bytes_per_line + x / 8;
int orig_bit = 7 - (x % 8);
uint8_t pixel = (src[orig_byte] >> orig_bit) & 1;
if (!pixel) continue;
for (int dy = 0; dy < scale; dy++) {
for (int dx = 0; dx < scale; dx++) {
int nx = x * scale + dx;
int ny = y * scale + dy;
int new_byte = ny * dst_bytes_per_line + nx / 8;
int new_bit = 7 - (nx % 8);
dst[new_byte] |= (1 << new_bit);
}
}
}
}
return true;
}
//NP80A打印绥阳铁合金厂过磅单完全按 PrintTest/main.c 的字节流生成)
// 关键点:
// 1) 标签用原始 GB2312 字节直接 push绝不经过 BuildStringLine
// 2) 值用 UTF8ToGB2312 转码后直接 push
// 3) 章图改用 GS v 0光栅位图不再是 NV 位图 FS p
// 4) 走纸量改为 0x1117行让切纸位置与 PrintTest 一致
void MsgHandler::Print1_1(const std::map<std::string, std::string>& msg)
{
std::map<std::string, std::string>::const_iterator it;
m_PrintData.clear();
// 1. 初始化打印机
m_PrintData.insert(m_PrintData.end(), {0x1B, 0x40}); // ESC @ 复位
usleep(200 * 1000);
// 2. 设置中文模式
m_PrintData.insert(m_PrintData.end(), {0x1C, 0x21, 0x00}); // FS ! 0
usleep(100 * 1000);
// 3. 换行 4 次PrintTest 实际是 4 次,不是 6 次)
for (int i = 0; i < 4; i++) {
m_PrintData.insert(m_PrintData.end(), {0x0A});
}
usleep(50 * 1000);
// 4. 标题居中 + 倍高倍宽 + 加粗
m_PrintData.insert(m_PrintData.end(), {0x1B, 0x61, 0x01}); // ESC a 1 居中
m_PrintData.insert(m_PrintData.end(), {0x1D, 0x21, 0x11}); // GS ! 0x11 倍高倍宽
m_PrintData.insert(m_PrintData.end(), {0x1B, 0x45, 0x01}); // ESC E 1 加粗开
// 绥阳铁合金厂(原始 GB2312 字节6 个汉字)
const uint8_t title_factory[] = {
0xCB, 0xE7, // 绥
0xD1, 0xF4, // 阳
0xCC, 0xFA, // 铁
0xBA, 0xCF, // 合
0xBD, 0xF0, // 金
0xB3, 0xA7, // 厂
};
m_PrintData.insert(m_PrintData.end(), std::begin(title_factory), std::end(title_factory));
m_PrintData.insert(m_PrintData.end(), {0x0A, 0x0A});
// 过磅单(原始 GB2312 字节3 个汉字)
const uint8_t title_bill[] = {
0xB9, 0xFD, // 过
0xB0, 0xF5, // 磅
0xB5, 0xA5, // 单
};
m_PrintData.insert(m_PrintData.end(), std::begin(title_bill), std::end(title_bill));
m_PrintData.insert(m_PrintData.end(), {0x0A, 0x0A});
// 5. 恢复正常状态
m_PrintData.insert(m_PrintData.end(), {0x1B, 0x45, 0x00}); // 加粗关
m_PrintData.insert(m_PrintData.end(), {0x1D, 0x21, 0x00}); // 正常大小
m_PrintData.insert(m_PrintData.end(), {0x1B, 0x61, 0x00}); // 左对齐
//m_PrintData.insert(m_PrintData.end(), {0x1B, 0x20, 0x00}); // ← 新增:字符右侧间距 16 点≈2mm
usleep(100 * 1000);
// 6. 打印各项字段 —— 直接 push 字节,避免任何二次转码
//标签是 GB2312 字节串,值是 UTF8 → 转 GB2312 后 push
// 每行结尾追加0x0A 0x0A两次换行和 PrintTest 一致)
auto print_line = [&](const uint8_t* label_gb, size_t label_len, const std::string& key)
{
// it = msg.find(key);
// if (it == msg.end()) return;
auto it = msg.find(key);
//每次打印前设置左缩进ESC $ 32 点 = ≈2mm
m_PrintData.insert(m_PrintData.end(), {0x1B, 0x24, 0x20, 0x00});
// 标签
m_PrintData.insert(m_PrintData.end(), label_gb, label_gb + label_len);
// 值
bool has_value = (it != msg.end()) && !(it->second.empty());
if (has_value)
{
std::string val_gb = ot::UTF8ToGB2312(it->second.c_str());
m_PrintData.insert(m_PrintData.end(), val_gb.begin(), val_gb.end());
}
else if (key == "RockWeight")
{
// 扣重默认值 0.00
std::string val_gb = ot::UTF8ToGB2312("0.00");
m_PrintData.insert(m_PrintData.end(), val_gb.begin(), val_gb.end());
}
else if (key == "TrainNumber")
{
// 车船号默认值 0
std::string val_gb = ot::UTF8ToGB2312("0");
m_PrintData.insert(m_PrintData.end(), val_gb.begin(), val_gb.end());
}
// 两次换行
m_PrintData.insert(m_PrintData.end(), {0x0A, 0x0A});
};
// 票据号:
{ const uint8_t l[] = {0xC6,0xB1,0xBE,0xDD,0xBA,0xC5,0xA3,0xBA};
print_line(l, sizeof(l), "CoalNumber"); }
// 品种:
{ const uint8_t l[] = {0xC6,0xB7,0xD6,0xD6,0xA3,0xBA};
print_line(l, sizeof(l), "GoodsType"); }
// 订单号:
{ const uint8_t l[] = {0xB6,0xA9,0xB5,0xA5,0xBA,0xC5,0xA3,0xBA};
print_line(l, sizeof(l), "OrderNumber"); }
// 车船号:
{ const uint8_t l[] = {0xB3,0xB5,0xB4,0xAC,0xBA,0xC5,0xA3,0xBA};
print_line(l, sizeof(l), "TrainNumber"); }
// 车牌号:
{ const uint8_t l[] = {0xB3,0xB5,0xC5,0xC6,0xBA,0xC5,0xA3,0xBA};
print_line(l, sizeof(l), "CarNumber"); }
// 供货单位:
{ const uint8_t l[] = {0xB9,0xA9,0xBB,0xF5,0xB5,0xA5,0xCE,0xBB,0xA3,0xBA};
print_line(l, sizeof(l), "Supplier"); }
// 收货单位:
{ const uint8_t l[] = {0xCA,0xD5,0xBB,0xF5,0xB5,0xA5,0xCE,0xBB,0xA3,0xBA};
print_line(l, sizeof(l), "Customer"); }
// 运输单位:
{ const uint8_t l[] = {0xD4,0xCB,0xCA,0xE4,0xB5,0xA5,0xCE,0xBB,0xA3,0xBA};
print_line(l, sizeof(l), "Transporter"); }
// 毛重t
{ const uint8_t l[] = {0xC3,0xAB,0xD6,0xD8,0xA3,0xA8,0x74,0xA3,0xA9,0xA3,0xBA};
print_line(l, sizeof(l), "GrossWeight"); }
// 毛重时间:
{ const uint8_t l[] = {0xC3,0xAB,0xD6,0xD8,0xCA,0xB1,0xBC,0xE4,0xA3,0xBA};
print_line(l, sizeof(l), "GrossTime"); }
// 皮重t
{ const uint8_t l[] = {0xC6,0xA4,0xD6,0xD8,0xA3,0xA8,0x74,0xA3,0xA9,0xA3,0xBA};
print_line(l, sizeof(l), "TareWeight"); }
// 皮重时间:
{ const uint8_t l[] = {0xC6,0xA4,0xD6,0xD8,0xCA,0xB1,0xBC,0xE4,0xA3,0xBA};
print_line(l, sizeof(l), "TareTime"); }
// 净重t
{ const uint8_t l[] = {0xBE,0xBB,0xD6,0xD8,0xA3,0xA8,0x74,0xA3,0xA9,0xA3,0xBA};
print_line(l, sizeof(l), "SuttleWeight"); }
// 扣重t
{ const uint8_t l[] = {0xBF,0xDB,0xD6,0xD8,0xA3,0xA8,0x74,0xA3,0xA9,0xA3,0xBA};
print_line(l, sizeof(l), "RockWeight"); }
usleep(100 * 1000);
// 7. 扣重打印完成后,反向走纸 10 单位(为打印章做准备)
m_PrintData.insert(m_PrintData.end(), {0x1B, 0x6A, 0x0A}); // ESC j 10
usleep(100 * 1000);
// 8. 设置右对齐,准备打印章
m_PrintData.insert(m_PrintData.end(), {0x1B, 0x61, 0x02}); // ESC a 2 右对齐
usleep(50 * 1000);
// 9. 打印章图片GS v 0 光栅位图160x160 → 放大 2 倍 → 320x320
{
std::vector<uint8_t> stamp_raw;
if (LoadStampHexImage("/userdata/auseft/yuqian.bin", stamp_raw)) {
std::vector<uint8_t> stamp_scaled;
if (EnlargeStampNearest(stamp_raw, 160, 160, 2, stamp_scaled)) {
const int bytes_per_line = (320 + 7) / 8; // = 40
const int total_lines = 320;
const uint8_t hdr[8] = {
0x1D, 0x76, 0x30, 0x00, // GS v 0 m=0
static_cast<uint8_t>(bytes_per_line & 0xFF),
static_cast<uint8_t>((bytes_per_line >> 8) & 0xFF),
static_cast<uint8_t>(total_lines & 0xFF),
static_cast<uint8_t>((total_lines >> 8) & 0xFF),
};
m_PrintData.insert(m_PrintData.end(), hdr, hdr + 8);
m_PrintData.insert(m_PrintData.end(), stamp_scaled.begin(), stamp_scaled.end());
// 不再把栅格塞进 m_PrintData直接分块写串口每 256 字节间隔 3ms
// SendDeviceMsg(std::vector<uint8_t>(hdr, hdr + 8)); // 先单独发 GS v 0 头
// usleep(50 * 1000); // 等打印机解析完头
// SendDeviceMsgChunked(stamp_scaled, 256, 3000); // 栅格分 256 字节/段,段间等 3ms
} else {
std::cerr << "[Print] 章图放大失败" << std::endl;
}
} else {
std::cerr << "[Print] 章图加载失败,将跳过章图打印" << std::endl;
}
}
usleep(100 * 1000);
// 10. 正向走纸 7 行
m_PrintData.insert(m_PrintData.end(), {0x1B, 0x64, 0x7}); // ESC d 17
usleep(100 * 1000);
// 11. 全切
m_PrintData.insert(m_PrintData.end(), {0x1B, 0x69});
}