#include #include #include #include #include #include #include #include #include #include #include "log.h" #include "libuv_dbus.h" #define TIMER_TIMEOUT (200) #define IS_LEAP_YEAR(year) ((year) % 4 == 0 && ((year) % 100 != 0 || (year) % 400 == 0)) static unsigned char g_DayOfMonth[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; typedef struct { void* pUserData; OnAlarmTimer pOnAlarmCb; unsigned int alarmId; struct tm setDateTime; int setWeekDay; unsigned int repeatMode; struct tm onDateTime; time_t onTimestamp; unsigned int timerPriority; UT_hash_handle hh; ///< UT Hash handle } ALARM_ITEM_DATA, *PALARM_ITEM_DATA; static uv_timer_t g_uvTimer; static unsigned int g_iAlarmId = 1; static struct tm g_LocalTime; static time_t g_TimeStamp; static uv_loop_t* g_pMainLoop = NULL; static uv_rwlock_t g_uvHashRwLock; static PALARM_ITEM_DATA g_TimerTbl = NULL; const char* DumpTimerRepeatModeString(int mode) { switch(mode & 0xFF) { case REPEAT_MODE_NONE: return "NONE"; case REPEAT_MODE_EVERY_DAY: return "EVERY_DAY"; case REPEAT_MODE_WORKDAY: return "WORKDAY"; case REPEAT_MODE_HOLIDAY: return ("REPEAT_MODE_HOLIDAY"); case REPEAT_MODE_WEEKEND: return "WEEKEND"; case REPEAT_MODE_WEEKDAY: return "WEEKDAY"; case REPEAT_MODE_EVERY_MONTH_DAY: return "EVERY_MONTH_DAY"; case REPEAT_MODE_EVERY_YEAR_DAY: return "EVERY_YEAR_DAY"; case REPEAT_MODE_EVERY_TIME: return ("EVERY_TIME"); case REPEAT_MODE_MONTH_LAST_DAY: return "REPEAT_MODE_MONTH_LAST_DAY"; default: return ("Unknown Mode"); } } static int __timestampSort(PALARM_ITEM_DATA p1, PALARM_ITEM_DATA p2) { if(p1->onTimestamp == p2->onTimestamp) { return (p2->timerPriority - p1->timerPriority); } else { return (p1->onTimestamp - p2->onTimestamp); } } static int __getNextOnTimestamp(PALARM_ITEM_DATA pInfo) { int ret; struct tm setTime; time_t timestamp; if(pInfo == NULL) { return (-ERR_INPUT_PARAMS); } if(pInfo->repeatMode == REPEAT_MODE_NONE) { pInfo->onTimestamp = 0; return (-ERR_INPUT_PARAMS); } timestamp = pInfo->onTimestamp + 24 * 3600; pInfo->onTimestamp = timestamp; localtime_r(×tamp, &setTime); switch(pInfo->repeatMode) { case REPEAT_MODE_EVERY_DAY: localtime_r(×tamp, &pInfo->onDateTime); break; case REPEAT_MODE_WORKDAY: do { ret = CurrentIsWorkDay(setTime.tm_year, setTime.tm_yday); if(ret == 0) { timestamp = mktime(&setTime) + 24 * 3600; localtime_r(×tamp, &setTime); } } while(ret == 0); if(ret < 0) { pInfo->onTimestamp = 0; pInfo->onDateTime.tm_year = -1; pInfo->onDateTime.tm_mon = -1; pInfo->onDateTime.tm_mday = -1; } else { pInfo->onDateTime.tm_year = setTime.tm_year; pInfo->onDateTime.tm_mon = setTime.tm_mon; pInfo->onDateTime.tm_mday = setTime.tm_mday; pInfo->onTimestamp = mktime(&pInfo->onDateTime); } break; case REPEAT_MODE_HOLIDAY: do { ret = CurrentIsWorkDay(setTime.tm_year, setTime.tm_yday); if(ret == 0) { timestamp = mktime(&setTime) + 24 * 3600; localtime_r(×tamp, &setTime); } } while(ret == 1); if(ret < 0) { pInfo->onTimestamp = 0; pInfo->onDateTime.tm_year = -1; pInfo->onDateTime.tm_mon = -1; pInfo->onDateTime.tm_mday = -1; } else { pInfo->onDateTime.tm_year = setTime.tm_year; pInfo->onDateTime.tm_mon = setTime.tm_mon; pInfo->onDateTime.tm_mday = setTime.tm_mday; pInfo->onTimestamp = mktime(&pInfo->onDateTime); } break; case REPEAT_MODE_WEEKEND: while(setTime.tm_wday != 0 && setTime.tm_wday != 6) { timestamp = mktime(&setTime) + 24 * 3600; localtime_r(×tamp, &setTime); } pInfo->onDateTime.tm_year = setTime.tm_year; pInfo->onDateTime.tm_mon = setTime.tm_mon; pInfo->onDateTime.tm_mday = setTime.tm_mday; pInfo->onTimestamp = mktime(&pInfo->onDateTime); break; case REPEAT_MODE_WEEKDAY: if(pInfo->setDateTime.tm_wday == 0) { pInfo->setDateTime.tm_wday = 1 << 0; } else if(pInfo->setDateTime.tm_wday & (1 << 7)) { pInfo->setDateTime.tm_wday = 1 << 0; } while(((1 << setTime.tm_wday) & pInfo->setDateTime.tm_wday) == 0) { timestamp = mktime(&setTime) + 24 * 3600; localtime_r(×tamp, &setTime); } pInfo->onDateTime.tm_year = setTime.tm_year; pInfo->onDateTime.tm_mon = setTime.tm_mon; pInfo->onDateTime.tm_mday = setTime.tm_mday; pInfo->onTimestamp = mktime(&pInfo->onDateTime); break; case REPEAT_MODE_EVERY_TIME: timestamp = mktime(&g_LocalTime); if(pInfo->setDateTime.tm_hour > 0) { timestamp += pInfo->setDateTime.tm_hour * 3600; } if(pInfo->setDateTime.tm_min > 0) { timestamp += pInfo->setDateTime.tm_min * 60; } if(pInfo->setDateTime.tm_sec > 0) { timestamp += pInfo->setDateTime.tm_sec; } localtime_r(×tamp, &pInfo->onDateTime); pInfo->onTimestamp = timestamp; break; case REPEAT_MODE_MONTH_LAST_DAY: if(pInfo->onDateTime.tm_mon < 11) { pInfo->onDateTime.tm_mon++; } else { pInfo->onDateTime.tm_mon = 0; pInfo->onDateTime.tm_year++; } pInfo->onDateTime.tm_mday = g_DayOfMonth[pInfo->onDateTime.tm_mon]; if(IS_LEAP_YEAR(pInfo->onDateTime.tm_year) && (pInfo->onDateTime.tm_mon == 1)) { pInfo->onDateTime.tm_mday += 1; } pInfo->onTimestamp = mktime(&pInfo->onDateTime); break; } return (0); } static int __getOnTimestamp(PALARM_ITEM_DATA pInfo) { int ret = 0; struct tm setTime; time_t timestamp; if(pInfo == NULL) { return (-ERR_INPUT_PARAMS); } if(pInfo->setDateTime.tm_hour == -1) { pInfo->onDateTime.tm_hour = g_LocalTime.tm_hour; } else { pInfo->onDateTime.tm_hour = pInfo->setDateTime.tm_hour; } if(pInfo->setDateTime.tm_min == -1) { pInfo->onDateTime.tm_min = g_LocalTime.tm_min; } else { pInfo->onDateTime.tm_min = pInfo->setDateTime.tm_min; } if(pInfo->setDateTime.tm_sec == -1) { pInfo->onDateTime.tm_sec = g_LocalTime.tm_sec; } else { pInfo->onDateTime.tm_sec = pInfo->setDateTime.tm_sec; } switch(pInfo->repeatMode) { case REPEAT_MODE_EVERY_MONTH_DAY: pInfo->setDateTime.tm_mon = -1; pInfo->setDateTime.tm_year = -1; case REPEAT_MODE_EVERY_YEAR_DAY: pInfo->setDateTime.tm_year = -1; case REPEAT_MODE_NONE: if(pInfo->setDateTime.tm_year == -1) { pInfo->onDateTime.tm_year = g_LocalTime.tm_year; } else { pInfo->onDateTime.tm_year = pInfo->setDateTime.tm_year; } if(pInfo->setDateTime.tm_mon == -1) { pInfo->onDateTime.tm_mon = g_LocalTime.tm_mon; } else { pInfo->onDateTime.tm_mon = pInfo->setDateTime.tm_mon; } if(pInfo->setDateTime.tm_mday == -1) { pInfo->onDateTime.tm_mday = g_LocalTime.tm_mday; } else { pInfo->onDateTime.tm_mday = pInfo->setDateTime.tm_mday; } break; case REPEAT_MODE_EVERY_DAY: case REPEAT_MODE_WORKDAY: case REPEAT_MODE_WEEKEND: case REPEAT_MODE_WEEKDAY: case REPEAT_MODE_EVERY_TIME: case REPEAT_MODE_HOLIDAY: pInfo->onDateTime.tm_year = g_LocalTime.tm_year; pInfo->onDateTime.tm_mon = g_LocalTime.tm_mon; pInfo->onDateTime.tm_mday = g_LocalTime.tm_mday; break; case REPEAT_MODE_MONTH_LAST_DAY: pInfo->onDateTime.tm_year = g_LocalTime.tm_year; pInfo->onDateTime.tm_mon = g_LocalTime.tm_mon; pInfo->onDateTime.tm_mday = g_DayOfMonth[g_LocalTime.tm_mon]; if(IS_LEAP_YEAR(g_LocalTime.tm_year) && (g_LocalTime.tm_mon == 1)) { pInfo->onDateTime.tm_mday += 1; } break; } pInfo->onDateTime.tm_wday = g_LocalTime.tm_wday; pInfo->onDateTime.tm_yday = g_LocalTime.tm_yday; pInfo->onTimestamp = mktime(&pInfo->onDateTime); if(pInfo->repeatMode == REPEAT_MODE_NONE) { return (0); } memcpy(&setTime, &g_LocalTime, sizeof(struct tm)); if(mktime(&setTime) > (pInfo->onTimestamp + 1)) { if(pInfo->repeatMode == REPEAT_MODE_EVERY_MONTH_DAY) { if(pInfo->onDateTime.tm_mon < 11) { pInfo->onDateTime.tm_mon++; } else { pInfo->onDateTime.tm_mon = 0; pInfo->onDateTime.tm_year++; } pInfo->onTimestamp = mktime(&pInfo->onDateTime); return (0); } else if(pInfo->repeatMode == REPEAT_MODE_EVERY_YEAR_DAY) { pInfo->onDateTime.tm_year++; pInfo->onTimestamp = mktime(&pInfo->onDateTime); return (0); } else if(pInfo->repeatMode == REPEAT_MODE_MONTH_LAST_DAY) { if(pInfo->onDateTime.tm_mon < 11) { pInfo->onDateTime.tm_mon++; } else { pInfo->onDateTime.tm_mon = 0; pInfo->onDateTime.tm_year++; } pInfo->onDateTime.tm_mday = g_DayOfMonth[pInfo->onDateTime.tm_mon]; if(IS_LEAP_YEAR(pInfo->onDateTime.tm_year) && (pInfo->onDateTime.tm_mon == 1)) { pInfo->onDateTime.tm_mday += 1; } pInfo->onTimestamp = mktime(&pInfo->onDateTime); return (0); } else { timestamp = mktime(&setTime) + 24 * 3600; localtime_r(×tamp, &setTime); } } switch(pInfo->repeatMode) { case REPEAT_MODE_EVERY_DAY: pInfo->onDateTime.tm_year = setTime.tm_year; pInfo->onDateTime.tm_mon = setTime.tm_mon; pInfo->onDateTime.tm_mday = setTime.tm_mday; pInfo->onTimestamp = mktime(&pInfo->onDateTime); break; case REPEAT_MODE_WORKDAY: do { ret = CurrentIsWorkDay(setTime.tm_year, setTime.tm_yday); if(ret == 0) { timestamp = mktime(&setTime) + 24 * 3600; localtime_r(×tamp, &setTime); } } while(ret == 0); if(ret < 0) { pInfo->onTimestamp = 0; pInfo->onDateTime.tm_year = -1; pInfo->onDateTime.tm_mon = -1; pInfo->onDateTime.tm_mday = -1; } else { pInfo->onDateTime.tm_year = setTime.tm_year; pInfo->onDateTime.tm_mon = setTime.tm_mon; pInfo->onDateTime.tm_mday = setTime.tm_mday; pInfo->onTimestamp = mktime(&pInfo->onDateTime); } break; case REPEAT_MODE_HOLIDAY: do { ret = CurrentIsWorkDay(setTime.tm_year, setTime.tm_yday); if(ret == 0) { timestamp = mktime(&setTime) + 24 * 3600; localtime_r(×tamp, &setTime); } } while(ret == 1); if(ret < 0) { pInfo->onTimestamp = 0; pInfo->onDateTime.tm_year = -1; pInfo->onDateTime.tm_mon = -1; pInfo->onDateTime.tm_mday = -1; } else { pInfo->onDateTime.tm_year = setTime.tm_year; pInfo->onDateTime.tm_mon = setTime.tm_mon; pInfo->onDateTime.tm_mday = setTime.tm_mday; pInfo->onTimestamp = mktime(&pInfo->onDateTime); } break; case REPEAT_MODE_WEEKEND: while(setTime.tm_wday != 0 && setTime.tm_wday != 6) { timestamp = mktime(&setTime) + 24 * 3600; localtime_r(×tamp, &setTime); } pInfo->onDateTime.tm_year = setTime.tm_year; pInfo->onDateTime.tm_mon = setTime.tm_mon; pInfo->onDateTime.tm_mday = setTime.tm_mday; pInfo->onTimestamp = mktime(&pInfo->onDateTime); break; case REPEAT_MODE_WEEKDAY: if(pInfo->setDateTime.tm_wday == 0) { pInfo->setDateTime.tm_wday = 1 << 0; } else if(pInfo->setDateTime.tm_wday & (1 << 7)) { pInfo->setDateTime.tm_wday = 1 << 0; } while(((1 << setTime.tm_wday) & pInfo->setDateTime.tm_wday) == 0) { timestamp = mktime(&setTime) + 24 * 3600; localtime_r(×tamp, &setTime); } pInfo->onDateTime.tm_year = setTime.tm_year; pInfo->onDateTime.tm_mon = setTime.tm_mon; pInfo->onDateTime.tm_mday = setTime.tm_mday; pInfo->onTimestamp = mktime(&pInfo->onDateTime); break; case REPEAT_MODE_EVERY_TIME: timestamp = mktime(&g_LocalTime); if(pInfo->setDateTime.tm_hour > 0) { timestamp += pInfo->setDateTime.tm_hour * 3600; } if(pInfo->setDateTime.tm_min > 0) { timestamp += pInfo->setDateTime.tm_min * 60; } if(pInfo->setDateTime.tm_sec > 0) { timestamp += pInfo->setDateTime.tm_sec; } localtime_r(×tamp, &pInfo->onDateTime); pInfo->onTimestamp = timestamp; break; } return (0); } static void __timerout200msCb(uv_timer_t *pTimer) { PALARM_ITEM_DATA pItem = NULL, pTemp = NULL; // upgrade current time and timestamp g_TimeStamp = time((time_t*)NULL); localtime_r(&g_TimeStamp, &g_LocalTime); uv_rwlock_wrlock(&g_uvHashRwLock); HASH_ITER(hh, g_TimerTbl, pItem, pTemp) { // cleanup out of time more than 10s timer if(g_TimeStamp - pItem->onTimestamp > 10) { LOG_EX(LOG_Warn, "Remove out of time timer: %u, %ld, %ld\n", pItem->alarmId, g_TimeStamp, pItem->onTimestamp); HASH_DEL(g_TimerTbl, pItem); free(pItem); continue; } // timer not on time if(pItem->onTimestamp != g_TimeStamp) { break; } // timer on time, call callback if(pItem->pOnAlarmCb) { pItem->pOnAlarmCb(pItem->alarmId, g_TimeStamp, pItem->pUserData); } //LOG_EX(LOG_Debug, "Timer %d Alarming..................\n", pItem->alarmId); // cleanup not repeat timer if(pItem->repeatMode == REPEAT_MODE_NONE) { HASH_DEL(g_TimerTbl, pItem); free(pItem); } else { // calc next on time int ret = __getNextOnTimestamp(pItem); if(ret != 0 || pItem->onTimestamp == 0) { // some error, remove it LOG_EX(LOG_Error, "Timer %d repeat error: ret = %d, timestamp = %u\n", pItem->alarmId, ret, pItem->onTimestamp); HASH_DEL(g_TimerTbl, pItem); free(pItem); } else { // resort table by upgrade timestamp HASH_SORT(g_TimerTbl, __timestampSort); // show log LOG_EX(LOG_Debug, "Readd Timer: %u at [%04u-%02u-%02u %02u:%02u:%02u], repMode = %s, Timestamp = %u\n", pItem->alarmId, pItem->onDateTime.tm_year + 1900, pItem->onDateTime.tm_mon + 1, pItem->onDateTime.tm_mday, pItem->onDateTime.tm_hour, pItem->onDateTime.tm_min, pItem->onDateTime.tm_sec, DumpTimerRepeatModeString(pItem->repeatMode), pItem->onTimestamp); } } } uv_rwlock_wrunlock(&g_uvHashRwLock); } int AlarmTimerInit(uv_loop_t* pLoop) { g_pMainLoop = pLoop; uv_rwlock_init(&g_uvHashRwLock); uv_timer_init(g_pMainLoop, &g_uvTimer); g_TimeStamp = time((time_t*)NULL); localtime_r(&g_TimeStamp, &g_LocalTime); g_iAlarmId = 1; uv_timer_start(&g_uvTimer, __timerout200msCb, 0, TIMER_TIMEOUT); } int AlarmTimerCleanup(void) { uv_timer_stop(&g_uvTimer); uv_rwlock_destroy(&g_uvHashRwLock); if(g_pMainLoop != NULL) { AlarmTimerInit(g_pMainLoop); } } int AlarmTimerRemove(unsigned int tmId) { PALARM_ITEM_DATA pItem = NULL; uv_rwlock_rdlock(&g_uvHashRwLock); HASH_FIND_INT(g_TimerTbl, &tmId, pItem); uv_rwlock_rdunlock(&g_uvHashRwLock); if(pItem == NULL) { LOG_EX(LOG_Error, "Can't find item: %u\n", tmId); return (-ERR_NO_ITEMS); } uv_rwlock_wrlock(&g_uvHashRwLock); HASH_DEL(g_TimerTbl, pItem); uv_rwlock_wrunlock(&g_uvHashRwLock); free(pItem); return (tmId); } unsigned int AlarmTimerAdd(int year, int month, int day, int hour, int minute, int second, int weekDay, int repMode, OnAlarmTimer pOnTimerCb, int priority, void *pUserData, int *pError) { int et; PALARM_ITEM_DATA pAlarmData = NULL; if(pOnTimerCb == NULL) { LOG_EX(LOG_Error, "Input Params Error: pOnTimerCb = %p\n", pOnTimerCb); if(pError) { *pError = -ERR_INPUT_PARAMS; } return (0xFFFFFFFF); } g_TimeStamp = time((time_t*)NULL); localtime_r(&g_TimeStamp, &g_LocalTime); pAlarmData = (PALARM_ITEM_DATA)malloc(sizeof(ALARM_ITEM_DATA)); if(pAlarmData == NULL) { LOG_EX(LOG_Error, "Malloc Memory Error\n"); if(pError) { *pError = -ERR_MALLOC_MEMORY; } return (0xFFFFFFFF); } memset(pAlarmData, 0, sizeof(ALARM_ITEM_DATA)); // save input params pAlarmData->setDateTime.tm_year = year; pAlarmData->setDateTime.tm_mon = month; pAlarmData->setDateTime.tm_mday = day; pAlarmData->setDateTime.tm_hour = hour; pAlarmData->setDateTime.tm_min = minute; pAlarmData->setDateTime.tm_sec = second; pAlarmData->setDateTime.tm_wday = weekDay; pAlarmData->repeatMode = repMode; pAlarmData->pOnAlarmCb = pOnTimerCb; pAlarmData->pUserData = pUserData; pAlarmData->timerPriority = priority; // get timer on time __getOnTimestamp(pAlarmData); // check on time et = pAlarmData->onTimestamp - mktime(&g_LocalTime); if(et < -1 || pAlarmData->onTimestamp == 0) { LOG_EX(LOG_Debug, "Add Timer Error: [%04u-%02u-%02u %02u:%02u:%02u], repMode = %s(%u), %d, %u/%u\n", pAlarmData->setDateTime.tm_year + 1900, pAlarmData->setDateTime.tm_mon + 1, pAlarmData->setDateTime.tm_mday, pAlarmData->setDateTime.tm_hour, pAlarmData->setDateTime.tm_min, pAlarmData->setDateTime.tm_sec, DumpTimerRepeatModeString(repMode), repMode, et, pAlarmData->onTimestamp, mktime(&g_LocalTime)); if(pError) { *pError = -ERR_INPUT_PARAMS; } return (0xFFFFFFFF); } if(pError) { *pError = 0; } // upgrade time global id pAlarmData->alarmId = __sync_fetch_and_add(&g_iAlarmId, 1); // save new timer to hash table, and sort it by timestamp uv_rwlock_wrlock(&g_uvHashRwLock); HASH_ADD_INT(g_TimerTbl, alarmId, pAlarmData); HASH_SORT(g_TimerTbl, __timestampSort); uv_rwlock_wrunlock(&g_uvHashRwLock); LOG_EX(LOG_Debug, "Add: %u [%04u-%02u-%02u %02u:%02u:%02u] at [%04u-%02u-%02u %02u:%02u:%02u], repMode = %s, priority = %d, Timestamp = %u\n", pAlarmData->alarmId, (pAlarmData->setDateTime.tm_year == -1) ? 1900 : pAlarmData->setDateTime.tm_year + 1900, (pAlarmData->setDateTime.tm_mon == -1) ? 0 : pAlarmData->setDateTime.tm_mon + 1, (pAlarmData->setDateTime.tm_mday == -1) ? 0 : pAlarmData->setDateTime.tm_mday, (pAlarmData->setDateTime.tm_hour == -1) ? 0 : pAlarmData->setDateTime.tm_hour, (pAlarmData->setDateTime.tm_min == -1) ? 0 : pAlarmData->setDateTime.tm_min, (pAlarmData->setDateTime.tm_sec == -1) ? 0 : pAlarmData->setDateTime.tm_sec, pAlarmData->onDateTime.tm_year + 1900, pAlarmData->onDateTime.tm_mon + 1, pAlarmData->onDateTime.tm_mday, pAlarmData->onDateTime.tm_hour, pAlarmData->onDateTime.tm_min, pAlarmData->onDateTime.tm_sec, DumpTimerRepeatModeString(repMode), pAlarmData->timerPriority, pAlarmData->onTimestamp); return (pAlarmData->alarmId); }