UNPKG

scalra

Version:

node.js framework to prototype and scale rapidly

645 lines (575 loc) 15.9 kB
var status = { enabled: false }; var l_callbackPool = {}; //因為 function 不行存入資料庫,因此要另外處理 var l_schedulePool = {}; var dbName_schedule = 'schedule'; SR.DB.useCollections([dbName_schedule]); const l_cat = 'SR.Schedule'; // TODO: to run a callback function (task) every cycle seconds exports.setTask = function (arg) { if (!arg.id) { arg.id = UTIL.createUUID(); } LOG.warn('setTask', l_cat); //todo: check input if (arg.callback) { //~ console.log("arg.callback exists"); if (typeof arg.callback === 'function') { //~ console.log("arg.callback is a function"); l_callbackPool[arg.id] = arg.callback; LOG.warn('new l_callbackPool: ', l_cat); LOG.warn(l_callbackPool, l_cat); } else { LOG.warn('callback is not a function, but a ', l_cat); LOG.warn(typeof arg.callback, l_cat); } } else { LOG.warn('arg.callback Not exist', l_cat); } if (typeof arg.monthday === 'string') arg.monthday = parseInt(arg.monthday); if (typeof arg.hour === 'string') arg.hour = parseInt(arg.hour); if (typeof arg.minute === 'string') arg.minute = parseInt(arg.minute); arg.cycle = arg.cycle.toLowerCase(); arg.weekday = arg.weekday.toLowerCase(); l_schedulePool[arg.id] = { name: arg.scheduleName, id: arg.id, cycle: arg.cycle, monthday: arg.monthday, weekday: arg.weekday, hour: arg.hour, minute: arg.minute, do: arg.do, action: arg.action, suspend: arg.suspend || false, description: arg.description, latestExecuted: new Date(), created: new Date(), }; SR.DB.updateData(dbName_schedule, { id: arg.id }, l_schedulePool[arg.id], function (msg) { arg.onDone({ scheduleId: arg.id, message: 'success' }); //LOG.warn('SR.Schedule', "success: "); //LOG.warn('SR.Schedule', msg); }, function (msg) { arg.onDone({ scheduleId: arg.id, message: 'db failure' }); LOG.warn('failure: ', l_cat); LOG.warn(msg, l_cat); }); }; // TODO: delete a task by id exports.deleteTask = function (arg) { if (!arg.id) { arg.onDone({ message: 'no given id' }); //LOG.warn('SR.Schedule', "no given id"); return; } if (!l_schedulePool[arg.id]) { arg.onDone({ message: 'assigned id is invalid' }); //LOG.warn('SR.Schedule', "assigned id does not exist"); return; } SR.DB.deleteData(dbName_schedule, function (result) { delete l_schedulePool[arg.id]; delete l_callbackPool[arg.id]; arg.onDone({ id: arg.id, message: 'The given schedule task is begin deleted.' }); //LOG.warn('SR.Schedule', "The given task is deleted."); }, function (result) {}, { id: { $in: arg.id } }); }; // TODO: to temporarily pause a task without delete it exports.suspendTask = function (arg) { if (!arg.id) { arg.onDone({ message: 'no given id' }); LOG.warn('no given id', l_cat); return; } if (!l_schedulePool[arg.id]) { arg.onDone({ message: 'assigned id is invalid' }); LOG.warn('assigned id does not exist', l_cat); return; } l_schedulePool[arg.id].suspend = true; SR.DB.updateData(dbName_schedule, { id: arg.id }, l_schedulePool[arg.id], function (msg) { //LOG.warn('SR.Schedule', "success: "); //LOG.warn('SR.Schedule', msg); arg.onDone({ id: arg.id, message: 'The given task is suspended.' }); }, function (msg) { LOG.warn('failure: db failure ', l_cat); LOG.warn(msg, l_cat); }); }; exports.resumeTask = function (arg) { if (!arg.id) { arg.onDone({ message: 'no given id' }); LOG.warn('no given id', l_cat); return; } if (!l_schedulePool[arg.id]) { arg.onDone({ message: 'assigned id is invalid' }); LOG.warn('assigned id does not exist', l_cat); return; } l_schedulePool[arg.id].suspend = false; SR.DB.updateData(dbName_schedule, { id: arg.id }, l_schedulePool[arg.id], function (msg) { arg.onDone({ id: arg.id, message: 'The given task is resumed.' }); //LOG.warn('SR.Schedule', "success: "); //LOG.warn('SR.Schedule', msg); }, function (msg) { LOG.warn('failure: ', l_cat); LOG.warn(msg, l_cat); }); }; // TODO: 由於 function 不會被取到資料庫,若此筆資料從 db 讀出後,要再補上 authentic function exports.patchCallback = function (arg) { LOG.warn('to patch callback: ', l_cat); LOG.warn(arg, l_cat); if (!arg.callback) { LOG.warn('no assigned callback function', l_cat); return; } if (!typeof arg.callback === 'function') { LOG.warn('assigned is not a callback function', l_cat); return; } if (arg.id && l_schedulePool[arg.id]) { l_callbackPool[arg.id] = arg.callback; LOG.warn('The given callback function was patched:' + arg.id, l_cat); } else if (arg.action) { l_callbackPool[arg.action] = arg.callback; for (var i in l_schedulePool) { if (l_schedulePool[i].action === arg.action) { l_callbackPool[i] = arg.callback; LOG.warn('The given callback function was patched:' + l_schedulePool[i].id, l_cat); } } } }; // TODO: to get a task status exports.getStatus = function (arg) { LOG.warn(l_schedulePool, l_cat); LOG.warn(l_callbackPool, l_cat); return l_schedulePool; //todo: return callbackPool to REST }; exports.enable = function (arg) { status.enabled = true; setTimeout(function () { readDB({}); }, 2000); }; exports.disable = function (arg) { status.enabled = false; }; var readDB = exports.readDB = function (arg) { SR.DB.getArray(dbName_schedule, function (msg) { l_schedulePool = {}; for (var index in msg) { l_schedulePool[msg[index].id] = msg[index]; } // 之所以這裡要逐筆做,是為了能用 l_schedulePool[id] 存取 LOG.warn('SR.Schedule is enabled.', l_cat); }, function (msg) { LOG.warn('failure: db failure (schedule.js )', l_cat); LOG.warn(msg, l_cat); }); }; exports.daemon = function (arg) { switch (arg.action) { case 'startSetInterval': setInterval(l_schedule, 5000); break; default: break; } }; var toNumberWeekday = function (arg) { switch (arg.toLowerCase()) { case 'sunday': return 0; break; case 'monday': return 1; break; case 'tuesday': return 2; break; case 'wednesday': return 3; break; case 'thursday': return 4; break; case 'friday': return 5; break; case 'saturday': return 6; break; default: return false; break; } }; var isNumberRange = function (arg) { LOG.warn(arg, l_cat); if (!arg) { LOG.warn('no arg', l_cat); return; } if (!arg.start) { LOG.warn('no arg.start', l_cat); return; } if (!arg.end) { LOG.warn('no arg.end', l_cat); return; } if (!arg.current) { LOG.warn('no arg.current', l_cat); return; } var c = arg.current; var s = arg.start; var e = arg.end; if (c === s || c === e) return true; else if (s <= c && c <= e) return true; else if (e <= s && s <= c) return true; else return false; return false; }; exports.checkRange = function (arg) { if (!arg) { LOG.warn('no arg', l_cat); return; } if (!arg.start) { LOG.warn('no arg.start', l_cat); return; } if (!arg.end) { LOG.warn('no arg.end', l_cat); return; } LOG.warn('in checkRange', l_cat); if (arg.start.weekday) arg.start.weekday_num = toNumberWeekday(arg.start.weekday); if (arg.end.weekday) arg.end.weekday_num = toNumberWeekday(arg.end.weekday); LOG.warn(arg, l_cat); if (arg.start.cycle && typeof(arg.start.cycle) === 'string') { switch (arg.start.cycle.toLowerCase()) { case 'daily': LOG.warn('============= in daily', l_cat); var d = new Date(); if (isNumberRange({ start: arg.start.hour, end: arg.end.hour, current: d.getHours() })) if (isNumberRange({ start: arg.start.minute, end: arg.end.minute, current: d.getMinutes() })) return true; return false; break; case 'weekly': LOG.warn('============= in weekly', l_cat); var d = new Date(); if (isNumberRange({ start: arg.start.weekday_num, end: arg.end.weekday_num, current: d.getDay() })) if (isNumberRange({ start: arg.start.hour, end: arg.end.hour, current: d.getHours() })) if (isNumberRange({ start: arg.start.minute, end: arg.end.minute, current: d.getMinutes() })) return true; return false; break; case 'monthly': LOG.warn('============= in monthly', l_cat); var d = new Date(); if (isNumberRange({ start: arg.start.monthday, end: arg.end.monthday, current: d.getDate() })) if (isNumberRange({ start: arg.start.hour, end: arg.end.hour, current: d.getHours() })) if (isNumberRange({ start: arg.start.minute, end: arg.end.minute, current: d.getMinutes() })) return true; return false; break; default: return false; break; } } }; exports.triggerTask = function (arg) { if (typeof(arg) != 'string') return; var i = arg; if (!l_schedulePool[i]) { LOG.warn('The assigned id does not exist.' + i, l_cat); return; } LOG.warn('schedule: task is triggered: ' + l_schedulePool[i].id + ' ' + l_schedulePool[i].action, l_cat); var key, param; if (l_schedulePool[i].action && typeof l_callbackPool[l_schedulePool[i].action] === 'function') { key = l_schedulePool[i].action.toString(); } else if (l_schedulePool[i].id && typeof l_callbackPool[l_schedulePool[i].id] === 'function') { key = l_schedulePool[i].id; } else { LOG.warn('Scheduled But No Callback Provided!\nYou must set either action=startRecord|stopRecord or a callback', l_cat); } if (l_schedulePool[i].do) { param = l_schedulePool[i].do; } else if (l_schedulePool[i].argument) { param = l_schedulePool[i].argument; } if (key) { l_callbackPool[key](param); } else { LOG.warn('Triggered but can do nothing.', l_cat); } l_schedulePool[i].latestExecuted = new Date(); SR.DB.updateData(dbName_schedule, { id: l_schedulePool[i].id }, l_schedulePool[i], function (msg) { LOG.warn('success: ', l_cat); LOG.warn(msg, l_cat); }, function (msg) { LOG.warn('failure: ', l_cat); LOG.warn(msg, l_cat); }); }; //////////////////////////////////////// // 每單位時間(目前為 5 秒) 檢查是否有符合條件的 task // //////////////////////////////////////// var l_schedule = function () { if (status.enabled !== true) { return; } //~ console.log("l_callbackPool: ", l_callbackPool); //~ console.log("l_schedulePool: ", l_schedulePool); //console.log(l_schedulePool); var resolutionTS = 1000 * 60; // one minute support; 最小解析度(目前每分鐘) var cycleTShourly = 1000 * 60 * 60; // 每小時有 60 分鐘 (60*60秒) var cycleTSdaily = cycleTShourly * 24; // 每天有 24 小時 var cycleTSweekly = cycleTSdaily * 7; // 每周有 7 天 var cycleTSmonthly = cycleTSdaily * 30; // 因為推導時間的演算法還未想完整,有些變數還沒有用到,暫時先留著 var now = getDateTime(); var currentTS = new Date() .valueOf(); // current date and time //console.log("current: " + current + " " + typeof current); //~ LOG.debug("now: " + now.hour + ":" + now.minute + " || " + typeof now); //~ LOG.debug("currentTS: " + currentTS + " || " + typeof currentTS); for (var i in l_schedulePool) { //~ console.log("IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII:::::::::::::::: ", i); var latestExecutedTS = new Date(l_schedulePool[i].latestExecuted) .valueOf(); // latest executed date and time var createdTS = new Date(l_schedulePool[i].created) .valueOf(); // created date and time //~ console.log("latestExecutedTS - createdTS: " + Math.abs(latestExecutedTS - createdTS)); var deltaTS = (currentTS - latestExecutedTS); //現在時間減最後執行過的時間 //~ console.log("delta: " + deltaTS ); if (l_schedulePool[i].suspend === true) { //LOG.warn('SR.Schedule', "returned"); return; } //LOG.warn('SR.Schedule', "l_schedulePool[i]: %j", l_schedulePool[i]); //LOG.warn('SR.Schedule', "now: %j", now); var trigger = false; //~ console.log("l_schedulePool[i].cycle: ", l_schedulePool[i].cycle); switch (l_schedulePool[i].cycle) { case 'hourly': if (l_schedulePool[i].minute === now.minute) { if (deltaTS > resolutionTS) { trigger = true; } } break; case 'daily': if (l_schedulePool[i].hour === now.hour && l_schedulePool[i].minute === now.minute) { if (deltaTS > resolutionTS) { trigger = true; } } break; case 'weekly': //~ console.log(" " + typeof l_schedulePool[i].minute + " " + typeof now.minute ); //~ console.log("Weekdays: " + l_schedulePool[i].weekday + " " + now.weekDay ); //~ console.log("hours: " + l_schedulePool[i].hour + " " + now.hour ); //~ console.log("minutes: " + l_schedulePool[i].minute + " " + now.minute ); //~ console.log("DELTA and RESOLUTION: ",deltaTS + " " + resolutionTS); if (l_schedulePool[i].weekday === now.weekDay && l_schedulePool[i].hour === now.hour && l_schedulePool[i].minute === now.minute) { if (deltaTS > resolutionTS) { trigger = true; } } break; case 'monthly': if (l_schedulePool[i].monthday === now.monthday && l_schedulePool[i].hour === now.hour && l_schedulePool[i].minute === now.minute) { if (deltaTS > resolutionTS) { trigger = true; } } break; default: LOG.warn('The period is out of scope. please debug: ' + l_schedulePool[i], l_cat); break; } //LOG.sys("trigger---: " + trigger, l_cat); if (trigger === true) { LOG.warn('schedule: task is triggered: ' + l_schedulePool[i].id + ' ' + l_schedulePool[i].action, l_cat); //~ l_callbackPool[l_schedulePool[i].action](l_schedulePool[i].do); var key, param; if (l_schedulePool[i].action && typeof l_callbackPool[l_schedulePool[i].action] === 'function') { key = l_schedulePool[i].action.toString(); } else if (l_schedulePool[i].id && typeof l_callbackPool[l_schedulePool[i].id] === 'function') { key = l_schedulePool[i].id; } else { LOG.warn('Scheduled But No Callback Provided!\nYou must set either action=startRecord|stopRecord or a callback', l_cat); } if (l_schedulePool[i].do) { param = l_schedulePool[i].do; } else if (l_schedulePool[i].argument) { param = l_schedulePool[i].argument; } if (key) { l_callbackPool[key](param); } else { LOG.warn('Triggered but can do nothing.', l_cat); } l_schedulePool[i].latestExecuted = new Date(); SR.DB.updateData(dbName_schedule, { id: l_schedulePool[i].id }, l_schedulePool[i], function (msg) { LOG.warn('success: ', l_cat); LOG.warn(msg, l_cat); }, function (msg) { LOG.warn('failure: ', l_cat); LOG.warn(msg, l_cat); }); } else { //~ console.log("trigger is false ---------"); } } }; var getDateTime = function (d) { var eventName = 'getDateTime'; if (d) var date = new Date(d); else var date = new Date(); var hour = date.getHours(); hour = (hour < 10 ? '0' : '') + hour; var min = date.getMinutes(); min = (min < 10 ? '0' : '') + min; var sec = date.getSeconds(); sec = (sec < 10 ? '0' : '') + sec; var year = date.getFullYear(); var month = date.getMonth() + 1; month = (month < 10 ? '0' : '') + month; var day = date.getDate(); day = (day < 10 ? '0' : '') + day; var timeObj = { 'year': parseInt(year), 'month': parseInt(month), 'monthday': parseInt(day), 'weekday': date.getDay(), 'hour': parseInt(hour), 'minute': parseInt(min), 'second': parseInt(sec) }; //LOG.warn('SR.Schedule', "date.getDay: " + date.getDay()); switch (date.getDay()) { case 0: timeObj.weekDay = 'sunday'; break; case 1: timeObj.weekDay = 'monday'; break; case 2: timeObj.weekDay = 'tuesday'; break; case 3: timeObj.weekDay = 'wednesday'; break; case 4: timeObj.weekDay = 'thursday'; break; case 5: timeObj.weekDay = 'friday'; break; case 6: timeObj.weekDay = 'saturday'; break; default: LOG.warn('error code: xxxxxxxx', l_cat); break; } return timeObj; };