Linux基础项目开发day06:量产工具——业务系统

ops/2024/10/22 14:31:27/

文章目录

  • 前言
  • 一、流程代码框架
    • 1、业务系统框架流程
    • 2、主页面流程图
    • 3、main.c实现流程
  • 二、处理配置文件
    • 1、配置文件是啥?
      • config.h
    • 2、怎么处理配置文件?
      • config.c
  • 三、生成界面
    • 1、计算每个按钮的Region
    • 2、逐个生成按钮画面->生成页面
  • 四、读取输入事件
  • 五、根据输入事件找到按钮
  • 六、调用按钮的OnPressed函数
    • 完整的main.page.c
  • 七、上机测试

前言

我们目前已经搭建了显示系统、输入系统、文字系统、UI系统、页面系统,这就已经完成了这个项目的百分之80了,之间的都是独立的开发基础,我们就是要利用前面的基础来合成再运用产品所需要的逻辑,就可以实现项目功能!
1.前面实现了各个子系统:显示、输入、文字、UI、页面
2.它们只是提供基础能力,跟业务逻辑没有关系
3.这样的架构很容易扩展,可以在这上面实现各种业务
4.比如可以做收银机、自动售卖机、智能称、取号机;
再加上摄像头显示,就可以做出可视对讲、视频监控、人脸红外测温
5.对于不同的产品,我们只需要写出自己页面函数

一、流程代码框架

1、业务系统框架流程

在这里插入图片描述

2、主页面流程图

在这里插入图片描述

3、main.c实现流程

这里我们在main.c里面做的事情就很明显了,先初始化前面的独立的系统,后面再只要运行业务里面的Run函数即可!


#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <linux/fb.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <sys/ioctl.h>
#include <stdlib.h>#include <disp_manager.h>
#include <font_manager.h>
#include <input_manager.h>
#include <page_manager.h>int main(int argc, char **argv)
{int error;if (argc != 2){printf("Usage: %s <font_file>\n", argv[0]);return -1;}/* 初始化显示系统 */		DisplayInit();SelectDefaultDisplay("fb");InitDefaultDisplay();/* 初始化输入系统 */		InputInit();IntpuDeviceInit();/* 初始化文字系统 */		FontsRegister();/* 选择一个文字系统 */error = SelectAndInitFont("freetype", argv[1]);if (error){printf("SelectAndInitFont err\n");return -1;}/* 初始化页面系统 */		PagesRegister();/* 运行业务系统的主页面 */Page("main")->Run(NULL);return 0;	
}

那么main函数里面的Run函数里面到底在做啥呢?
在这里插入图片描述
要做的东西就只有这些,所以我们后面只需要实现对应作用的函数即可!

二、处理配置文件

1、配置文件是啥?

使用配置文件我们可以使这个代码更有兼容性和更灵活,我们需要修改页面内容时,我们只需要去修改配置文件即可!就不用特地去修改代码,配置文件是怎么样的呢?
在这里插入图片描述
按钮的名称都是有配置文件读取得到。
对于每一行,都对应一个按钮图画,但是这些按钮图画有些是可以通过触摸去改变颜色的,但是有一些是不可以的,只能通过网络发送信息来修改颜色,所以我们就用一个结构体来代指一个按钮图画:
在这里插入图片描述

config.h


#ifndef _CONFIG_H
#define _CONFIG_H#include <common.h>#define ITEMCFG_MAX_NUM 30
#define CFG_FILE  "/etc/test_gui/gui.conf"typedef struct ItemCfg {int index;        char name[100];   int bCanBeTouched; char command[100]; 
}ItemCfg, *PItemCfg;int ParseConfigFile(void);  //处理配置文件函数
int GetItemCfgCount(void);  //判断有多少个按钮图画
PItemCfg GetItemCfgByIndex(int index); //通过索引查找config结构体
PItemCfg GetItemCfgByName(char *name); //通过name查找config结构体#endif

那我们要怎么处理配置文件呢?

2、怎么处理配置文件?

看到了配置文件的格式,我们就知道了,有#号的第一行知识注释,省略这一行其他的就简单了:

#include <config.h>
#include <stdio.h>
#include <string.h>static ItemCfg g_tItemCfgs[ITEMCFG_MAX_NUM];
static int g_iItemCfgCount = 0;/* 处理配置文件函数 */
int ParseConfigFile(void)
{FILE *fp;      //用于保存返回的文件标识符char buf[100]; //缓冲数组char *p = buf; //数组指针/* 1. open config file */fp = fopen(CFG_FILE, "r");if (!fp){//打开文件失败,打印信息printf("can not open cfg file %s\n", CFG_FILE);return -1;}//读取一行配置文件信息while (fgets(buf, 100, fp)){/* 2.1 read each line */buf[99] = '\0';		/* 2.2 吃掉开头的空格或TAB */p = buf;while (*p == ' ' || *p =='\t')p++;/* 2.3 忽略注释 */if (*p == '#')continue;//挑出循环,直接忽略这一行,直接读取下一行/* 2.4 处理 */g_tItemCfgs[g_iItemCfgCount].command[0] = '\0';g_tItemCfgs[g_iItemCfgCount].index = g_iItemCfgCount;sscanf(p, "%s %d %s", g_tItemCfgs[g_iItemCfgCount].name, &g_tItemCfgs[g_iItemCfgCount].bCanBeTouched, \g_tItemCfgs[g_iItemCfgCount].command);g_iItemCfgCount++;		}return 0;
}

config.c

#include <config.h>
#include <stdio.h>
#include <string.h>static ItemCfg g_tItemCfgs[ITEMCFG_MAX_NUM];
static int g_iItemCfgCount = 0;//处理配置文件函数
int ParseConfigFile(void)
{FILE *fp;char buf[100];char *p = buf;/* 1. open config file */fp = fopen(CFG_FILE, "r");if (!fp){printf("can not open cfg file %s\n", CFG_FILE);return -1;}while (fgets(buf, 100, fp)){/* 2.1 read each line */buf[99] = '\0';		/* 2.2 吃掉开头的空格或TAB */p = buf;while (*p == ' ' || *p =='\t')p++;/* 2.3 忽略注释 */if (*p == '#')continue;/* 2.4 处理 */g_tItemCfgs[g_iItemCfgCount].command[0] = '\0';g_tItemCfgs[g_iItemCfgCount].index = g_iItemCfgCount;sscanf(p, "%s %d %s", g_tItemCfgs[g_iItemCfgCount].name, &g_tItemCfgs[g_iItemCfgCount].bCanBeTouched, \g_tItemCfgs[g_iItemCfgCount].command);g_iItemCfgCount++;		}return 0;
}//判断有多少个按钮图画
int GetItemCfgCount(void)
{return g_iItemCfgCount;
}//通过索引查找config结构体
PItemCfg GetItemCfgByIndex(int index)
{if (index < g_iItemCfgCount)return &g_tItemCfgs[index];elsereturn NULL;
}//通过name查找config结构体
PItemCfg GetItemCfgByName(char *name)
{int i;for (i = 0; i < g_iItemCfgCount; i++){if (strcmp(name, g_tItemCfgs[i].name) == 0)return &g_tItemCfgs[i];}return NULL;
}

三、生成界面

1、计算每个按钮的Region

在这里插入图片描述

2、逐个生成按钮画面->生成页面

这个函数是写在main_page.c里面的

static void GenerateButtons(void)
{int width, height; //宽度高度int n_per_line;    //一行可以画的下多少个按钮int row, rows;     //行(for循环用), 行数int col;           //列数int n;             //按钮个数PDispBuff pDispBuff;int xres, yres;   //屏幕XY分辨率int start_x, start_y;   //区域左上角坐标int pre_start_x, pre_start_y; //上一个区域左上角坐标PButton pButton;  //按钮结构体int i = 0;/* 算出单个按钮的width/height */g_tButtonCnt = n = GetItemCfgCount();pDispBuff = GetDisplayBuffer();xres = pDispBuff->iXres;yres = pDispBuff->iYres;width = sqrt(1.0/0.618 *xres * yres / n);n_per_line = xres / width + 1;width  = xres / n_per_line;height = 0.618 * width;	/* 居中显示:  计算每个按钮的region  */start_x = (xres - width * n_per_line) / 2;rows    = n / n_per_line;if (rows * n_per_line < n)rows++;start_y = (yres - rows*height)/2;/* 计算每个按钮的region */for (row = 0; (row < rows) && (i < n); row++){pre_start_y = start_y + row * height;pre_start_x = start_x - width;for (col = 0; (col < n_per_line) && (i < n); col++){/* 逐个计算赋值按钮结构体 */pButton = &g_tButtons[i];pButton->tRegion.iLeftUpX = pre_start_x + width;pButton->tRegion.iLeftUpY = pre_start_y;pButton->tRegion.iWidth   = width - X_GAP;pButton->tRegion.iHeigh   = height - Y_GAP;pre_start_x = pButton->tRegion.iLeftUpX;/* InitButton */InitButton(pButton, GetItemCfgByIndex(i)->name, NULL, NULL, MainPageOnPressed);i++;}}/* OnDraw逐个Draw按钮图画 */for (i = 0; i < n; i++)g_tButtons[i].OnDraw(&g_tButtons[i], pDispBuff);
}

四、读取输入事件

对于读取输入事件我们之前在输入系统就已经实现了,忘记的可以看看:Linux基础项目开发day02:量产工具——输入系统

五、根据输入事件找到按钮

这些函数都是在main.page.c里面实现的


static int isTouchPointInRegion(int iX, int iY, PRegion ptRegion);
static PButton GetButtonByName(char *name);static PButton GetButtonByInputEvent(PInputEvent ptInputEvent)
{int i;char name[100];/* 输入事件是触摸屏事件 */if (ptInputEvent->iType == INPUT_TYPE_TOUCH){for (i = 0; i < g_tButtonCnt; i++){/* 拿触摸点的坐标一个个的去和按钮Region对比,在区域内就返回这个按钮 */if (isTouchPointInRegion(ptInputEvent->iX, ptInputEvent->iY, &g_tButtons[i].tRegion))return &g_tButtons[i];//返回这个按钮}}/* 输入事件是网络输入事件 */else if (ptInputEvent->iType == INPUT_TYPE_NET){//从网络数据中读取数据到name数组中sscanf(ptInputEvent->str, "%s", name);//拿网络输入的数据和按钮Region的名称对比,找到一样的返回这个按钮Bottonreturn GetButtonByName(name);}else{return NULL;}return NULL;
}/* 拿触摸点的坐标一个个的去和按钮Region对比,在区域内就返回这个按钮 */
static int isTouchPointInRegion(int iX, int iY, PRegion ptRegion)
{if (iX < ptRegion->iLeftUpX || iX >= ptRegion->iLeftUpX + ptRegion->iWidth)return 0;if (iY < ptRegion->iLeftUpY || iY >= ptRegion->iLeftUpY + ptRegion->iHeigh)return 0;return 1;
}//拿网络输入的数据和按钮Region的名称对比,找到一样的返回这个按钮Botton
static PButton GetButtonByName(char *name)
{int i;for (i = 0; i < g_tButtonCnt; i++){if (strcmp(name, g_tButtons[i].name) == 0)return &g_tButtons[i];}return NULL;
}

六、调用按钮的OnPressed函数

通过第五步我们已经找到了按钮,最后一步就简单了,只需要调用里面的函数即可!
在这里插入图片描述
我们在生成按钮界面的时候就已经初始化了Botton结构体里面的函数了
在这里插入图片描述
所以我们这里只需要实现函数MainPageOnPressed即可!

static int MainPageOnPressed(struct Button *ptButton, PDispBuff ptDispBuff, PInputEvent ptInputEvent)
{unsigned int dwColor = BUTTON_DEFAULT_COLOR;char name[100];char status[100];char *strButton;strButton = ptButton->name;/* 1. 对于触摸屏事件 */if (ptInputEvent->iType == INPUT_TYPE_TOUCH){/* 1.1 分辨能否被点击 */if (GetItemCfgByName(ptButton->name)->bCanBeTouched == 0)return -1;/* 1.2 修改颜色 */ptButton->status = !ptButton->status;//修改按钮状态if (ptButton->status)//根据按钮状态修改颜色dwColor = BUTTON_PRESSED_COLOR;}else if (ptInputEvent->iType == INPUT_TYPE_NET){/* 2. 对于网络事件 *//* 根据传入的字符串修改颜色 : wifi ok, wifi err, burn 70 */sscanf(ptInputEvent->str, "%s %s", name, status);if (strcmp(status, "ok") == 0)dwColor = BUTTON_PRESSED_COLOR;//设置为绿色else if (strcmp(status, "err") == 0)dwColor = BUTTON_DEFAULT_COLOR;//设置为红色else if (status[0] >= '0' && status[0] <= '9'){			dwColor = BUTTON_PERCENT_COLOR;//设置为蓝色strButton = status;			}elsereturn -1;}else{return -1;}/* 绘制底色 */DrawRegion(&ptButton->tRegion, dwColor);/* 居中写文字 */DrawTextInRegionCentral(strButton, &ptButton->tRegion, BUTTON_TEXT_COLOR);/* flush to lcd/web */FlushDisplayRegion(&ptButton->tRegion, ptDispBuff);return 0;
}

完整的main.page.c

#include <config.h>
#include <stdio.h>
#include <ui.h>
#include <page_manager.h>
#include <math.h>
#include <string.h>#define X_GAP 5
#define Y_GAP 5static Button g_tButtons[ITEMCFG_MAX_NUM];
static int g_tButtonCnt;static int MainPageOnPressed(struct Button *ptButton, PDispBuff ptDispBuff, PInputEvent ptInputEvent)
{unsigned int dwColor = BUTTON_DEFAULT_COLOR;char name[100];char status[100];char *strButton;strButton = ptButton->name;/* 1. 对于触摸屏事件 */if (ptInputEvent->iType == INPUT_TYPE_TOUCH){/* 1.1 分辨能否被点击 */if (GetItemCfgByName(ptButton->name)->bCanBeTouched == 0)return -1;/* 1.2 修改颜色 */ptButton->status = !ptButton->status;if (ptButton->status)dwColor = BUTTON_PRESSED_COLOR;}else if (ptInputEvent->iType == INPUT_TYPE_NET){/* 2. 对于网络事件 *//* 根据传入的字符串修改颜色 : wifi ok, wifi err, burn 70 */sscanf(ptInputEvent->str, "%s %s", name, status);if (strcmp(status, "ok") == 0)dwColor = BUTTON_PRESSED_COLOR;else if (strcmp(status, "err") == 0)dwColor = BUTTON_DEFAULT_COLOR;else if (status[0] >= '0' && status[0] <= '9'){			dwColor = BUTTON_PERCENT_COLOR;strButton = status;			}elsereturn -1;}else{return -1;}/* 绘制底色 */DrawRegion(&ptButton->tRegion, dwColor);/* 居中写文字 */DrawTextInRegionCentral(strButton, &ptButton->tRegion, BUTTON_TEXT_COLOR);/* flush to lcd/web */FlushDisplayRegion(&ptButton->tRegion, ptDispBuff);return 0;
}static void GenerateButtons(void)
{int width, height;int n_per_line;int row, rows;int col;int n;PDispBuff pDispBuff;int xres, yres;int start_x, start_y;int pre_start_x, pre_start_y;PButton pButton;int i = 0;/* 算出单个按钮的width/height */g_tButtonCnt = n = GetItemCfgCount();pDispBuff = GetDisplayBuffer();xres = pDispBuff->iXres;yres = pDispBuff->iYres;width = sqrt(1.0/0.618 *xres * yres / n);n_per_line = xres / width + 1;width  = xres / n_per_line;height = 0.618 * width;	/* 居中显示:  计算每个按钮的region  */start_x = (xres - width * n_per_line) / 2;rows    = n / n_per_line;if (rows * n_per_line < n)rows++;start_y = (yres - rows*height)/2;/* 计算每个按钮的region */for (row = 0; (row < rows) && (i < n); row++){pre_start_y = start_y + row * height;pre_start_x = start_x - width;for (col = 0; (col < n_per_line) && (i < n); col++){pButton = &g_tButtons[i];pButton->tRegion.iLeftUpX = pre_start_x + width;pButton->tRegion.iLeftUpY = pre_start_y;pButton->tRegion.iWidth   = width - X_GAP;pButton->tRegion.iHeigh   = height - Y_GAP;pre_start_x = pButton->tRegion.iLeftUpX;/* InitButton */InitButton(pButton, GetItemCfgByIndex(i)->name, NULL, NULL, MainPageOnPressed);i++;}}/* OnDraw */for (i = 0; i < n; i++)g_tButtons[i].OnDraw(&g_tButtons[i], pDispBuff);
}static int isTouchPointInRegion(int iX, int iY, PRegion ptRegion)
{if (iX < ptRegion->iLeftUpX || iX >= ptRegion->iLeftUpX + ptRegion->iWidth)return 0;if (iY < ptRegion->iLeftUpY || iY >= ptRegion->iLeftUpY + ptRegion->iHeigh)return 0;return 1;
}static PButton GetButtonByName(char *name)
{int i;for (i = 0; i < g_tButtonCnt; i++){if (strcmp(name, g_tButtons[i].name) == 0)return &g_tButtons[i];}return NULL;
}static PButton GetButtonByInputEvent(PInputEvent ptInputEvent)
{int i;char name[100];if (ptInputEvent->iType == INPUT_TYPE_TOUCH){for (i = 0; i < g_tButtonCnt; i++){if (isTouchPointInRegion(ptInputEvent->iX, ptInputEvent->iY, &g_tButtons[i].tRegion))return &g_tButtons[i];}}else if (ptInputEvent->iType == INPUT_TYPE_NET){sscanf(ptInputEvent->str, "%s", name);return GetButtonByName(name);}else{return NULL;}return NULL;
}static void MainPageRun(void *pParams)
{int error;InputEvent tInputEvent;PButton ptButton;PDispBuff ptDispBuff = GetDisplayBuffer();/* 读取配置文件 */error = ParseConfigFile();if (error)return ;/* 根据配置文件生成按钮、界面 */GenerateButtons();while (1){/* 读取输入事件 */error = GetInputEvent(&tInputEvent);if (error)continue;/* 根据输入事件找到按钮 */ptButton = GetButtonByInputEvent(&tInputEvent);if (!ptButton)continue;/* 调用按钮的OnPressed函数 */ptButton->OnPressed(ptButton, ptDispBuff, &tInputEvent);}
}static PageAction g_tMainPage = {.name = "main",.Run  = MainPageRun,
};void MainPageRegister(void)
{PageRegister(&g_tMainPage);
}

七、上机测试

到这里我们就已经完成了基础开发项目了——量产工具

book@100ask:~/28_business_test$ make
book@100ask:~/28_business_test$ arm-buildroot-linux-gnueabihf-gcc -o client unittest/client.c

[root@100ask:~]# mount -t nfs -o nolock,vers=3 192.168.5.11:/home/book/nfs_rootfs /mnt
[root@100ask : /mnt/28_business_test] # mkdir /etc/test_gui
[root@100ask : /mnt/28 business test] # cp etc/test_gui/gui.conf /etc/
[root@100ask : /mnt/28 business test] # ./test ./simsun.ttc &
[root@100ask : /mnt/28 business test] # ./client 192.168.5.9 “net1 ok”
[root@100ask : /mnt/28 business test] # ./client 192.168.5.9 “ALL ok”
[root@100ask : /mnt/28 business test] # ./client 192.168.5.9 “net1 err”
[root@100ask : /mnt/28 business test] # ./client 192.168.5.9 “ALL err”

触摸点击测试:
在这里插入图片描述
点击后:
在这里插入图片描述


http://www.ppmy.cn/ops/127590.html

相关文章

微信小程序——消息订阅

首先用到的就是wx.requestSubscribeMessage接口。 注意&#xff1a;用户发生点击行为或者发起支付回调后&#xff0c;才可以调起订阅消息界面 requestSubscribeMessage() {uni.requestSubscribeMessage({tmplIds: [],//需要订阅的消息模板的id的集合&#xff0c;一次调用最多可…

vscode连接远端docker高效开发的方法

1 前言 目前项目源码部署在远端服务器的docker上&#xff0c;但是我习惯在vscode中修改源码并验证。所以需要通过vscode连接远端容器进行代码开发。vscode通过remote ssh连接服务器是比较常见的用法了&#xff0c;但是连接远端容器还是第一次使用。摸索了一阵后发现一个方便快…

laravel 查询数据库

数据库准备 插入 三行 不同的数据 自行搭建 laravel 工程 参考 工程创建点击此处 laravel 配置 数据库信息 DB_CONNECTIONmysql #连接什么数据库 DB_HOST127.0.0.1 # 连接 哪个电脑的 ip &#xff08;决定 电脑 本机&#xff09; DB_PORT3306 # 端口 DB_DATABASEyanyu…

在服务器启动docker容器卡住、无启动成功信息,docker ps一a状态码137

在服务器启动docker容器卡住、无启动成功信息&#xff0c;docker ps 一a状态码137 docker、ubuntu 20.04、emqx 5.8.0背景 想从移动安卓设备往服务器发点数据&#xff0c;因为服务器有固定IP&#xff0c;想起来之前看过的这个mqtt&#xff0c;感觉比较合适&#xff0c;但是启…

【贪心算法】(第二篇)

目录 最⼤数&#xff08;medium&#xff09; 题目解析 讲解算法原理 编写代码 摆动序列&#xff08;medium&#xff09; 题目解析 讲解算法原理 编写代码 最⼤数&#xff08;medium&#xff09; 题目解析 1.题目链接&#xff1a;. - 力扣&#xff08;LeetCode&#xf…

淘宝客服自动回复机器人-千牛工作台自动回复机器人-集成RPA+知识库+大模型AI工具

淘宝客服自动回复-千牛工作台接待中心自动回复机器人 影刀通过集成RPA知识库大模型AI工具&#xff0c;帮助商家打造24小时在线响应的AI客服 现在已经实现 llike620

electron-vite_9win软件名称和安装包名称设置?

软件名称、安装包名称、卸载软件名称都在electron-builder.yml中设置&#xff1b; 文章分开为什么&#xff0c;因为是新手上路系列; electron-builder.yml artifactName: 安装文件名称; 【 n a m e 和 {name}和 name和{version}是package.json中的name和version】 shortcutNa…

torch.nn.TransformerEncoderLayer层介绍

nn.TransformerEncoderLayer 是 PyTorch 中 Transformer 模型的基本组成部分之一,它用于处理序列数据,通常是用来编码输入的序列特征。在 Transformer 中,编码器由多个这样的层堆叠而成。 每个 TransformerEncoderLayer 由两部分组成: 多头自注意力机制(Multi-head Self-…