refine voting algo, equal votes dont elect anything
[profiled] / profiled.lua
1 -- Profiled  -  automatic profile switcher for Maemo
2 --
3 --  Copyright (C)         Lumiera.org
4 --    2010,               Christian Thaeter <ct@pipapo.org>
5 --
6 --  This program is free software; you can redistribute it and/or
7 --  modify it under the terms of the GNU General Public License as
8 --  published by the Free Software Foundation; either version 2 of the
9 --  License, or (at your option) any later version.
10 --
11 --  This program is distributed in the hope that it will be useful,
12 --  but WITHOUT ANY WARRANTY; without even the implied warranty of
13 --  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 --  GNU General Public License for more details.
15 --
16 --  You should have received a copy of the GNU General Public License
17 --  along with this program; if not, write to the Free Software
18 --  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19
20
21 -- queries contains functions for each aspect to supervise
22 -- this functions return the table containing the current applicable definition
23 queries = {}
24
25 -- just for playing
26 function queries.test()
27    return { test = 'working' }
28 end
29
30 -- the defaults table biases voting
31 function queries.defaults()
32    return defaults
33 end
34
35 -- TODO
36 -- orientation = function() end
37 -- moving = function () end
38 -- charging = function () end
39 -- playing = function () end
40 -- display = function () end
41 -- locked = function () end
42
43
44 -- engines contains functions to alter the state of some aspect of the system
45 -- this functions take a single string as parameter describing the target state
46 engines = {}
47
48 function engines.test(state)
49    print("test state:", state)
50 end
51
52 --TODO
53 --engines.gps(state)
54 --engines.availability(state)
55 --engines.profile(state)
56 --engines.celluar(state)
57 --engines.secure(state)
58 --engines.lock(state)
59 --engines.wlan(state)
60
61 -- the voting function, takes the votes table with all current votes, the key for which shall be voted and the value of its attribute
62 function vote(votes, key, value)
63    attr, bias = value:match("^([^+-]*)([+-]*)")
64
65    if bias == "" then
66       bias = "+"
67    end
68
69    local counter = 0
70
71    for v in bias:gmatch "." do
72       if v == "+" then
73          counter = counter + 1
74       elseif v == "-" then
75          counter = counter - 1
76       else
77          break
78       end
79    end
80
81    if votes[key] == nil then
82       votes[key] = {}
83    end
84    if votes[key][attr] == nil then
85       votes[key][attr] = 0
86    end
87    votes[key][attr] = votes[key][attr] + counter
88
89    print("voting:", counter, "for:", key..":"..attr, "giving:", votes[key][attr])
90 end
91
92 function mainloop()
93    --sleep(sample_interval)
94    --config()
95
96    print("mainloop")
97
98    local votes = {}
99
100    -- do the voting
101    for name, func in pairs(queries) do
102       if type(_G[name]) == 'table' then
103          print("in table:", name)
104          for k,v in pairs(func() or {}) do
105             vote(votes, k, v)
106          end
107       end
108    end
109
110    -- calculate results
111    for engine, values in pairs(votes) do
112       local winner
113       local winning_votes = 0
114       for attr, votes in pairs(values) do
115          if votes == winning_votes then
116             winner = nil
117          elseif votes > winning_votes then
118             winner = attr
119             winning_votes = votes
120          end
121       end
122
123       if winner then
124          print ("elected:", engine..":"..winner, "votes:", winning_votes)
125          if engines[engine] then
126             engines[engine](winner)
127          end
128       end
129    end
130 end
131
132
133 -- example config:
134
135 sample_interval = 30000
136
137 --voting system, not fair but usable
138 -- Examples:
139 --  key = 'value'     votes +1 for 'value' on 'key'
140 --  key = 'value-'    votes -1 for 'value' on 'key'
141 --  key = 'value+++'  votes +4 for 'value' on 'key'
142 --  key = 'value---'  votes -3 for 'value' on 'key'
143 --  key = 'value++--' votes +1 for 'value' on 'key'
144 --
145 -- only things which got not manually altered get voted for,
146 -- after voting the value with the most votes and being positive is choosen
147 -- if it passes this becomes the new setting for the associated thing.
148 --
149
150 -- the default table gives some bias for all voting
151 defaults = {
152    gps = 'on--',    -- need at least 2 votes to turn the gps on
153 }
154
155
156 -- timespans hh:mm in hhmm notation (700 is 7:00h)
157 time = {
158    [100] = {
159       availability = 'offline',
160       profile = 'silent',
161       celluar = 'off',
162    },
163    [700] = {
164    },
165 }
166
167 -- loadavg times 100
168 load = {
169    [0] = {},
170    [20] = {
171       secure = 'no',
172       lock = 'no',
173       wlan = 'on'
174    }
175 }
176
177
178 -- orientation of the device
179 orientation = {
180    face_down = {
181       availability = 'offline',
182       profile = 'silent',
183       celluar = 'off',
184       secure = 'yes',
185       gps = 'off'
186    },
187    face_up = {
188       gps = 'on'
189    }
190 }
191
192 -- moving around
193 moving = {
194    yes = {
195       availability = 'online',
196       profile = 'normal',
197       celluar = 'on',
198       secure = 'no',
199       gps = 'on'
200    },
201    no = {
202       availability = 'offline',
203       secure = 'yes'
204    }
205 }
206
207 -- connected to charger
208 charging = {
209    yes = {
210       celluar = 'on',
211       wlan = 'on',
212       gps = 'off'
213    }
214 }
215
216 -- if sound/multimedia is playing
217 playing = {
218    yes = {
219       secure = 'no',
220    }
221 }
222
223 -- display state
224 display = {
225    on = {
226       secure = 'no'
227    }
228 }
229
230 -- manually locked
231 locked = {
232    yes = {
233       secure = 'yes'
234    },
235    no = {
236       secure = 'no'
237    }
238 }
239
240 -- by location, gps must be enabled for this, gps will not be enabled for this rules, 'unknown' is special for any unknown position
241 location = {
242    home = {
243       secure = 'no'
244    },
245
246    unknown = {
247       secure = 'yes',
248       celluar = 'on',
249       wlan = 'off',
250       gps = 'on'
251    }
252 }
253
254 test = {}
255
256
257 mainloop()