From 190155913a90c6caf4c9e9f35d439930682bd0b5 Mon Sep 17 00:00:00 2001 From: byons Date: Wed, 26 Aug 2026 16:23:04 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E9=80=82=E9=85=8D=E9=9D=9E?= =?UTF-8?q?=E7=AD=89=E5=AE=BD=E5=AD=97=E4=BD=93=E7=9A=84=E8=AE=A1=E6=95=B0?= =?UTF-8?q?=E5=99=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- DevOp.cpp | 48 +++++++++++++++++++++++-- DevOp.h | 39 ++++++++++++++++++++ PrintTextDisplay.cpp | 78 +++++++++++++++++++++++++++++++--------- SysPara.cpp | 2 +- SysPara.h | 2 ++ docs/项目架构说明文档.md | 77 ++++++++++++++++++++------------------- mainwidget.cpp | 77 +++++++++++++++++++++++++++++++++++++-- mainwidget.h | 3 ++ scene/PicCounterItem.cpp | 6 +--- testprintwidget.cpp | 5 ++- 10 files changed, 267 insertions(+), 70 deletions(-) diff --git a/DevOp.cpp b/DevOp.cpp index 72d19a2..f1bd457 100644 --- a/DevOp.cpp +++ b/DevOp.cpp @@ -3176,14 +3176,15 @@ void DevOp::writeCnt1GroupSetting(bool bCntByGroupEnable){ } #endif } -void DevOp::readCalandarDat(void){ + +void DevOp::readCalandarDat(void) +{ fpgaRd(READ_CALENDAR_DAT_ADDR,(unsigned char *)&sysPara->status.calandar_and_dat,16); fpgaRd((READ_CALENDAR_DAT_ADDR + 0x34) ,(unsigned char *)&sysPara->status.Status_Counter3.cnt3CurrValue,4); fpgaRd((READ_CALENDAR_DAT_ADDR + 0x38) ,(unsigned char *)&sysPara->status.XXXDay,2); } - void DevOp::writeFpgaCalandar(bool Enable){ Q_UNUSED(Enable); time_t rawtime; @@ -3258,7 +3259,16 @@ void DevOp::writeFpgaCalandar(bool Enable){ sysPara->status.cnt1CurrentValueInitDoneFlag = false; } unsigned char dataOfvariNum[1]; -unsigned char dataOfreg_var_print_ctrl[8][8]; + +/** + * @brief 原 dataOfreg_var_print_ctrl 定义 + * dataOfreg_var_print_ctrl[8][8] + * + * 现在多了一个字节,是为了计数器添加支持非等宽字体时,存放对齐方向和字符间距所用 + * @see reg_var_print_ctrl_t_v2 + */ +unsigned char dataOfreg_var_print_ctrl[8][12]; + void DevOp::writeFpgaVariCtrl(void){ writeFpgaCalandar(0); dataOfvariNum[0]=sysPara->status.variNum; @@ -3283,6 +3293,38 @@ void DevOp::writeFpgaVariCtrl(void){ fpgaWr(VAR_CTRL_REG_ADDR,dataOfreg_var_print_ctrl[i],8); } } + +void DevOp::writeFpgaVariCtrlv2() +{ + writeFpgaCalandar(0); + dataOfvariNum[0]=sysPara->status.variNum; + fpgaWr(VAR_CNT_ADDR,dataOfvariNum,1); /* 0xa450 变量个数 */ + reg_var_print_ctrl_t_v2 reg_var_print_ctrl; + for(int i = 0; i < sysPara->status.variNum; i++){ + vars_t *p_var = &sysPara->status.vars[i]; + if(p_var->original && p_var->dat){ + fpgaWr(PRINT_DIGITAL_CHAR_BASE_ADDR + p_var->bassAddr,p_var->dat,p_var->datWidth *p_var->rowByteNum); + fpgaWr(PRINT_DIGITAL_CHAR_WIDTH_TABLE_BASE_ADDR + i * 10, p_var->widthTable, sizeof(p_var->widthTable)); + } + + memset((char *)®_var_print_ctrl,0,sizeof(reg_var_print_ctrl)); + reg_var_print_ctrl.charColNum = std::max_element(std::begin(p_var->widthTable), std::end(p_var->widthTable)); + reg_var_print_ctrl.charRamBaseAddr = p_var->bassAddr; + reg_var_print_ctrl.charRowNum = p_var->height; + reg_var_print_ctrl.horizonWidth = p_var->width; //字模的宽度 + reg_var_print_ctrl.colOffset = p_var->startColPos+BLANK_COL_NUM; + reg_var_print_ctrl.verticalOffset = p_var->startRowPos; + reg_var_print_ctrl.varDigitalNum = p_var->digitalNum; + reg_var_print_ctrl.varTypeSel = Public::getType(p_var->type) %20; + reg_var_print_ctrl.enbVarWidthFont = 1; + reg_var_print_ctrl.alignLeft = 0; + reg_var_print_ctrl.charSpacing = p_var->letterSpace; + memcpy(dataOfreg_var_print_ctrl[i],(unsigned char *)®_var_print_ctrl,8); + + fpgaWr(VAR_CTRL_REG_ADDR,dataOfreg_var_print_ctrl[i], 9); + } +} + unsigned char dataOfCmd[1]; void DevOp::printCtrl(int cmd){ dataOfCmd[0]=cmd; diff --git a/DevOp.h b/DevOp.h index cbe8c6c..ea75ee2 100644 --- a/DevOp.h +++ b/DevOp.h @@ -70,6 +70,7 @@ class SysPara; #define PRINT_CTRL_ADDR_BASE 0xa400 #define PRINT_DIGITAL_CHAR_BASE_ADDR (0xe000) +#define PRINT_DIGITAL_CHAR_WIDTH_TABLE_BASE_ADDR 0xf000 #define WRITE_CALENDAR_DAT_ADDR (0xb100) @@ -242,7 +243,20 @@ public : void ReadCnt1GroupStatus(void); void writeCnt1GroupSetting(bool bCntByGroupEnable); void writeFpgaCalandar(bool Enable); + + /** + * @brief 写入计数器变量控制参数 + * 这是旧的,用于等宽字体的计数器变量控制 + * + */ void writeFpgaVariCtrl(); + + /** + * @brief 写入计数器变量控制,此版本支持非等宽字体的变量控制 + * + */ + void writeFpgaVariCtrlv2(); + void readCalandarDat(); void resumeFault(); void highVoltageCtrl(int val); @@ -391,6 +405,31 @@ int pwm_ink; unsigned int dummy:1; }reg_var_print_ctrl_t; + /** + * @brief 相比于 @see reg_var_print_ctrl_t ,这个版本多了 alignLeft enbVarWidthFont。两个字段,用于支持非等宽字体 + * + */ + typedef struct { + // 0x51 + unsigned int varDigitalNum:4; // 数字的个数 + unsigned int charRamBaseAddr:12; // 基址 + unsigned int colOffset:16; // 列偏移 + + // 0x54 + unsigned int horizonWidth:9;//字模宽度 + unsigned int verticalOffset:6; // 行偏移 + unsigned int charRowNum:6; // 每个字符占行个数 + unsigned int charColNum:6; // 每个字符占列个数 + unsigned int varTypeSel:4; + unsigned int enbVarWidthFont:1; // 启用非等宽字体 + + // 0x59 + unsigned int reserved: 3; + unsigned int charSpacing: 4; + unsigned int alignLeft:1; // 使用非等宽字体时,计数器的对齐方向。 0左 1右 + }reg_var_print_ctrl_t_v2; + + }; #endif /* DEVOP_H_ */ diff --git a/PrintTextDisplay.cpp b/PrintTextDisplay.cpp index 232773a..dbedebc 100644 --- a/PrintTextDisplay.cpp +++ b/PrintTextDisplay.cpp @@ -218,10 +218,9 @@ void PrintTextDisplay::paintEvent(QPaintEvent *event){ for(int i = 0; i < sysPara->status.variNum; i++){ vars_t *p_var = &sysPara->status.vars[i]; -#if defined(ARCH_AARCH64) int val = p_var->currVal; -#else - int val = 123078; +#ifdef QT_DEBUG + val = 123078; #endif int x = p_var->startColPos; int y = p_var->startRowPos; @@ -313,17 +312,31 @@ void PrintTextDisplay::paintEvent(QPaintEvent *event){ else p.setBrush(QColor(Qt::red)); //其他 QChar *qchar = str.data(); - int charWidth; - if(sysPara->para.flagOfModuleDecimal || sysPara->para.flagOfModuleHide) - charWidth = p_var->datWidth/13; - else - charWidth = p_var->datWidth/10; + // 准备回退宽度与安全保护值 + int datWidth = p_var->datWidth > 0 ? p_var->datWidth : 1; + int datHeight = p_var->datHeight > 0 ? p_var->datHeight : (p_var->rowByteNum * 8); + int fallbackWidth = qMax(1, datWidth / qMax(1, (p_var->digitalNum? p_var->digitalNum:10))); + int rowByteNum = p_var->rowByteNum > 0 ? p_var->rowByteNum : 1; + + // 构建字形前缀和(字形按 0..9 顺序存放) + int glyphPrefix[11]; + glyphPrefix[0] = 0; + for(int gi = 1; gi <= 10; ++gi){ + int w = (gi-1 < 10 && p_var->widthTable[gi-1] > 0) ? p_var->widthTable[gi-1] : fallbackWidth; + glyphPrefix[gi] = glyphPrefix[gi-1] + w; + } + + // 限制使用的行数为字模高度与请求行数的较小者 + int useRows = qMin(rowDiff, datHeight); + + // 累计可变宽数字的显示位置 + int posInDigits = 0; for( j = jmin; j < digitalNum; j++){ int digitalIndex; if(!sysPara->para.rrr_nc && sysPara->para.printNumPoint && p_var->type == 7 && j == (digitalNum-2)) { - digitalIndex = 10; + digitalIndex = 10; // 小数点 } else if(sysPara->para.rrr_nc && !sysPara->para.printNumPoint && p_var->type == 7 && j == (digitalNum-3)) { @@ -335,25 +348,58 @@ void PrintTextDisplay::paintEvent(QPaintEvent *event){ } else if(sysPara->status.specialVarPrint.MeterEnable && p_var->type == 15 && j == (digitalNum-1)) { - digitalIndex = 11; + digitalIndex = 11; // 单位 'm' } else { digitalIndex = qchar[j].toLatin1() - '0'; } - unsigned char *pAaddr = p_var->dat + (digitalIndex*charWidth)*p_var->rowByteNum; - for(int k = 0; k < rowDiff; k++){ - for(int l = 0; l < charWidth; l++){ - if(pAaddr[l*p_var->rowByteNum + k/8] & (1<<(k%8))){ + // 获取此字形的宽度(优先取 widthTable,否则使用回退宽度) + int glyphW = (digitalIndex >=0 && digitalIndex < 10 && p_var->widthTable[digitalIndex] > 0) ? p_var->widthTable[digitalIndex] : fallbackWidth; + + // 计算字形在 p_var->dat 中的水平偏移(字形按 0..9 顺序存放) + int glyphOffset = 0; + if(digitalIndex >= 0 && digitalIndex <= 10){ + glyphOffset = glyphPrefix[qMin(digitalIndex,10)]; + } else { + glyphOffset = 0; + } + + // 如果字形数据比预期窄,则裁剪宽度 + if(glyphOffset + glyphW > datWidth){ + if(glyphOffset >= datWidth) { + // 超出数据宽度,跳过绘制 + posInDigits += glyphW; + continue; + } + glyphW = datWidth - glyphOffset; + } + + unsigned char *pAaddr = p_var->dat + glyphOffset * rowByteNum; + + int displayBaseX = x + posInDigits; // 当前数字的绘制起始 X + + for(int k = 0; k < useRows; k++){ + for(int l = 0; l < glyphW; l++){ + if(pAaddr[l*rowByteNum + k/8] & (1<<(k%8))){ if(sysPara->status.addSpace){ - p.drawRect((x + j*charWidth + l)/2,y + k,1,1);l++; + p.drawRect((displayBaseX + l)/2, y + k, 1, 1); } else - p.drawRect(x + j*charWidth + l,y + k,1,1); + p.drawRect(displayBaseX + l, y + k, 1, 1); } } } + + // 累加字形宽度 + posInDigits += glyphW; + // 模拟 FPGA 处理的字符间距(以像素为单位) + int spacing = p_var->letterSpace; + if(spacing < 0) spacing = 0; + // 避免过大的间距导致溢出预览区域 + if(spacing > datWidth) spacing = datWidth; + posInDigits += spacing; } } updateDigitalOnly = false; diff --git a/SysPara.cpp b/SysPara.cpp index 85e442d..569aeac 100644 --- a/SysPara.cpp +++ b/SysPara.cpp @@ -59,7 +59,7 @@ SysPara::SysPara(void): //para.HVFaultCloseEn= 1; //开机默认为1,允许高压故障自动关闭回收功能 devOp->writeFpgaPrintPara(true); //系统起来的时候,将上次记录的配置写入fpga,比如晶振分频等信息 - devOp->writeFpgaVariCtrl(); + devOp->writeFpgaVariCtrlv2(); devOp->resumeFault(); status.lastProductCnt = status.productCnt ; //这会让产品计数显示出来 diff --git a/SysPara.h b/SysPara.h index cec50dd..a9ff346 100644 --- a/SysPara.h +++ b/SysPara.h @@ -989,6 +989,8 @@ typedef struct { int datHeight; // 变量的高度 int rowByteNum; bool original; //表示当前dat是通过calloc申请而来还是直接引用别人的指针,主要是影响到指针的释放 + + uint8_t widthTable[10]; // 非等宽字体下,每个数字所占用的宽度 }vars_t; typedef struct{ diff --git a/docs/项目架构说明文档.md b/docs/项目架构说明文档.md index bc2badb..d32a4c1 100644 --- a/docs/项目架构说明文档.md +++ b/docs/项目架构说明文档.md @@ -395,51 +395,50 @@ PicScene (QGraphicsScene) # 场景,管理所有图元 ``` ### 6.2 作业文件格式 +作业文件使用 XML 格式(根元素为 `VBfiles`),由 `PicSceneLoader` 读写。要点如下: -作业文件使用XML格式(VBfiles结构),保存场景内容和打印设置: +- 根元素: ``(保存时会写入 `Version` 属性及一个简单的 DOCTYPE)。 +- 每个图元为一个 `` 节点,常见子元素: + - `` / ``: 图元位置(浮点数) + - ``: 特殊条件标志,0/1 + - `` / ``: 与 Catch 相关的计数器起始/当前值(整数) + - ``: 图元属性以属性集合的形式保存(见下),`PicSceneLoader` 通过属性解析并传给具体图元对象 + - ``: 包裹图元 HTML 表示的 CDATA(编辑器中使用的 HTML 内容) + +示例(简化): ```xml - - - - - - 55 - 0 - ... - - - - - 55 - 0 - ... - - - - - 83 - 17 - ... - - - - - 0 - 0 - ... - - - - - 0 - 0 - ... - - + + + 55 + 0 + 0 + -1 + -1 + + + + + + ``` +其他细节: +- `itemAttrs` 中常见属性由各图元实现 `saveAttrs` / `setAttrs` 控制,通用属性包括 `sourceType`(必有)、`dir`、`boldMultiple`、`colorReversal`("true"/"false")、`letterSpace`、`fontFamilay`(注意拼写)以及 `source`(图元内容或图片路径)等。 +- `html_src` 使用 CDATA 保存 `toHtml()` 的内容,加载时会把 CDATA 拼接并通过 `setHtml()` 恢复。 +- `PrintSettings` 由 `PrintSettingModel::saveToXml`/`loadFromXml` 通过属性读写打印相关配置(保存时作为空元素写出属性)。`PicSceneLoader` 在解析 `VBfiles` 时会识别 `PrintSettings`(位置可以在文件中任意出现,保存实现默认写在所有 `` 之后)。 +- 图元类型(`sourceType`)的数值映射(见 `scene/pictextitem.h`): + - `0` = `UNKNOWN` + - `1` = `TEXT` + - `2` = `IMAGE` + - `3` = `DATETIME` + - `4` = `SCHEDULE_SHIFT` + - `5` = `CODE`(一维/二维码) + - `6` = `COUNTER` + +该节已根据 `scene/PicSceneLoader.cpp` 的实际实现和 `Printsetting.cpp` 中的读写逻辑做了校正(包含 `Condition`/`Catch*` 字段、`Version` 属性、`html_src` CDATA、以及 `PrintSettings` 的属性化保存)。 + ### 6.3 关键类说明 - **PicSceneLoader** - 场景文件的加载/保存/预览,负责XML序列化/反序列化 diff --git a/mainwidget.cpp b/mainwidget.cpp index 94464c3..8793feb 100644 --- a/mainwidget.cpp +++ b/mainwidget.cpp @@ -36,6 +36,8 @@ #include #include #include +#include "MainWidget.h" +#include "scene/ItemCommon.h" extern MyInputMethod *myInputMethod; unsigned char test_flag,open_close; @@ -647,6 +649,72 @@ void MainWidget::generateCharRamData() } +void MainWidget::generateCharRamDataV2() +{ + if(sysPara->status.variNum == 0) + return; + + qDebug()<<"sysPara->status.variNum:"<status.variNum; + + int ramAddress = 0; + int i; + for( i = 0; i < sysPara->status.variNum; i++){ + vars_t *p_var = &sysPara->status.vars[i]; + p_var->bassAddr = -1; + if(p_var->original){ //清除原来的记录 + if(p_var->dat){ + free(p_var->dat); + } + p_var->original = false; + } + + int width; + if(p_var->bassAddr == -1){ + width = (p_var->digitalNum)?(10*(p_var->width/p_var->digitalNum)):12; + int height = p_var->height; + + QString fontname = Public::getFontFamily(height,p_var->type); + QFont font(fontname); + + // 用 0 间距,间距交给FPGA处理 + font.setLetterSpacing(QFont::AbsoluteSpacing, 0); + qDebug() << "Generate char, letter space: " << p_var->letterSpace << "font height: " << height; + + static constexpr char *NUMBERS = "0123456789"; + QImage image = ItemUtils::txtToImage(NUMBERS, font, Qt::black, Qt::transparent); + int imgH = image.height(); + int imgW = image.width(); + + int rowByteNum = imgH/8 + ((imgH%8)?1:0); // 一列占用字节数 + unsigned char *dat = (unsigned char *)calloc(imgW, rowByteNum); + for(int k = 0; k < imgH; k++){ + for(int j = 0; j < imgW; j ++){ + QRgb rgb = image.pixel(j,k); + int abit = ((rgb >> 24)&0xff) > 0x80 ? 1 : 0; + dat[rowByteNum * j + k / 8] |= (abit << (k%8)); + } + } + + // 填充宽度表 + QFontMetrics fm(font); + for(int i = 0; i < 10; ++i) { + p_var->widthTable[i] = fm.horizontalAdvance(NUMBERS[i]); + } + + // 填充其他值 + p_var->bassAddr = ramAddress; + p_var->datHeight = imgH; + + p_var->datWidth = imgW; + p_var->rowByteNum = rowByteNum; + p_var->dat = dat; + p_var->original = true; + p_var->currVal = -1; //相当于初始化 + ramAddress += rowByteNum * imgW; + } + } +} + QList MainWidget::variableItemsUpdate() { if(!pPicScene) { @@ -1033,7 +1101,7 @@ void MainWidget::SendPrintDataToFpga(unsigned char alterEnable, bool isUpdate) if(!isUpdate) { sysPara->devOp->writeFpgaPrintPara(1); - sysPara->devOp->writeFpgaVariCtrl(); + sysPara->devOp->writeFpgaVariCtrlv2(); } sysPara->devOp->fpgaWr(0,blankDat1,sizeof(blankDat1));//...所加的空白偏移地址 @@ -2057,7 +2125,10 @@ void MainWidget::onPreviewScene() imageToPrintData(image, 0, 0); // /*--------------------------------------------------------------------------*/ - generateCharRamData(); + // generateCharRamData(); + + generateCharRamDataV2(); + if(sysPara->status.currPrintRow>MAX_PRINT_ROW_NUM-1)sysPara->status.currPrintRow=MAX_PRINT_ROW_NUM-1; sysPara->status.currPrintRow = qMin(sysPara->status.currPrintRow, MAX_PRINT_ROW_NUM-1); @@ -2605,4 +2676,4 @@ void MainWidget::UpdateUIVersion(){ QString Mac = QString::number(sysPara->status.machineMac); LogInfo::setRunStatusText(QString("Mac:%1").arg(Mac)); LogInfo::showVerionInformation(Mac,VernonBach,fpgaVersion,STM32Ver); -} +} \ No newline at end of file diff --git a/mainwidget.h b/mainwidget.h index b4f186b..e822ce4 100644 --- a/mainwidget.h +++ b/mainwidget.h @@ -82,6 +82,9 @@ public: int imageToPrintData(QImage& img, int startX, int startY); void generateCharRamData(); + + void generateCharRamDataV2(); + void setRunStatus(QString str, bool enbRoll = true); void enablePrintButton(bool); int getCurrPrintStatus(); diff --git a/scene/PicCounterItem.cpp b/scene/PicCounterItem.cpp index 68663c8..4b26eda 100644 --- a/scene/PicCounterItem.cpp +++ b/scene/PicCounterItem.cpp @@ -57,14 +57,10 @@ void PicCounterItem::setCounter(COUNTER_TYPE type, int digitalNum) int width = (fontMetrics.horizontalAdvance("0123456789")) / 10; int height = font.pixelSize(); width += letterSpace(); - - setHtml(QString("") - .arg(type).arg(digitalNum).arg(height + 1).arg(width*digitalNum).arg(type)); m_counterType = type; m_digitalNum = digitalNum; - m_digitalImg = QImage(QString(":/%1/image/digitalWidth%2.bmp").arg(type).arg(digitalNum)); - m_digitalImg = m_digitalImg.scaled(width*digitalNum, height, Qt::IgnoreAspectRatio, Qt::FastTransformation); + m_digitalImg = strToImage(QString("8").repeated(digitalNum)); applyModify(); qInfo() << "Counter item set to " << type ; diff --git a/testprintwidget.cpp b/testprintwidget.cpp index bc89783..192b448 100644 --- a/testprintwidget.cpp +++ b/testprintwidget.cpp @@ -224,10 +224,9 @@ TestPrintWidget::~TestPrintWidget() } if (imageLoader) { - delete imageLoader; + delete imageLoader; imageLoader = nullptr; -} - + } } void TestPrintWidget::addDateItem()