在物联网中,定时控制服务可能是非常非常重要的一种服务,而服务器如何将控制信息,发送给设备,让设备在某时某分周几,每周的星期几,每月的几号开关或者进行其他的控制。下面详细的定义了控制命令的格式:
定时器1; 定时器2; 定时器3; 定时器4; 定时器5;… 每个定时器的格式如下: 秒 分 小时 日 月 周 年 执行命令
序号 说明 范围 允许的通配符 备注 1 秒 0-59 , - * / 2 分 0-59 , - * / 3 小时 0-23 , - * / 4 日 1-31 , - * ? / 5 月 1-12 , - * / 6 周 0-6 , - * ? / 0表示星期日 7 年 2000-2099 , - * / 8 执行命令 SwitchOn SwitchOff LeakTest 通配符说明: *表示所有值。 例如:在分的字段上设置 "*",表示每一分钟都会触发。 ? 表示不指定值。 使用的场景为不需要关心当前设置这个字段的值。例如:要在每月的10号触发一个操作,但不关心是周几,所以需要周位置的那个字段设置为"?" ,后面代码会根据周几和每月几号中的一个去判断是否到达某分时刻 - 表示区间。例如 在小时上设置 "10-12",表示 10,11,12点都会触发。 , 表示指定多个值。 例如在周字段上设置 "1,3,5" 表示周一,周三和周五触发 /用于递增触发。 例如在秒上面设置"5/15" 表示从5秒开始,每增15秒触发(5,20,35,50)。 常用示例: 0 0 8 * * ? * SwitchOn 每天8点开启 0 0 17 * * ? * SwitchOff 每天17点关闭 0 0 8 ? * 1-5 * SwitchOn 周一至周五8点开启 0 0 17 ? * 1-5 * SwitchOff 周一至周五17点关闭 0 0 8 ? 4-10 1-5 * SwitchOn 4至10月份周一至周五8点开启 0 0 17 ? 4-10 1-5 * SwitchOff 4至10月份周一至周五17点关闭 0 0 0 1 * ?* LeakTest 每月1号00:00:00进行自测
我们实现以上方法就根据控制信息解析成时间结构体,然后填充,填充后将本地或者网络时间转换成同样的时间结构体,然后去对每一位进行对比(每一位代表一天或者一分,如60分就用8个字节存储,当月日时分秒都是置1时就认为到达了时间结构体),然后响应动作
具体查看已下代码:
注意几点写在前面:
- 提供给上层用户的API其实就是几个,具体怎么使用请查看每个函数的作用
- TimerTask() 需在绝对定时器1s调用一次
- UpdateTimeConfigIinfo() 需在开机配置信息初始化和服务器下达命令时自主调用
__weak bool GetRealTime (struct tm *ptOutTime); 需要自己根据实际情况填充,如RTC或者实时网络获取等等
__weak bool pTimerOverAction(ETimerActionType eAction); 这两个是虚函数,必须由调用定时模块的用户重新定义,
2. 可用于一个设备设置多个定时,当多个设备设置索格定时时,需要修改此代码
3. 全部存储方式位小端方式,大神写的大端的存储,算法由于本人实在太菜看不懂,就自己写的一个非常简单的处理方式,实现方法很简单,已下代码已测试,可直接使用
1 #ifndef __TIMER_MODULE_H 2 #define __TIMER_MODULE_H 3 #include <stdio.h> 4 #include <stdbool.h> 5 #include <time.h> 6 #include "tool.h" 7 #include "ModuleLibOpt.h" 8 9 10 #if (TIME_MODULE == 1) 11 #define TIMERS_CONFIG_STR_MAX_LEN 200 12 #define TIME_STR_FRAME_LEN_MIN 8 13 #define TIMER_CONTROL_MOUDLE_MAX 4 14 #define TIMERS_CONIFG_STR_ONCE_LEN 40 /* 逗号字符数量 */ 15 16 17 18 #define TIMER_BITS_NUM_DOW 7 19 #define TIMER_BITS_NUM_MONS 12 20 #define TIMER_BITS_NUM_HOURS 24 21 #define TIMER_BITS_NUM_DAYS 32 22 #define TIMER_BITS_NUM_MINS 60 23 #define TIMER_BITS_NUM_SECS 60 24 #define TIMER_BITS_NUM_YEARS 100 25 26 typedef enum { 27 TIMER_ACTION_TYPE_UNKNOWN = 0, 28 TIMER_ACTION_TYPE_SWITCHON = 1, /* 开启断路器 */ 29 TIMER_ACTION_TYPE_SWITCHOFF = 2, /* 关闭断路器 */ 30 TIMER_ACTION_TYPE_LEAKTEST = 3, /* 漏保自检 */ 31 TIMER_ACTION_TYPE_MAX 32 }ETimerActionType; 33 34 typedef struct TTimerCtl 35 { 36 ETimerActionType m_eAction; 37 38 /* orderd by bit */ 39 uint8_t m_byDow; /* 0-6 bit, beginning sunday */ 40 uint8_t m_abyMons[2]; /* 0-11 bit */ 41 uint8_t m_abyHrs[3]; /* 0-23 bit */ 42 uint8_t m_abyDays[4]; /* 1-31 bit */ 43 uint8_t m_abyMins[8]; /* 0-59 bit */ 44 uint8_t m_abySecs[8]; /* 0-59 bit */ 45 #if 0 /* Not Support Now */ 46 uint8_t m_abyYears[13]; /* 0-99 bit, beginning 2000, */ 47 #endif 48 }TTimerCtl; 49 50 51 52 typedef struct TIMER_Handle_t 53 { 54 bool m_TimeStatsus; 55 uint8_t m_byTimersCount; /* 定时控制数量 */ 56 uint8_t m_abyTimerControl[TIMERS_CONFIG_STR_MAX_LEN]; /* 定时控制 */ 57 TTimerCtl m_atTimers[TIMER_CONTROL_MOUDLE_MAX]; /* 定时控制,TimerControl配置解析得到 */ 58 }TIMER_Handle; 59 60 61 TIMER_Handle *TimerRegister(void); 62 bool PerseTimers(char * pbyTimerCtrl,TTimerCtl *ptTimers,uint8_t *pbycnt); 63 bool PerseField(char *arry,int dwlen,uint8_t bybitCnt,char *pbyBuff); 64 bool UpdateTimeConfigIinfo(const char *pbydata); 65 void TimerTask(void); 66 67 __weak bool GetRealTime (struct tm *ptOutTime); 68 __weak bool pTimerOverAction(ETimerActionType eAction); 69 70 71 #endif 72 73 #endif
1 TIMER_Handle g_tTimer = { 2 false, 3 TIMER_CONTROL_MOUDLE_MAX, 4 {0}, 5 {0} 6 }; 7 /*==================================================================== 8 函数名:TimerRegister 9 功 能:获取定时模块操作句柄 10 输入参数说明: 11 输出参数说明: 12 返回值说明 :当前任务操作句柄 13 ====================================================================*/ 14 15 TIMER_Handle *TimerRegister(void) 16 { 17 return &g_tTimer; 18 } 19 /*==================================================================== 20 函数名:GetRealTime 21 功 能:获取当前真实时间 22 输入参数说明: 23 pbydata :控制命令 24 输出参数说明: 25 返回值说明 : 26 备 注:需要用户在初始化的时候自己注册 27 ====================================================================*/ 28 __weak bool GetRealTime (struct tm *ptOutTime) 29 { 30 return true; 31 } 32 /*==================================================================== 33 函数名:UpdateTimeConfigIinfo 34 功 能:更新一下定时的命令,服务器下载的时候和从flash读取配置用 35 输入参数说明: 36 pbydata :控制命令 37 输出参数说明: 38 返回值说明 : 39 ====================================================================*/ 40 bool UpdateTimeConfigIinfo(const char *pbydata) 41 { 42 u16 wRecvLen = 0; 43 u8 byTimerCnt = 0; 44 bool bRet = false; 45 wRecvLen = strlen((pbydata)); 46 if( wRecvLen >= TIME_STR_FRAME_LEN_MIN && wRecvLen <= TIMERS_CONFIG_STR_MAX_LEN) 47 { 48 memset(g_tTimer.m_abyTimerControl,0,sizeof(g_tTimer.m_abyTimerControl)); 49 memset(g_tTimer.m_atTimers,0,sizeof(g_tTimer.m_atTimers)); 50 memcpy(g_tTimer.m_abyTimerControl,pbydata,wRecvLen); 51 byTimerCnt = TIMER_CONTROL_MOUDLE_MAX; 52 bRet = PerseTimers((char *)(g_tTimer.m_abyTimerControl),g_tTimer.m_atTimers, &byTimerCnt); 53 if(bRet) 54 { 55 g_tTimer.m_TimeStatsus = true; 56 g_tTimer.m_byTimersCount = byTimerCnt; 57 } 58 } 59 else 60 { 61 g_tTimer.m_TimeStatsus = false; 62 memset(g_tTimer.m_abyTimerControl,0,sizeof(g_tTimer.m_abyTimerControl)); 63 memset(g_tTimer.m_atTimers,0,sizeof(g_tTimer.m_atTimers)); 64 } 65 } 66 67 /*==================================================================== 68 函数名:pTimerOverAction 69 功能: 到达延时,操作响应函数 70 输入参数说明: 71 ETimerActionType: 操作类型 开/关/自检 72 输出参数说明: 73 返回值说明 : 74 备 注:需要用户在初始化的时候自己注册 75 ====================================================================*/ 76 __weak bool pTimerOverAction(ETimerActionType eAction) 77 { 78 return true; 79 } 80 81 /*==================================================================== 82 函数名:TimerTask 83 功能: 定时调用函数,绝对定时器中1s调用一次 84 输入参数说明: 85 输出参数说明: 86 返回值说明: 87 ====================================================================*/ 88 void TimerTask(void) 89 { 90 u8 byAddr = 0, byTimerIndex = 0; 91 TTimerCtl *ptTimer = NULL; 92 struct tm ptm = {0}; 93 if(false == g_tTimer.m_TimeStatsus) 94 return ; 95 96 GetRealTime(&ptm); 97 98 if(g_tTimer.m_TimeStatsus) 99 { 100 for(byTimerIndex = 0; byTimerIndex < g_tTimer.m_byTimersCount; byTimerIndex++) 101 { 102 ptTimer = &(g_tTimer.m_atTimers[byTimerIndex]); 103 if(TestBit(ptTimer->m_abyMins, sizeof(ptTimer->m_abyMins), ptm.tm_min) == true && 104 TestBit(ptTimer->m_abyHrs, sizeof(ptTimer->m_abyHrs), ptm.tm_hour) == true && 105 (TestBit(ptTimer->m_abyDays, sizeof(ptTimer->m_abyDays), ptm.tm_mday) == true || TestBit(&(ptTimer->m_byDow), sizeof(ptTimer->m_byDow), ptm.tm_wday) == true) && 106 TestBit(ptTimer->m_abyMons, sizeof(ptTimer->m_abyMons), ptm.tm_mon) == true) 107 { 108 pTimerOverAction(ptTimer->m_eAction); 109 } 110 } 111 } 112 } 113 114 /*==================================================================== 115 函数名:FixDayDow 116 功 能:根据周/日调整,判断使用的是什么定时 117 输入参数说明: 118 ptTimer :时间控制结构体 119 输出参数说明: 120 121 返回值说明: 122 ====================================================================*/ 123 static void FixDayDow(TTimerCtl *ptTimer) 124 { 125 unsigned i; 126 int weekUsed = 0; 127 int daysUsed = 0; 128 129 for (i = 0; i < TIMER_BITS_NUM_DOW; ++i) 130 { 131 if (TestBit(&(ptTimer->m_byDow), sizeof(ptTimer->m_byDow), i) == false) 132 { 133 weekUsed = 1; 134 break; 135 } 136 } 137 for (i = 0; i < TIMER_BITS_NUM_DAYS; ++i) 138 { 139 if (TestBit(ptTimer->m_abyDays, sizeof(ptTimer->m_abyDays), i) == false) 140 { 141 daysUsed = 1; 142 break; 143 } 144 } 145 if (weekUsed != daysUsed) 146 { 147 if (weekUsed) 148 memset(ptTimer->m_abyDays, 0, sizeof(ptTimer->m_abyDays)); 149 else /* daysUsed */ 150 memset(&(ptTimer->m_byDow), 0, sizeof(ptTimer->m_byDow)); 151 } 152 } 153 154 /*==================================================================== 155 函数名:PerseField 156 功 能:解析字段 157 输入参数说明: 158 dwlen :需要保存的数组的长度 159 bybitCnt:一共有多少位 160 pbyBuff :待解析字段 161 输出参数说明: 162 arry :需要保存的数组 163 返回值说明: 解析成功/失败 164 ====================================================================*/ 165 bool PerseField(char *arry,int dwlen,uint8_t bybitCnt,char *pbyBuff) 166 { 167 if(NULL == arry || NULL == pbyBuff || dwlen == 0 || bybitCnt == 0 || (bybitCnt / 8 > dwlen)) 168 return false; 169 170 uint8_t i; 171 int sta,end; 172 char *pBase = pbyBuff; 173 const char *delim_t = ","; 174 char *token_t = NULL,*saveptr_t = NULL; 175 char byTemp[TIMERS_CONIFG_STR_ONCE_LEN] = {0}; 176 uint8_t bydiv = bybitCnt / 8; 177 uint8_t byred = bybitCnt % 8 - 1; 178 179 if(*pBase == '*' || *pBase == '?') 180 { 181 for(i =0; i < bybitCnt; i ++ ) 182 SetBit(arry,dwlen,i); 183 } 184 else if(isdigit(*pBase)) 185 { 186 if(NULL != strstr(pBase,"-")) 187 { 188 sscanf(pBase,"%d-%d",&sta,&end); 189 for(i =sta; i <= end; i ++ ) 190 SetBit(arry,dwlen,i); 191 } 192 else if(NULL != strstr(pBase,",")) 193 { 194 i = 0; 195 memcpy(byTemp,pBase,strlen(pBase)); 196 pBase = byTemp; 197 while(NULL != (token_t = strtok_r(pBase,delim_t,&saveptr_t))) 198 { 199 SetBit(arry,dwlen,atoi(token_t)); 200 pBase = NULL; 201 } 202 } 203 else if(NULL != strstr(pBase,"/")) 204 { 205 sscanf(pBase,"%d/%d",&sta,&end); 206 for(i =sta; i <= bybitCnt; i += (end - 1)) 207 SetBit(arry,dwlen,i); 208 } 209 else 210 { 211 sscanf(pBase,"%d",&sta); 212 SetBit(arry,dwlen,sta); 213 } 214 215 } 216 else 217 return false; 218 219 return true; 220 221 } 222 223 /*==================================================================== 224 函数名:PerseTimers 225 功 能:将服务器控制命令解析 226 输入参数说明: 227 pbyTimerCtrl :服务下达的控制命令 228 输出参数说明: 229 TTimerCtl :解析成控制命令结构体 230 pbycnt :解析成的第几个定时器 231 返回值说明: 解析成功/失败 232 ====================================================================*/ 233 bool PerseTimers(char * pbyTimerCtrl,TTimerCtl *ptTimers,uint8_t *pbycnt) 234 { 235 char strbuf[TIMERS_CONFIG_STR_MAX_LEN]; 236 char *pbyPos = strbuf; 237 char *token_t = NULL, *saveptr_t =NULL; 238 char *token_f = NULL, *saveptr_f =NULL; 239 char *delim_t = ";" , *delim_f = " "; 240 char *savefield[8]; 241 uint8_t byFieldCnt = 0; 242 uint8_t byTimeCnt = 0; 243 uint16_t dwLen = strlen(pbyTimerCtrl); 244 TTimerCtl *timer = NULL; 245 246 if(NULL == pbyTimerCtrl || NULL == ptTimers || NULL == pbycnt || dwLen > TIMERS_CONFIG_STR_MAX_LEN) 247 return false; 248 249 memset(ptTimers,0,sizeof(TTimerCtl)); 250 memcpy(strbuf,pbyTimerCtrl,strlen(pbyTimerCtrl)); 251 printf("strbuf:%s ",strbuf); 252 while(NULL != (token_t = strtok_r(pbyPos,delim_t,&saveptr_t))) 253 { 254 while(*token_t == ' ') 255 { 256 token_t++; 257 } 258 printf("token_t:%s ",token_t); 259 memset(savefield,0,sizeof(savefield)); 260 261 while(NULL != (token_f = strtok_r(token_t,delim_f,&saveptr_f))) 262 { 263 while(*token_f == ' ') 264 { 265 token_t++; 266 } 267 savefield[byFieldCnt] = token_f; 268 printf("field[%d]:%s ",byFieldCnt,savefield[byFieldCnt]); 269 270 byFieldCnt++; 271 token_t = NULL; 272 } 273 274 if(byFieldCnt >= 8) 275 { 276 timer = &ptTimers[byTimeCnt]; 277 278 PerseField((char *)timer->m_abySecs,sizeof(timer->m_abySecs),TIMER_BITS_NUM_SECS,savefield[0]); 279 PerseField((char *)timer->m_abyMins,sizeof(timer->m_abyMins),TIMER_BITS_NUM_MINS,savefield[1]); 280 PerseField((char *)timer->m_abyHrs, sizeof(timer->m_abyHrs),TIMER_BITS_NUM_HOURS,savefield[2]); 281 PerseField((char *)timer->m_abyDays,sizeof(timer->m_abyDays),TIMER_BITS_NUM_DAYS,savefield[3]); 282 PerseField((char *)timer->m_abyMons,sizeof(timer->m_abyMons),TIMER_BITS_NUM_MONS,savefield[4]); 283 PerseField((char *)&(timer->m_byDow),sizeof(timer->m_byDow), TIMER_BITS_NUM_DOW,savefield[5]); 284 /* year not surport now */ 285 } 286 287 FixDayDow(timer); 288 289 if(strcmp(savefield[7], "SwitchOn") == 0) 290 { 291 timer->m_eAction = TIMER_ACTION_TYPE_SWITCHON; 292 } 293 else if(strcmp(savefield[7], "SwitchOff") == 0) 294 { 295 timer->m_eAction = TIMER_ACTION_TYPE_SWITCHOFF; 296 } 297 else if(strcmp(savefield[7], "LeakTest") == 0) 298 { 299 timer->m_eAction = TIMER_ACTION_TYPE_LEAKTEST; 300 } 301 else 302 { 303 continue; 304 } 305 306 byTimeCnt++; 307 byFieldCnt = 0; 308 pbyPos = NULL; 309 } 310 *pbycnt = byTimeCnt; 311 return true; 312 313 }
1 /*==================================================================== 2 函数名:SetBit 3 功 能:将字符串的某一位置1 4 输入参数说明: 5 date :传入的字符串 6 byCnt:传入字节数 7 byBit:第几位 8 输出参数说明: 9 10 返回值说明: 11 ====================================================================*/ 12 void SetBit(char *date,uint8_t byCnt,uint8_t byBit) 13 { 14 if(byBit / 8 >= byCnt) return ; 15 uint8_t bydiv = byBit / 8; 16 uint8_t byred = byBit % 8; 17 18 *(date + bydiv) |= 1 << byred; 19 printf("byBit:%d date:%02x ",byBit,*(date + bydiv)); 20 } 21 22 23 24 /*==================================================================== 25 函数名:TestBit 26 功 能:判断某位是否置1 27 输入参数说明: 28 pbydata :传入数组 29 byCnt :传入数组长度 30 byBit :待判断的位 31 输出参数说明: 32 33 返回值说明: 置1/置0 34 ====================================================================*/ 35 bool TestBit(uint8_t *pbydata,uint8_t byCnt,uint8_t byBit) 36 { 37 if(byBit / 8 >= byCnt) return false; 38 39 uint8_t *pBytePos = NULL; 40 uint8_t pBitPos = byBit % 8; 41 42 pBytePos = pbydata + byBit / 8; 43 44 if(*pBytePos & (1 << pBitPos)) 45 { 46 return true; 47 } 48 else 49 { 50 return false; 51 } 52 }
1 /*==================================================================== 2 函数名:SetBit 3 功 能:将字符串的某一位置1 4 输入参数说明: 5 date :传入的字符串 6 byCnt:传入字节数 7 byBit:第几位 8 输出参数说明: 9 10 返回值说明: 11 ====================================================================*/ 12 void SetBit(char *date,uint8_t byCnt,uint8_t byBit) 13 { 14 if(byBit / 8 >= byCnt) return ; 15 uint8_t bydiv = byBit / 8; 16 uint8_t byred = byBit % 8; 17 18 *(date + bydiv) |= 1 << byred; 19 printf("byBit:%d date:%02x ",byBit,*(date + bydiv)); 20 } 21 22 23 24 /*==================================================================== 25 函数名:TestBit 26 功 能:判断某位是否置1 27 输入参数说明: 28 pbydata :传入数组 29 byCnt :传入数组长度 30 byBit :待判断的位 31 输出参数说明: 32 33 返回值说明: 置1/置0 34 ====================================================================*/ 35 bool TestBit(uint8_t *pbydata,uint8_t byCnt,uint8_t byBit) 36 { 37 if(byBit / 8 >= byCnt) return false; 38 39 uint8_t *pBytePos = NULL; 40 uint8_t pBitPos = byBit % 8; 41 42 pBytePos = pbydata + byBit / 8; 43 44 if(*pBytePos & (1 << pBitPos)) 45 { 46 return true; 47 } 48 else 49 { 50 return false; 51 } 52 }