1 | var express = require('express'); |
||
2 | var extend = require('extend'); |
||
3 | |||
4 | // ------------------------------------------ |
||
5 | // CONSTRUCTOR |
||
6 | // ------------------------------------------ |
||
7 | |||
8 | var init = function(){ |
||
9 | info('Starting RuleEngine ...'); |
||
10 | return RuleEngine; |
||
11 | } |
||
12 | |||
13 | // ------------------------------------------ |
||
14 | // SCRIPT |
||
15 | // ------------------------------------------ |
||
16 | |||
17 | var dispatch = function(name, data, callback){ |
||
18 | // 1. Answer now to callback |
||
19 | if (callback) { callback(data); } |
||
20 | |||
21 | // 2. Then iterate on explicit rules |
||
22 | iterate(name, data, 0); |
||
23 | } |
||
24 | |||
25 | var iterate = function(name, data, i){ |
||
26 | var options = data; // backward compatibility |
||
27 | var rules = Config.rules; |
||
28 | if (!rules){ return false; } |
||
29 | |||
30 | for (; i < rules.length ; i++){ |
||
31 | try { |
||
32 | var rule = rules[i]; |
||
33 | if (rule['disabled']){ continue; } |
||
34 | if (rule['if'] != name){ continue; } |
||
35 | if (rule['script']){ eval(decode(rule['script'])); } |
||
0 ignored issues
–
show
Security
Performance
introduced
by
![]() |
|||
36 | if (!rule['then']){ continue; } |
||
37 | |||
38 | info('iterate ', i, rule); |
||
39 | return SARAH.run(rule['then'], data, function(result){ |
||
40 | if (result) { |
||
41 | // Handle speech |
||
42 | SARAH.speak(result.tts); |
||
43 | |||
44 | // Merge resutls |
||
45 | extend(true, data, result); |
||
46 | } |
||
47 | |||
48 | // Recursive call |
||
49 | iterate(name, data, i+1); |
||
50 | }); |
||
51 | |||
52 | } catch(ex) { |
||
53 | warn('Rule %s:', name, rule, ex); |
||
54 | iterate(name, data, i+1); |
||
55 | } |
||
56 | } |
||
57 | } |
||
58 | |||
59 | var decode = function(str){ |
||
60 | if (!str){ return str; } |
||
61 | return str.replace(/"e;/g, '"').replace(/ /g, '\n').trim(); |
||
62 | } |
||
63 | |||
64 | // ------------------------------------------ |
||
65 | // SORTING MANAGER |
||
66 | // ------------------------------------------ |
||
67 | |||
68 | var SEEK = 1*60*3; // 3 minutes |
||
69 | var SCOPE = 1*60*10; // 10 minutes |
||
70 | var moment = require('moment'); moment.locale('fr'); |
||
71 | |||
72 | var log = function(name, data){ |
||
73 | var now = moment(); |
||
74 | var day = moment().startOf('day'); |
||
75 | |||
76 | var entry = {}; |
||
77 | entry.name = name; |
||
78 | entry.data = data; |
||
79 | entry.timeOfDay = now.diff(day) / 1000; |
||
80 | entry.dayOfWeek = now.day(); |
||
81 | entry.dayOfMonth = now.date(); |
||
82 | entry.location = 'house'; |
||
83 | entry.count = 0; |
||
84 | |||
85 | var i = indexOf(entry); |
||
86 | if (i < 0){ |
||
87 | logs.push(entry); |
||
88 | } else { |
||
89 | entry = logs[i]; |
||
90 | entry.count++; |
||
91 | } |
||
92 | logs.sort(compare); |
||
93 | |||
94 | return entry; |
||
95 | } |
||
96 | |||
97 | var indexOf = function(entry){ |
||
98 | for (var i = 0 ; i < logs.length; i++){ |
||
99 | if (compare(entry, logs[i]) == 0){ return i } |
||
100 | } |
||
101 | return -1; |
||
102 | } |
||
103 | |||
104 | var compare = function(x, y){ |
||
105 | var tod = x.timeOfDay - y.timeOfDay; |
||
106 | var dow = x.dayOfWeek - y.dayOfWeek; |
||
107 | var dom = x.dayOfMonth - y.dayOfMonth; |
||
108 | var cnt = x.count - y.count; |
||
109 | |||
110 | if (Math.abs(tod) < SCOPE ){ |
||
111 | if (x.name === y.name){ return 0; } |
||
112 | var delta = (Math.abs(dom) + Math.abs(dow) + Math.abs(tod)) / ((Math.abs(cnt)+1) * SCOPE); |
||
113 | if (dom != 0){ return dom>0 ? delta : -delta; } |
||
114 | if (dow != 0){ return dow>0 ? delta : -delta; } |
||
115 | if (cnt != 0){ return cnt>0 ? delta : -delta; } |
||
116 | return tod>0 ? delta : -delta; //x.name.localeCompare(y.name); |
||
117 | } |
||
118 | else { return tod; } |
||
119 | } |
||
120 | |||
121 | var next = function(entry){ |
||
122 | |||
123 | /* |
||
124 | var i = indexOf(entry); |
||
125 | var d1 = 1000000; |
||
126 | var e1 = undefined; |
||
127 | |||
128 | var d2 = 1000000; |
||
129 | var e2 = undefined; |
||
130 | |||
131 | console.log(i,entry.name,'--------------------'); |
||
132 | for (var j in logs){ |
||
133 | console.log(j, logs[j].name); |
||
134 | } |
||
135 | |||
136 | if (i+1 < logs.length){ |
||
137 | e1 = logs[i+1]; |
||
138 | d1 = compare(e1, entry); |
||
139 | console.log('i+1 <'+logs.length, e1.name, d1); |
||
140 | } |
||
141 | |||
142 | if (i-1 >= 0){ |
||
143 | e2 = logs[i-1]; |
||
144 | d2 = compare(entry, e2); |
||
145 | console.log('i-1 >= 0', e2.name, d2); |
||
146 | } |
||
147 | |||
148 | return d1 < d2 ? e1 : e2; |
||
149 | */ |
||
150 | } |
||
151 | |||
152 | var logs = [] |
||
153 | var getLogs = function(){ |
||
154 | return logs; |
||
155 | } |
||
156 | |||
157 | |||
158 | // ------------------------------------------ |
||
159 | // RULES |
||
160 | // ------------------------------------------ |
||
161 | |||
162 | var getRules = function(ifname){ |
||
163 | |||
164 | // Set empty rules |
||
165 | if (!Config.rules){ |
||
166 | Config.rules = [ |
||
167 | { "if": "before", "then": "", "script" : ""}, |
||
168 | { "if": "after", "then": "", "script" : ""} |
||
169 | ]; |
||
170 | } |
||
171 | |||
172 | // Return all rules if there is no name |
||
173 | if (!ifname){ |
||
174 | return Config.rules; |
||
175 | } |
||
176 | |||
177 | // Filter on name |
||
178 | var tmp = []; |
||
179 | for (var i in Config.rules){ |
||
180 | var rule = Config.rules[i]; |
||
181 | if (ifname == rule['if']){ |
||
182 | tmp.push(rule); |
||
183 | } |
||
184 | } |
||
185 | return tmp; |
||
186 | } |
||
187 | |||
188 | var save = function(body){ |
||
189 | if (!body['if']){ return; } |
||
190 | |||
191 | var tmp = [] |
||
192 | for(var i in body['if']){ |
||
193 | var ifname = Helper.parse(body['if'][i]); |
||
194 | if (!ifname) continue; |
||
195 | |||
196 | tmp.push({ |
||
197 | "if" : ifname, |
||
198 | "then" : Helper.parse(body['then'][i]), |
||
199 | "script" : Helper.parse(body['script'][i]), |
||
200 | "disabled" : Helper.parse(body['disabled'][i]), |
||
201 | }); |
||
202 | } |
||
203 | Config.rules = tmp; |
||
204 | SARAH.ConfigManager.save(); |
||
205 | } |
||
206 | |||
207 | // ------------------------------------------ |
||
208 | // ROUTER |
||
209 | // ------------------------------------------ |
||
210 | |||
211 | var Router = express.Router(); |
||
212 | Router.post('/portal/rules', function(req, res, next) { |
||
213 | var name = req.body.opSave; |
||
214 | if (name){ save(req.body); } |
||
215 | res.redirect('/portal/rules'); |
||
216 | }); |
||
217 | |||
218 | Router.all('/rules/script', function(req, res, next) { |
||
219 | res.render('rules/rules-script.ejs', {'title' : i18n('modal.rules.script')}); |
||
220 | }); |
||
221 | |||
222 | // ------------------------------------------ |
||
223 | // PUBLIC |
||
224 | // ------------------------------------------ |
||
225 | |||
226 | var RuleEngine = { |
||
227 | 'init' : init, |
||
228 | 'dispatch': dispatch, |
||
229 | 'log': log, |
||
230 | 'next': next, |
||
231 | 'getLogs': getLogs, |
||
232 | 'getRules': getRules, |
||
233 | 'before': function(){ return getRules('before')[0] }, |
||
234 | 'after' : function(){ return getRules('after')[0] }, |
||
235 | 'Router' : Router |
||
236 | } |
||
237 | |||
238 | // Exports Manager |
||
239 | exports.init = RuleEngine.init; |