修改适配非等宽字体的计数器

This commit is contained in:
2026-08-26 16:23:04 +08:00
parent 950d0d0386
commit 190155913a
10 changed files with 267 additions and 70 deletions
+45 -3
View File
@@ -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 *)&reg_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 *)&reg_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;
+39
View File
@@ -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_ */
+62 -16
View File
@@ -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;
+1 -1
View File
@@ -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 ; //这会让产品计数显示出来
+2
View File
@@ -989,6 +989,8 @@ typedef struct {
int datHeight; // 变量的高度
int rowByteNum;
bool original; //表示当前dat是通过calloc申请而来还是直接引用别人的指针,主要是影响到指针的释放
uint8_t widthTable[10]; // 非等宽字体下,每个数字所占用的宽度
}vars_t;
typedef struct{
+38 -39
View File
@@ -395,51 +395,50 @@ PicScene (QGraphicsScene) # 场景,管理所有图元
```
### 6.2 作业文件格式
作业文件使用 XML 格式(根元素为 `VBfiles`),由 `PicSceneLoader` 读写。要点如下:
作业文件使用XML格式(VBfiles结构),保存场景内容和打印设置:
- 根元素: `<VBfiles Version="1.0">`(保存时会写入 `Version` 属性及一个简单的 DOCTYPE)。
- 每个图元为一个 `<VBfile>` 节点,常见子元素:
- `<pointer_x>` / `<pointer_y>`: 图元位置(浮点数)
- `<Condition>`: 特殊条件标志,0/1
- `<CatchStartValue>` / `<CatchValue>`: 与 Catch 相关的计数器起始/当前值(整数)
- `<itemAttrs ... />`: 图元属性以属性集合的形式保存(见下),`PicSceneLoader` 通过属性解析并传给具体图元对象
- `<html_src>`: 包裹图元 HTML 表示的 CDATA(编辑器中使用的 HTML 内容)
示例(简化):
```xml
<VBfiles>
<!-- 打印设置 -->
<PrintSettings timeTriggerPrintMode="0" triggerInterval="90" ... />
<!-- 文本图元 -->
<VBfile>
<pointer_x>55</pointer_x>
<pointer_y>0</pointer_y>
...
<itemAttrs sourceType="1" dir="0" letterSpace="1" fontFamilay="WenQuanYi7" source="FGJ" ... />
</VBfile>
<!-- 日期图元 -->
<VBfile>
<pointer_x>55</pointer_x>
<pointer_y>0</pointer_y>
...
<itemAttrs sourceType="3" datetimePattern="HH:mm:ss" ... />
</VBfile>
<!-- 计数器图元 -->
<VBfile>
<pointer_x>83</pointer_x>
<pointer_y>17</pointer_y>
...
<itemAttrs sourceType="6" counter_type="7" digital_num="8" ... />
</VBfile>
<!-- 条码/二维码图元 -->
<VBfile>
<pointer_x>0</pointer_x>
<pointer_y>0</pointer_y>
...
<itemAttrs sourceType="5" dir="0" barcodeContentPattern="dr66re{t}{c}" barcodeType="2" qrcodeFormat="0" ... />
</VBfile>
<!-- 图片图元 -->
<VBfile>
<pointer_x>0</pointer_x>
<pointer_y>0</pointer_y>
...
<itemAttrs sourceType="2" dir="0" source="/home/plg/doc/scene.png" ... />
</VBfile>
<VBfiles Version="1.0">
<VBfile>
<pointer_x>55</pointer_x>
<pointer_y>0</pointer_y>
<Condition>0</Condition>
<CatchStartValue>-1</CatchStartValue>
<CatchValue>-1</CatchValue>
<itemAttrs sourceType="1" dir="0" letterSpace="1" fontFamilay="WenQuanYi7" source="FGJ" />
<html_src><![CDATA[...item html...]]></html_src>
</VBfile>
<!-- 更多 VBfile ... -->
<!-- PrintSettings 以属性形式保存在一个空元素中 -->
<PrintSettings timeTriggerPrintMode="0" triggerInterval="90" ... />
</VBfiles>
```
其他细节:
- `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`(位置可以在文件中任意出现,保存实现默认写在所有 `<VBfile>` 之后)。
- 图元类型(`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序列化/反序列化
+74 -3
View File
@@ -36,6 +36,8 @@
#include <QScroller>
#include <QFontMetrics>
#include <QPainter>
#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:"<<sysPara->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<QRect> 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);
}
}
+3
View File
@@ -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();
+1 -5
View File
@@ -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("<img src=\":/%1/%5/../image/digitalWidth%2.bmp\" height=\"%3\" width=\"%4\" style=\"vertical-align: bottom;\" />")
.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 ;
+2 -3
View File
@@ -224,10 +224,9 @@ TestPrintWidget::~TestPrintWidget()
}
if (imageLoader) {
delete imageLoader;
delete imageLoader;
imageLoader = nullptr;
}
}
}
void TestPrintWidget::addDateItem()