Linux基础项目开发1:量产工具——业务系统(七)

简介: Linux基础项目开发1:量产工具——业务系统(七)

一、流程代码框架

1.业务系统流程图

2.主页面流程图

3.main.c

 
#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)
{
  PDispBuff ptBuffer;
  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; 
}

第22~26行:打印用法

第29~35行:初始化显示系统

       第29行:初始化显示设备

       第30行:选择默认的显示设备

       第31行:初始化这个默认的设备

       第35行:获得显示区域

第38、39行:初始化输入系统

       第38行:初始化输入设备

       第39行:选择默认输入设备

第43~50行:初始化文字系统

       第45行:选择字库

第53行:初始化页面系统

第55行:运行业务系统的主页面

4.main_page.c

 
#include <page_manager.h>
#include <stdio.h>
 
static void MainPageRun(void *pParams)
{
  /* 读取配置文件 */
 
  /* 根据配置文件生成按钮、界面 */
 
  while (1)
  {
    /* 读取输入事件 */
 
    /* 根据输入事件找到按钮 */
 
    /* 调用按钮的OnPressed函数 */
  }
}
 
static PageAction g_tMainPage = {
  .name = "main",
  .Run  = MainPageRun,
};
 
void MainPageRegister(void)
{
  PageRegister(&g_tMainPage);
}
 
 

二、处理配置文件

       对于我们要制作的页面,里面有很多按钮,那么我们怎么知道每个按钮里面都存放着什么呢?这时我们就需要配置文件,以后我们想要打开哪个按钮,调整测试模块时,可以直接修改配置文件即可,就不需要重新修改,编译程序就可以带来非常大的灵活性。

1.配置文件的样子

2. 怎么处理配置文件

对于配置文件的每一行,都创建一个ItemCfg结构体

并对外提供函数,比如获得某个配置项

3.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);
PItemCfg GetItemCfgByName(char *name);
 
 
#endif

第7行:解析配置文件,得到的结构体保存到数组里,数组的内存大小为30

第8行:提供默认的配置文件

第11行:描述配置文件的哪一项

第12行:描述配置文件的名字

第13行:描述能否被点击

第14行:状态发生变化时调用某个命令

第17行:传入配置文件的名字解析配置文件,得到ItemCfg结构体

第18行:确定有多少行,也就是确定出有多少个按钮

第19行:传入某个index,返回某个配置项

第20行:传入名字,返回某个配置项

4.config.c

 
#include <string.h>
#include <config.h>
#include <stdio.h>
 
static ItemCfg g_tItemCfgs[ITEMCFG_MAX_NUM];
static int g_iItemCfgCount = 0;
 
int ParseConfigFile(char *strFileName)
{
  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;
}
 
PItemCfg GetItemCfgByIndex(int index)
{
  if (index < g_iItemCfgCount)
    return &g_tItemCfgs[index];
  else
    return NULL;
}
 
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;
}

第6行:解析配置文件,得到ItemCfg结构体保存到g_tItemCfgs[ITEMCFG_MAX_NUM]这个数组里

第7行:解析配置文件以后,记录配置文件有多少项

int ParseConfigFile(char *strFileName)

第9~45行: 进行解析配置文件

       第13行:读进一行放到buf数组里

       第16行:打开文件 ,只需要读(r)即

       第19行:如果没有打开的话,打印不能打开这个文件,表示配置失败

       第23行:fgets(buf, 100, fp)读入一行

       第26行:读出每一行,最大数量是100

       第29~31行:吃掉开头的空格TAB

       第34~35行:忽略注释

       第38~42行:进行处理

               第38行:根据command[0] 是否为 '\0' 来判断g_tItemCfgs里是否有命令,如果第0项没有command就表明command从配置文件里面得到了他的值

       第44行:返回0表示成功

int GetItemCfgCount(void)

第47~50行:确定有多少行,也就是确定出有多少个按钮,直接获得g_iItemCfgCount即可

PItemCfg GetItemCfgByIndex(int index)

第52~58行:通过index获得某一项

PItemCfg GetItemCfgByName(char *name)

第60~69行:通过名字获得某一项

三、生成界面

想显示这样的界面

通过配置文件可以知道有哪些按钮,知道每个按钮的名字

只剩最后一项了:怎么确定每个按钮的位置、大小?需要计算!

1.计算每个按钮的Region

2.main_page.c

#include <ui.h>
#include <config.h>
#include <page_manager.h>
#include <stdio.h>
#include <math.h>
#include <string.h>
#define X_GAP 5
#define Y_GAP 5
 
static Button g_tButtons[ITEMCFG_MAX_NUM];
 
 
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 */
  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, NULL);
      i++;
    }
  }
 
  /* OnDraw */
  for (i = 0; i < n; i++)
    g_tButtons[i].OnDraw(&g_tButtons[i], pDispBuff);
}
 
static void MainPageRun(void *pParams)
{
  int error;
  /* 读取配置文件 */
  error = ParseConfigFile();
  if (error)
    return;
 
  /* 根据配置文件生成按钮、界面 */
  GenerateButtons();
 
  while (1)
  {
    /* 读取输入事件 */
 
    /* 根据输入事件找到按钮 */
 
    /* 调用按钮的OnPressed函数 */
  }
}
 
static PageAction g_tMainPage = {
  .name = "main",
  .Run  = MainPageRun,
};
 
void MainPageRegister(void)
{
  PageRegister(&g_tMainPage);
}
 
 

第77~75行:读取配置文件

第80~89行:根据配置文件生成按钮、界面

static void GenerateButtons(void)

第13~69行:生成按钮

       第28~36行:算出单个按钮的width/height

               第28行:算出有多少个按钮

               第30~32行:获得屏幕分辨率

               第33行:算出按钮理想宽度

               第34行:算出每一行最多显示出多少个按钮

               第35行:再反算出实际按钮宽度

               第36行:根据宽度算出高度

       第39~44行:居中显示:  计算每个按钮的区域region

               第39~44行:算出左上角的具体位置

                         第40行:算出有多少行

                         第41行:如果rows * n_per_line < n则有小数出现,需要给rows++

       第46~64行:计算每个按钮的区域region

               第49、50行:前一个按钮的位置

               第54、55行:计算下一个按钮的位置

               第56、57行:计算下一个按钮的宽度和高度,并且留5个距离的间隙

               第58行:更新起始位置

               第61行:初始化按钮

                               GetItemCfgByIndex(i)->name从配置文件中得到

                       

void InitButton(PButton ptButton, char *name, PRegion ptRegion, ONDRAW_FUNC OnDraw, ONPRESSED_FUNC OnPressed)
{
  ptButton->status = 0;
  ptButton->name = name;
  if (ptRegion)
    ptButton->tRegion = *ptRegion;
  ptButton->OnDraw    = OnDraw ? OnDraw : DefaultOnDraw;
  ptButton->OnPressed = OnPressed ? OnPressed : DefaultOnPressed;
}

       第67~68行:显示出按钮

 

四、处理输入事件

我们得到的输入事件,可能来自触摸屏,也可能是其他APP发来的网络数据

对于触摸屏事件,根据iXiY找到按钮

对于网络数据,我们限定为这样的格式:

 “name ok”、“name err”、“name 70%

 根据name找到按钮

对于按钮,我们要提供自己的OnPressed函数,不适用UI系统默认的函数

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 5
 
static 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;     
    }
    else
      return -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);
}

第86行:计算按钮的个数

第119行:初始化按钮时候提供我们自己的处理函数 MainPageOnPressed

第195~209行:

       第198~200行:读取输入事件,得到的数据保存在tInputEvent这个结构体里

       第203~205行:根据输入事件找到按钮

       第208行:调用按钮的OnPressed函数

static PButton GetButtonByInputEvent(PInputEvent ptInputEvent)

第155~178行:根据输入事件找到按钮

       第160~167行:判断事件的类型是否为触摸屏

               第164行:遍历每个按钮并且找寻触点

       第168~172行:判断事件的类型是否为网络数据

               第170行:将网络数据名字提取出来

               第171行:根据名字找到按钮

       

static int isTouchPointInRegion(int iX, int iY, PRegion ptRegion)

第129~138行:寻找触点

static PButton GetButtonByName(char *name)

第141~152行:根据网络数据输入的名字找到按钮

static int MainPageOnPressed(struct Button *ptButton, PDispBuff ptDispBuff, PInputEvent ptInputEvent)

第15~68行:之前是默认的处理函数,现在我们要提供自己自定义的处理函数

        第17行:设置默认按钮颜色为红色

        第25行:判断是否是触摸屏事件

                第28行:判断是否能被点击

                       第32行:修改颜色

       第36行:判断是否为网络事件  

               第41~52行:根据传入的字符串进行判断是否修改颜色

               在ui.h里面多定义一个颜色”蓝色“

#define BUTTON_PERCENT_COLOR 0x0000ff

献上韦东山老师对以上代码流程的梳理过程

电子产品量产工具业务系统代码流程梳理

五、综合测试:

1.business目录的Makefile

EXTRA_CFLAGS  := 
CFLAGS_file.o := 
 
obj-y += main.o

2.config目录的Makefile

EXTRA_CFLAGS  := 
CFLAGS_file.o := 
 
obj-y += config.o

3.顶层目录的Makefile

 
CROSS_COMPILE ?= 
AS    = $(CROSS_COMPILE)as
LD    = $(CROSS_COMPILE)ld
CC    = $(CROSS_COMPILE)gcc
CPP   = $(CC) -E
AR    = $(CROSS_COMPILE)ar
NM    = $(CROSS_COMPILE)nm
 
STRIP   = $(CROSS_COMPILE)strip
OBJCOPY   = $(CROSS_COMPILE)objcopy
OBJDUMP   = $(CROSS_COMPILE)objdump
 
export AS LD CC CPP AR NM
export STRIP OBJCOPY OBJDUMP
 
CFLAGS := -Wall -O2 -g
CFLAGS += -I $(shell pwd)/include
 
LDFLAGS := -lts -lpthread -lfreetype -lm
 
export CFLAGS LDFLAGS
 
TOPDIR := $(shell pwd)
export TOPDIR
 
TARGET := test
 
 
obj-y += display/
obj-y += input/
obj-y += font/
obj-y += ui/
obj-y += page/
obj-y += config/
obj-y += business/
 
all : start_recursive_build $(TARGET)
  @echo $(TARGET) has been built!
 
start_recursive_build:
  make -C ./ -f $(TOPDIR)/Makefile.build
 
$(TARGET) : built-in.o
  $(CC) -o $(TARGET) built-in.o $(LDFLAGS)
 
clean:
  rm -f $(shell find -name "*.o")
  rm -f $(TARGET)
 
distclean:
  rm -f $(shell find -name "*.o")
  rm -f $(shell find -name "*.d")
  rm -f $(TARGET)
  
obj-y += display/
obj-y += input/
obj-y += font/
obj-y += ui/
obj-y += page/
obj-y += config/
obj-y += business/
LDFLAGS := -lts -lpthread -lfreetype -lm

六、上板测试

1.ubuntu上:

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

2.开发板上:

[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"

3.测试结果:

当led测试没问题时,可以点击led

用客户端进行网络测试


目录
相关文章
|
1天前
|
弹性计算 Shell Linux
|
1天前
|
Web App开发 监控 Unix
Linux 常用命令汇总(七):进程管理 & 系统权限 & 用户授权
Linux 常用命令汇总(七):进程管理 & 系统权限 & 用户授权
|
2天前
|
存储 Unix Linux
【Linux系统编程】基础指令(三)
【Linux系统编程】基础指令(三)
|
2天前
|
Linux
【Linux系统编程】基础指令(二)(下)
【Linux系统编程】基础指令(二)
|
2天前
|
Linux C语言
【Linux系统编程】基础指令(二)(上)
【Linux系统编程】基础指令(二)
|
1天前
|
存储 Linux Shell
linux课程第二课------命令的简单的介绍2
linux课程第二课------命令的简单的介绍2
|
1天前
|
JSON 网络协议 Linux
Linux ip命令:网络的瑞士军刀
【4月更文挑战第25天】
7 1
|
1天前
|
安全 Linux C语言
linux课程第一课------命令的简单的介绍
linux课程第一课------命令的简单的介绍