Package gammu :: Module Worker
[hide private]
[frames] | no frames]

Source Code for Module gammu.Worker

  1  # -*- coding: UTF-8 -*- 
  2  # vim: expandtab sw=4 ts=4 sts=4: 
  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 - 2010 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._gammu 
 29  import threading 
 30  import Queue 
 31   
 32   
33 -class InvalidCommand(Exception):
34 ''' 35 Exception indicating invalid command. 36 '''
37 - def __init__(self, value):
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
47 - def __str__(self):
48 ''' 49 Returns textual representation of exception. 50 ''' 51 return 'Invalid command: "%s"' % self.value
52 53
54 -def check_worker_command(command):
55 ''' 56 Checks whether command is valid. 57 58 @param command: Name of command. 59 @type command: string 60 ''' 61 if hasattr(gammu._gammu.StateMachine, command): 62 return 63 64 raise InvalidCommand(command)
65 66
67 -class GammuCommand:
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
80 - def get_command(self):
81 ''' 82 Returns command name. 83 ''' 84 return self._command
85
86 - def get_params(self):
87 ''' 88 Returns command params. 89 ''' 90 return self._params
91
92 - def get_percentage(self):
93 ''' 94 Returns percentage of current task. 95 ''' 96 return self._percentage
97
98 - def __str__(self):
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
108 -class GammuTask:
109 ''' 110 Storage of taks for gammu. 111 '''
112 - def __init__(self, name, commands):
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
137 - def get_next(self):
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
145 - def get_name(self):
146 ''' 147 Returns task name. 148 ''' 149 return self._name
150 151
152 -class GammuThread(threading.Thread):
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._gammu.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._gammu.GSMError, info: 198 errcode = info[0]['Code'] 199 error = gammu._gammu.ErrorNumbers[errcode] 200 201 self._callback(name, result, error, percentage)
202
203 - def run(self):
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 # Wait at most ten seconds for next command 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 if task.name() != 'Init': 229 self._queue.task_done() 230 except AttributeError: 231 # This works since python 2.5 232 pass 233 except ValueError: 234 # This works since python 2.5 235 pass 236 except Queue.Empty: 237 if self._terminate: 238 break 239 # Read the device to catch possible incoming events 240 self._sm.ReadDevice()
241
242 - def kill(self):
243 ''' 244 Forces thread end without emptying queue. 245 ''' 246 self._kill = True
247
248 - def join(self, timeout=None):
249 ''' 250 Terminates thread and waits for it. 251 ''' 252 self._terminate = True 253 threading.Thread.join(self, timeout)
254 255
256 -class GammuWorker:
257 ''' 258 Wrapper class for asynchronous communication with Gammu. It spaws 259 own thread and then passes all commands to this thread. When task is 260 done, caller is notified via callback. 261 ''' 262
263 - def __init__(self, callback):
264 ''' 265 Initializes worker class. 266 267 @param callback: See L{GammuThread.__init__} for description. 268 ''' 269 self._thread = None 270 self._callback = callback 271 self._config = {} 272 self._lock = threading.Lock() 273 self._queue = Queue.Queue()
274
275 - def enqueue_command(self, command, params):
276 ''' 277 Enqueues command. 278 279 @param command: Command(s) to execute. Each command is tuple 280 containing function name and it's parameters. 281 @type command: tuple of list of tuples 282 @param params: Parameters to command. 283 @type params: tuple or string 284 ''' 285 self._queue.put(GammuTask(command, [(command, params)]))
286
287 - def enqueue_task(self, command, commands):
288 ''' 289 Enqueues task. 290 291 @param command: Command(s) to execute. Each command is tuple 292 containing function name and it's parameters. 293 @type command: tuple of list of tuples 294 @param commands: List of commands to execute. 295 @type commands: list of tuples or strings 296 ''' 297 self._queue.put(GammuTask(command, commands))
298
299 - def enqueue(self, command, params = None, commands = None):
300 ''' 301 Enqueues command or task. 302 303 @param command: Command(s) to execute. Each command is tuple 304 containing function name and it's parameters. 305 @type command: tuple of list of tuples 306 @param params: Parameters to command. 307 @type params: tuple or string 308 @param commands: List of commands to execute. When this is not 309 none, params are ignored and command is taken as task name. 310 @type commands: list of tuples or strings 311 ''' 312 if commands is not None: 313 self.enqueue_task(command, commands) 314 else: 315 self.enqueue_command(command, params)
316
317 - def configure(self, config):
318 ''' 319 Configures gammu instance according to config. 320 321 @param config: Gammu configuration, same as 322 L{StateMachine.SetConfig} accepts. 323 @type config: hash 324 ''' 325 self._config = config
326
327 - def abort(self):
328 ''' 329 Aborts any remaining operations. 330 ''' 331 raise NotImplementedError
332
333 - def initiate(self):
334 ''' 335 Connects to phone. 336 ''' 337 self._thread = GammuThread(self._queue, self._config, self._callback) 338 self._thread.start()
339
340 - def terminate(self, timeout=None):
341 ''' 342 Terminates phone connection. 343 ''' 344 self.enqueue('Terminate') 345 self._thread.join(timeout) 346 self._thread = None
347