用于小型嵌入式的定时控制服务

      在物联网中,定时控制服务可能是非常非常重要的一种服务,而服务器如何将控制信息,发送给设备,让设备在某时某分周几,每周的星期几,每月的几号开关或者进行其他的控制。下面详细的定义了控制命令的格式:

定时器1;    定时器2;    定时器3;    定时器4;    定时器5;…
每个定时器的格式如下:
秒 分 小时 日 月 周 年 执行命令
序号 说明 范围 允许的通配符 备注
10-59 , - * / 20-59 , - * / 3 小时 0-23 , - * / 41-31 , - * ? / 51-12 , - * / 60-6 , - * ? / 0表示星期日 72000-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 84-10 1-5 * SwitchOn 4至10月份周一至周五8点开启 0 0 174-10 1-5 * SwitchOff 4至10月份周一至周五17点关闭 0 0 0 1 * ?* LeakTest 每月1号00:00:00进行自测

我们实现以上方法就根据控制信息解析成时间结构体,然后填充,填充后将本地或者网络时间转换成同样的时间结构体,然后去对每一位进行对比(每一位代表一天或者一分,如60分就用8个字节存储,当月日时分秒都是置1时就认为到达了时间结构体),然后响应动作

具体查看已下代码:

  注意几点写在前面:

  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 }
原文地址:https://www.cnblogs.com/st-home/p/12527269.html