1. C语言使用enum做顺序执行状态机的思考

最近做完了通过8266 Qcloud AT做腾讯连连的台灯的项目, 使用enum做连接状态记录, 在做网络连接的过程中发现了一些问题, 这儿讨论一下.

命名

typedef enum _ESP_STATUS{
  ESP_STATUS_INIT,
  ESP_STATUS_READY,
  ESP_STATUS_GOT_MAC,
  ESP_STATUS_CONFIGED_NTP_SERVER,
  ESP_STATUS_GOT_TIME,
  ESP_STATUS_CONNECTED_MQTT,
  ESP_STATUS_SUBED_MQTT, 
  ESP_STATUS_WIFI_DISCONNECTTED,
}ESP_STATUS;

上面这种enum用下划线, type去掉下划线, 并在枚举项内体现type名称的命名习惯从华为开始就确定了. 这个不需要讨论.
从上到下依次为按时间顺序会依次进入的状态, 这个也没有问题.
GOT_MAC先动作再对象也是对的.
想讨论的READY/GOT/CONFIGED这种过去时态. 我认为不如进行时态. 过去时态会造成当前状态与上一个状态在意义上的耦合. 因为READY以后就要获取MAC, 所以READY不如改名为GETTING_MAC, GOT_MAC不如改名为CONFIGING_NTP_SERVER. 也就是凡是要主动做操作的, 都用进行时态.
另外还有一种是处在状态中不主动动作, 而是被动等待状态改变(相当于什么也不做). 名称应改为WAITFOR_DO_XXX, 如WAITFOR_RECONNECT_WIFI.
修改如下:

typedef enum _ESP_STATUS{
  ESP_STATUS_WAITFOR_READY, 
  ESP_STATUS_GETTING_MAC,
  ESP_STATUS_CONFIGING_NTP_SERVER,
  ESP_STATUS_GETTING_TIME,
  ESP_STATUS_CONNECTING_MQTT,
  ESP_STATUS_SUBING_MQTT,
  ESP_STATUS_REPORTING_MQTT_PROPERTY,
  ESP_STATUS_WAITFOR_CONNECT_WIFI
}ESP_STATUS;

其中, ESP_STATUS_SUBING_MQTT又可以分为对SERVICE和PROPERTY分别SUB, 两者虽然没有先后之分, 但是仍应该人为规定先后顺序并放入枚举项中. 改为:

typedef enum _ESP_STATUS{
  ESP_STATUS_INIT, // 这时候等待READY
  ESP_STATUS_GETTING_MAC,
  ESP_STATUS_CONFIGING_NTP_SERVER,
  ESP_STATUS_GETTING_TIME,
  ESP_STATUS_CONNECTING_MQTT,
  ESP_STATUS_SUBING_MQTT_SERVICE,
  ESP_STATUS_SUBING_MQTT_PROPERTY,
  ESP_STATUS_REPORTING_MQTT_PROPERTY,
}ESP_STATUS;

2. C语言文件结构

.h文件是用来放置public公有性质的内容, #define 常量, typedef enum, typedef struct, extern 全局变量, 公用函数声明返回值 函数值(参数列表);. 不可以做c文件的私有全局变量声明, 也不可以做函数定义, 也不可以做c文件私有函数声明.
.c文件用来放置公有和私有的全局变量定义, 私有函数声明, 共有和私有的函数定义.
.h文件内部由于有了#ifndef 头文件名 #define 头文件名 #endif的结构, 所以可以#include其他.h文件.
.c文件任何时候都不应被其他文件#include
将1对同名.h.c文件作为一个模块的话, 项目中为模块划分层次, 底层模块为上层提供服务, 设置基本共识. 需要注意的是上层模块可以#include下层模块, 但下层模块不能#include上层模块, 也就是说, 下层不应依赖上层, 上层的任何变化不应使下层模块无法使用, 无论上层如何变化下层都应提供一致的服务.
为进一步让上层解耦, 摆脱对特定实现方式的依赖, 下层可以定义标准接口性质的.h文件, 上层#include和调用标准.h, 下层在不修改标准.h的情况下进行升级修改, 或者完全替换为另一种实现.
同层模块可以相互#include.
目前考虑, 项目基础配置处于最底层L0. 硬件资源驱动处于L1, 基于硬件驱动提供的服务处于L2, 操作系统处于L3, 基于操作系统提供的服务处于L4, 具体应用处于L5.
按这种划分, 以灯为例, 作表如下:

层数 层名 功能模块
L5 应用层 灯的逻辑功能
L4 系统服务层 系统Tick的时钟
L3 系统层 RTOS
L2 硬件服务层 驱动回调
L1 硬件驱动层 Uart, Adc
L0 配置层

标签: none 阅读量: 1336

添加新评论