23 #Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
23 #Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
24 |
24 |
25 import Pyro.core as pyro |
25 import Pyro.core as pyro |
26 from threading import Timer, Thread, Lock, Semaphore |
26 from threading import Timer, Thread, Lock, Semaphore |
27 import ctypes, os, commands, types, sys |
27 import ctypes, os, commands, types, sys |
28 from targets.typemapping import SameEndianessTypeTranslator as TypeTranslator |
28 from targets.typemapping import LogLevelsDefault, LogLevelsCount, SameEndianessTypeTranslator as TypeTranslator |
|
29 |
29 |
30 |
30 if os.name in ("nt", "ce"): |
31 if os.name in ("nt", "ce"): |
31 from _ctypes import LoadLibrary as dlopen |
32 from _ctypes import LoadLibrary as dlopen |
32 from _ctypes import FreeLibrary as dlclose |
33 from _ctypes import FreeLibrary as dlclose |
33 elif os.name == "posix": |
34 elif os.name == "posix": |
77 |
80 |
78 def StatusChange(self): |
81 def StatusChange(self): |
79 if self.statuschange is not None: |
82 if self.statuschange is not None: |
80 self.statuschange(self.PLCStatus) |
83 self.statuschange(self.PLCStatus) |
81 |
84 |
|
85 def LogMessage(self, *args): |
|
86 if len(args) == 2: |
|
87 level, msg = args |
|
88 else: |
|
89 level = LogLevelsDefault |
|
90 msg, = args |
|
91 return self._LogMessage(level, msg, len(msg)) |
|
92 |
|
93 |
|
94 def GetLogCount(self, level): |
|
95 if self._GetLogCount is not None : |
|
96 return int(self._GetLogCount(level)) |
|
97 elif self._loading_error is not None and level==0: |
|
98 return 1; |
|
99 |
|
100 def GetLogMessage(self, level, msgid): |
|
101 tick = ctypes.c_uint32() |
|
102 tv_sec = ctypes.c_uint32() |
|
103 tv_nsec = ctypes.c_uint32() |
|
104 if self._GetLogMessage is not None: |
|
105 maxsz = len(self._log_read_buffer)-1 |
|
106 sz = self._GetLogMessage(level, msgid, |
|
107 self._log_read_buffer, maxsz, |
|
108 ctypes.byref(tick), |
|
109 ctypes.byref(tv_sec), |
|
110 ctypes.byref(tv_nsec)) |
|
111 if sz and sz <= maxsz: |
|
112 self._log_read_buffer[sz] = '\x00' |
|
113 return self._log_read_buffer.value,tick.value,tv_sec.value,tv_nsec.value |
|
114 elif self._loading_error is not None and level==0: |
|
115 return self._loading_error,0,0,0 |
|
116 return None |
|
117 |
82 def _GetMD5FileName(self): |
118 def _GetMD5FileName(self): |
83 return os.path.join(self.workingdir, "lasttransferedPLC.md5") |
119 return os.path.join(self.workingdir, "lasttransferedPLC.md5") |
84 |
120 |
85 def _GetLibFileName(self): |
121 def _GetLibFileName(self): |
86 return os.path.join(self.workingdir,self.CurrentPLCFilename) |
122 return os.path.join(self.workingdir,self.CurrentPLCFilename) |
103 self._stopPLC_real.restype = None |
139 self._stopPLC_real.restype = None |
104 |
140 |
105 self._PythonIterator = getattr(self.PLClibraryHandle, "PythonIterator", None) |
141 self._PythonIterator = getattr(self.PLClibraryHandle, "PythonIterator", None) |
106 if self._PythonIterator is not None: |
142 if self._PythonIterator is not None: |
107 self._PythonIterator.restype = ctypes.c_char_p |
143 self._PythonIterator.restype = ctypes.c_char_p |
108 self._PythonIterator.argtypes = [ctypes.c_char_p] |
144 self._PythonIterator.argtypes = [ctypes.c_char_p, ctypes.POINTER(ctypes.c_void_p)] |
109 |
145 |
110 self._stopPLC = self._stopPLC_real |
146 self._stopPLC = self._stopPLC_real |
111 else: |
147 else: |
112 # If python confnode is not enabled, we reuse _PythonIterator |
148 # If python confnode is not enabled, we reuse _PythonIterator |
113 # as a call that block pythonthread until StopPLC |
149 # as a call that block pythonthread until StopPLC |
114 self.PythonIteratorLock = Lock() |
150 self.PythonIteratorLock = Lock() |
115 self.PythonIteratorLock.acquire() |
151 self.PythonIteratorLock.acquire() |
116 def PythonIterator(res): |
152 def PythonIterator(res, blkid): |
117 self.PythonIteratorLock.acquire() |
153 self.PythonIteratorLock.acquire() |
118 self.PythonIteratorLock.release() |
154 self.PythonIteratorLock.release() |
119 return None |
155 return None |
120 self._PythonIterator = PythonIterator |
156 self._PythonIterator = PythonIterator |
121 |
157 |
143 self._suspendDebug.restype = ctypes.c_int |
179 self._suspendDebug.restype = ctypes.c_int |
144 self._suspendDebug.argtypes = [ctypes.c_int] |
180 self._suspendDebug.argtypes = [ctypes.c_int] |
145 |
181 |
146 self._resumeDebug = self.PLClibraryHandle.resumeDebug |
182 self._resumeDebug = self.PLClibraryHandle.resumeDebug |
147 self._resumeDebug.restype = None |
183 self._resumeDebug.restype = None |
148 |
184 |
|
185 self._GetLogCount = self.PLClibraryHandle.GetLogCount |
|
186 self._GetLogCount.restype = ctypes.c_uint32 |
|
187 self._GetLogCount.argtypes = [ctypes.c_uint8] |
|
188 |
|
189 self._LogMessage = self.PLClibraryHandle.LogMessage |
|
190 self._LogMessage.restype = ctypes.c_int |
|
191 self._LogMessage.argtypes = [ctypes.c_uint8, ctypes.c_char_p, ctypes.c_uint32] |
|
192 |
|
193 self._log_read_buffer = ctypes.create_string_buffer(1<<14) #16K |
|
194 self._GetLogMessage = self.PLClibraryHandle.GetLogMessage |
|
195 self._GetLogMessage.restype = ctypes.c_uint32 |
|
196 self._GetLogMessage.argtypes = [ctypes.c_uint8, ctypes.c_uint32, ctypes.c_char_p, ctypes.c_uint32, ctypes.POINTER(ctypes.c_uint32), ctypes.POINTER(ctypes.c_uint32), ctypes.POINTER(ctypes.c_uint32)] |
|
197 |
|
198 self._loading_error = None |
149 return True |
199 return True |
150 except: |
200 except: |
151 PLCprint(traceback.format_exc()) |
201 self._loading_error = traceback.format_exc() |
|
202 PLCprint(self._loading_error) |
152 return False |
203 return False |
153 |
204 |
154 def _FreePLC(self): |
205 def _FreePLC(self): |
155 """ |
206 """ |
156 Unload PLC library. |
207 Unload PLC library. |
166 self._FreeDebugData = lambda:None |
217 self._FreeDebugData = lambda:None |
167 self._GetDebugData = lambda:-1 |
218 self._GetDebugData = lambda:-1 |
168 self._suspendDebug = lambda x:-1 |
219 self._suspendDebug = lambda x:-1 |
169 self._resumeDebug = lambda:None |
220 self._resumeDebug = lambda:None |
170 self._PythonIterator = lambda:"" |
221 self._PythonIterator = lambda:"" |
|
222 self._GetLogCount = None |
|
223 self._LogMessage = lambda l,m,s:PLCprint("OFF LOG :"+m) |
|
224 self._GetLogMessage = None |
171 self.PLClibraryHandle = None |
225 self.PLClibraryHandle = None |
172 # Unload library explicitely |
226 # Unload library explicitely |
173 if getattr(self,"_PLClibraryHandle",None) is not None: |
227 if getattr(self,"_PLClibraryHandle",None) is not None: |
174 dlclose(self._PLClibraryHandle) |
228 dlclose(self._PLClibraryHandle) |
175 self._PLClibraryHandle = None |
229 self._PLClibraryHandle = None |
219 def PythonThreadProc(self): |
273 def PythonThreadProc(self): |
220 self.PLCStatus = "Started" |
274 self.PLCStatus = "Started" |
221 self.StatusChange() |
275 self.StatusChange() |
222 self.StartSem.release() |
276 self.StartSem.release() |
223 self.evaluator(self.PrepareRuntimePy) |
277 self.evaluator(self.PrepareRuntimePy) |
224 res,cmd = "None","None" |
278 res,cmd,blkid = "None","None",ctypes.c_void_p() |
|
279 compile_cache={} |
225 while True: |
280 while True: |
226 #print "_PythonIterator(", res, ")", |
281 # print "_PythonIterator(", res, ")", |
227 cmd = self._PythonIterator(res) |
282 cmd = self._PythonIterator(res,blkid) |
228 #print " -> ", cmd |
283 FBID = blkid.value |
|
284 # print " -> ", cmd, blkid |
229 if cmd is None: |
285 if cmd is None: |
230 break |
286 break |
231 try : |
287 try : |
232 res = str(self.evaluator(eval,cmd,self.python_threads_vars)) |
288 self.python_threads_vars["FBID"]=FBID |
|
289 ccmd,AST =compile_cache.get(FBID, (None,None)) |
|
290 if ccmd is None or ccmd!=cmd: |
|
291 AST = compile(cmd, '<plc>', 'eval') |
|
292 compile_cache[FBID]=(cmd,AST) |
|
293 result,exp = self.evaluator(eval,cmd,self.python_threads_vars) |
|
294 if exp is not None: |
|
295 raise(exp) |
|
296 else: |
|
297 res=str(result) |
|
298 self.python_threads_vars["FBID"]=None |
233 except Exception,e: |
299 except Exception,e: |
234 res = "#EXCEPTION : "+str(e) |
300 res = "#EXCEPTION : "+str(e) |
235 PLCprint(res) |
301 PLCprint(('*** Python eval EXCEPTION ***\n'+ |
|
302 '| Function Block ID: %d\n'+ |
|
303 '| Command : "%s"\n'+ |
|
304 '| Exception : "%s"')%(FBID,cmd,str(e))) |
236 self.PLCStatus = "Stopped" |
305 self.PLCStatus = "Stopped" |
237 self.StatusChange() |
306 self.StatusChange() |
238 self.evaluator(self.FinishRuntimePy) |
307 self.evaluator(self.FinishRuntimePy) |
239 |
308 |
240 def StartPLC(self): |
309 def StartPLC(self): |
241 PLCprint("StartPLC") |
|
242 if self.CurrentPLCFilename is not None and self.PLCStatus == "Stopped": |
310 if self.CurrentPLCFilename is not None and self.PLCStatus == "Stopped": |
243 c_argv = ctypes.c_char_p * len(self.argv) |
311 c_argv = ctypes.c_char_p * len(self.argv) |
244 error = None |
312 error = None |
245 if self._LoadNewPLC(): |
313 res = self._startPLC(len(self.argv),c_argv(*self.argv)) |
246 if self._startPLC(len(self.argv),c_argv(*self.argv)) == 0: |
314 if res == 0: |
247 self.StartSem=Semaphore(0) |
315 self.StartSem=Semaphore(0) |
248 self.PythonThread = Thread(target=self.PythonThreadProc) |
316 self.PythonThread = Thread(target=self.PythonThreadProc) |
249 self.PythonThread.start() |
317 self.PythonThread.start() |
250 self.StartSem.acquire() |
318 self.StartSem.acquire() |
251 else: |
319 self.LogMessage("PLC started") |
252 error = "starting" |
|
253 else: |
320 else: |
254 error = "loading" |
321 self.LogMessage(_("Problem starting PLC : error %d" % res)) |
255 if error is not None: |
|
256 PLCprint("Problem %s PLC"%error) |
|
257 self.PLCStatus = "Broken" |
322 self.PLCStatus = "Broken" |
258 self.StatusChange() |
323 self.StatusChange() |
259 self._FreePLC() |
|
260 |
324 |
261 def StopPLC(self): |
325 def StopPLC(self): |
262 PLCprint("StopPLC") |
|
263 if self.PLCStatus == "Started": |
326 if self.PLCStatus == "Started": |
|
327 self.LogMessage("PLC stopped") |
264 self._stopPLC() |
328 self._stopPLC() |
265 self.PythonThread.join() |
329 self.PythonThread.join() |
266 self._FreePLC() |
|
267 return True |
330 return True |
268 return False |
331 return False |
269 |
332 |
270 def _Reload(self): |
333 def _Reload(self): |
271 self.daemon.shutdown(True) |
334 self.daemon.shutdown(True) |
278 # respawn python interpreter |
341 # respawn python interpreter |
279 Timer(0.1,self._Reload).start() |
342 Timer(0.1,self._Reload).start() |
280 return True |
343 return True |
281 |
344 |
282 def GetPLCstatus(self): |
345 def GetPLCstatus(self): |
283 return self.PLCStatus |
346 return self.PLCStatus, map(self.GetLogCount,xrange(LogLevelsCount)) |
284 |
347 |
285 def NewPLC(self, md5sum, data, extrafiles): |
348 def NewPLC(self, md5sum, data, extrafiles): |
286 PLCprint("NewPLC (%s)"%md5sum) |
349 self.LogMessage("NewPLC (%s)"%md5sum) |
287 if self.PLCStatus in ["Stopped", "Empty", "Broken"]: |
350 if self.PLCStatus in ["Stopped", "Empty", "Broken"]: |
288 NewFileName = md5sum + lib_ext |
351 NewFileName = md5sum + lib_ext |
289 extra_files_log = os.path.join(self.workingdir,"extra_files.txt") |
352 extra_files_log = os.path.join(self.workingdir,"extra_files.txt") |
|
353 |
|
354 self._FreePLC() |
|
355 self.PLCStatus = "Empty" |
|
356 |
290 try: |
357 try: |
291 os.remove(os.path.join(self.workingdir, |
358 os.remove(os.path.join(self.workingdir, |
292 self.CurrentPLCFilename)) |
359 self.CurrentPLCFilename)) |
293 for filename in file(extra_files_log, "r").readlines() + [extra_files_log]: |
360 for filename in file(extra_files_log, "r").readlines() + [extra_files_log]: |
294 try: |
361 try: |
314 log.write(fname+'\n') |
381 log.write(fname+'\n') |
315 |
382 |
316 # Store new PLC filename |
383 # Store new PLC filename |
317 self.CurrentPLCFilename = NewFileName |
384 self.CurrentPLCFilename = NewFileName |
318 except: |
385 except: |
|
386 self.PLCStatus = "Broken" |
|
387 self.StatusChange() |
319 PLCprint(traceback.format_exc()) |
388 PLCprint(traceback.format_exc()) |
320 return False |
389 return False |
321 if self.PLCStatus == "Empty": |
390 |
|
391 if self._LoadNewPLC(): |
322 self.PLCStatus = "Stopped" |
392 self.PLCStatus = "Stopped" |
323 return True |
393 else: |
|
394 self._FreePLC() |
|
395 self.StatusChange() |
|
396 |
|
397 return self.PLCStatus == "Stopped" |
324 return False |
398 return False |
325 |
399 |
326 def MatchMD5(self, MD5): |
400 def MatchMD5(self, MD5): |
327 try: |
401 try: |
328 last_md5 = open(self._GetMD5FileName(), "r").read() |
402 last_md5 = open(self._GetMD5FileName(), "r").read() |