1
2
3 '''
4 Asynchronous communication to phone.
5
6 Mostly you should use only L{GammuWorker} class, others are only helpers
7 which are used by this class.
8 '''
9 __author__ = 'Michal Čihař'
10 __email__ = 'michal@cihar.com'
11 __license__ = '''
12 Copyright © 2003 - 2008 Michal Čihař
13
14 This program is free software; you can redistribute it and/or modify it
15 under the terms of the GNU General Public License version 2 as published by
16 the Free Software Foundation.
17
18 This program is distributed in the hope that it will be useful, but WITHOUT
19 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
20 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
21 more details.
22
23 You should have received a copy of the GNU General Public License along with
24 this program; if not, write to the Free Software Foundation, Inc.,
25 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
26 '''
27
28 import gammu.Core
29 import threading
30 import Queue
31
32
34 '''
35 Exception indicating invalid command.
36 '''
38 '''
39 Initializes exception.
40
41 @param value: Name of wrong command.
42 @type value: string
43 '''
44 Exception.__init__(self)
45 self.value = value
46
48 '''
49 Returns textual representation of exception.
50 '''
51 return 'Invalid command: "%s"' % self.value
52
53
55 '''
56 Checks whether command is valid.
57
58 @param command: Name of command.
59 @type command: string
60 '''
61 if hasattr(gammu.Core.StateMachine, command):
62 return
63
64 raise InvalidCommand(command)
65
66
68 '''
69 Storage of single command for gammu.
70 '''
71 - def __init__(self, command, params = None, percentage = 100):
72 '''
73 Creates single command instance.
74 '''
75 check_worker_command(command)
76 self._command = command
77 self._params = params
78 self._percentage = percentage
79
81 '''
82 Returns command name.
83 '''
84 return self._command
85
87 '''
88 Returns command params.
89 '''
90 return self._params
91
93 '''
94 Returns percentage of current task.
95 '''
96 return self._percentage
97
99 '''
100 Returns textual representation.
101 '''
102 if self._params is not None:
103 return '%s %r' % (self._command, self._params)
104 else:
105 return '%s ()' % self._command
106
107
109 '''
110 Storage of taks for gammu.
111 '''
113 '''
114 Creates single command instance.
115
116 @param name: Name of task.
117 @type name: string
118 @param commands: List of commands to execute.
119 @type commands: list of tuples or strings
120 '''
121 self._name = name
122 self._list = []
123 self._pointer = 0
124 for i in range(len(commands)):
125 if type(commands[i]) == tuple:
126 cmd = commands[i][0]
127 try:
128 params = commands[i][1]
129 except IndexError:
130 params = None
131 else:
132 cmd = commands[i]
133 params = None
134 percents = 100 * (i + 1) / len(commands)
135 self._list.append(GammuCommand(cmd, params, percents))
136
138 '''
139 Returns next command to be executed as L{GammuCommand}.
140 '''
141 result = self._list[self._pointer]
142 self._pointer += 1
143 return result
144
146 '''
147 Returns task name.
148 '''
149 return self._name
150
151
153 '''
154 Thread for phone communication.
155 '''
156 - def __init__(self, queue, config, callback):
157 '''
158 Initialises thread data.
159
160 @param queue: Queue with events.
161 @type queue: Queue.Queue object.
162
163 @param config: Gammu configuration, same as
164 L{StateMachine.SetConfig} accepts.
165 @type config: hash
166
167 @param callback: Function which will be called upon operation
168 completing.
169 @type callback: Function, needs to accept four params: name of
170 completed operation, result of it, error code and percentage of
171 overall operation. This callback is called from different
172 thread, so please take care of various threading issues in other
173 modules you use.
174 '''
175 threading.Thread.__init__(self)
176 self._kill = False
177 self._terminate = False
178 self._sm = gammu.Core.StateMachine()
179 self._callback = callback
180 self._queue = queue
181 self._sm.SetConfig(0, config)
182
183 - def _do_command(self, name, cmd, params, percentage = 100):
184 '''
185 Executes single command on phone.
186 '''
187 func = getattr(self._sm, cmd)
188 error = 'ERR_NONE'
189 result = None
190 try:
191 if params is None:
192 result = func()
193 elif type(params) is dict:
194 result = func(**params)
195 else:
196 result = func(*params)
197 except gammu.Core.GSMError, info:
198 errcode = info[0]['Code']
199 error = gammu.Core.ErrorNumbers[errcode]
200
201 self._callback(name, result, error, percentage)
202
204 '''
205 Thread body, which handles phone communication. This should not
206 be used from outside.
207 '''
208 start = True
209 while not self._kill:
210 try:
211 if start:
212 task = GammuTask('Init', ['Init'])
213 start = False
214 else:
215
216 task = self._queue.get(True, 10)
217 try:
218 while True:
219 cmd = task.get_next()
220 self._do_command(
221 task.get_name(),
222 cmd.get_command(),
223 cmd.get_params(),
224 cmd.get_percentage()
225 )
226 except IndexError:
227 try:
228 self._queue.task_done()
229 except AttributeError:
230
231 pass
232 except ValueError:
233
234 pass
235 except Queue.Empty:
236
237 self._sm.ReadDevice()
238 if self._terminate:
239 break
240
242 '''
243 Forces thread end without emptying queue.
244 '''
245 self._kill = True
246
247 - def join(self, timeout=None):
248 '''
249 Terminates thread and waits for it.
250 '''
251 self._terminate = True
252 threading.Thread.join(self, timeout)
253
254
256 '''
257 Wrapper class for asynchronous communication with Gammu. It spaws
258 own thread and then passes all commands to this thread. When task is
259 done, caller is notified via callback.
260 '''
261
263 '''
264 Initializes worker class.
265
266 @param callback: See L{GammuThread.__init__} for description.
267 '''
268 self._thread = None
269 self._callback = callback
270 self._config = {}
271 self._lock = threading.Lock()
272 self._queue = Queue.Queue()
273
275 '''
276 Enqueues command.
277
278 @param command: Command(s) to execute. Each command is tuple
279 containing function name and it's parameters.
280 @type command: tuple of list of tuples
281 @param params: Parameters to command.
282 @type params: tuple or string
283 '''
284 self._queue.put(GammuTask(command, [(command, params)]))
285
287 '''
288 Enqueues task.
289
290 @param command: Command(s) to execute. Each command is tuple
291 containing function name and it's parameters.
292 @type command: tuple of list of tuples
293 @param commands: List of commands to execute.
294 @type commands: list of tuples or strings
295 '''
296 self._queue.put(GammuTask(command, commands))
297
298 - def enqueue(self, command, params = None, commands = None):
299 '''
300 Enqueues command or task.
301
302 @param command: Command(s) to execute. Each command is tuple
303 containing function name and it's parameters.
304 @type command: tuple of list of tuples
305 @param params: Parameters to command.
306 @type params: tuple or string
307 @param commands: List of commands to execute. When this is not
308 none, params are ignored and command is taken as task name.
309 @type commands: list of tuples or strings
310 '''
311 if commands is not None:
312 self.enqueue_task(command, commands)
313 else:
314 self.enqueue_command(command, params)
315
325
327 '''
328 Aborts any remaining operations.
329 '''
330 raise NotImplementedError
331
333 '''
334 Connects to phone.
335 '''
336 self._thread = GammuThread(self._queue, self._config, self._callback)
337 self._thread.start()
338
340 '''
341 Terminates phone connection.
342 '''
343 self.enqueue('Terminate')
344 self._thread.join(timeout)
345 self._thread = None
346