185 self.pyruntimevars = server.pyruntimevars |
187 self.pyruntimevars = server.pyruntimevars |
186 self._loading_error = None |
188 self._loading_error = None |
187 self.python_runtime_vars = None |
189 self.python_runtime_vars = None |
188 self.TraceThread = None |
190 self.TraceThread = None |
189 self.TraceLock = Lock() |
191 self.TraceLock = Lock() |
190 self.TraceWakeup = Event() |
|
191 self.Traces = [] |
192 self.Traces = [] |
192 |
193 |
|
194 # First task of worker -> no @RunInMain |
193 def AutoLoad(self): |
195 def AutoLoad(self): |
194 # Get the last transfered PLC if connector must be restart |
196 # Get the last transfered PLC |
195 try: |
197 try: |
196 self.CurrentPLCFilename = open( |
198 self.CurrentPLCFilename = open( |
197 self._GetMD5FileName(), |
199 self._GetMD5FileName(), |
198 "r").read().strip() + lib_ext |
200 "r").read().strip() + lib_ext |
199 if self.LoadPLC(): |
201 if self.LoadPLC(): |
216 PLCprint(msg) |
219 PLCprint(msg) |
217 if self._LogMessage is not None: |
220 if self._LogMessage is not None: |
218 return self._LogMessage(level, msg, len(msg)) |
221 return self._LogMessage(level, msg, len(msg)) |
219 return None |
222 return None |
220 |
223 |
|
224 @RunInMain |
221 def ResetLogCount(self): |
225 def ResetLogCount(self): |
222 if self._ResetLogCount is not None: |
226 if self._ResetLogCount is not None: |
223 self._ResetLogCount() |
227 self._ResetLogCount() |
224 |
228 |
|
229 # used internaly |
225 def GetLogCount(self, level): |
230 def GetLogCount(self, level): |
226 if self._GetLogCount is not None: |
231 if self._GetLogCount is not None: |
227 return int(self._GetLogCount(level)) |
232 return int(self._GetLogCount(level)) |
228 elif self._loading_error is not None and level == 0: |
233 elif self._loading_error is not None and level == 0: |
229 return 1 |
234 return 1 |
230 |
235 |
|
236 @RunInMain |
231 def GetLogMessage(self, level, msgid): |
237 def GetLogMessage(self, level, msgid): |
232 tick = ctypes.c_uint32() |
238 tick = ctypes.c_uint32() |
233 tv_sec = ctypes.c_uint32() |
239 tv_sec = ctypes.c_uint32() |
234 tv_nsec = ctypes.c_uint32() |
240 tv_nsec = ctypes.c_uint32() |
235 if self._GetLogMessage is not None: |
241 if self._GetLogMessage is not None: |
250 return os.path.join(self.workingdir, "lasttransferedPLC.md5") |
256 return os.path.join(self.workingdir, "lasttransferedPLC.md5") |
251 |
257 |
252 def _GetLibFileName(self): |
258 def _GetLibFileName(self): |
253 return os.path.join(self.workingdir, self.CurrentPLCFilename) |
259 return os.path.join(self.workingdir, self.CurrentPLCFilename) |
254 |
260 |
255 @RunInMain |
261 def _LoadPLC(self): |
256 def LoadPLC(self): |
|
257 """ |
262 """ |
258 Load PLC library |
263 Load PLC library |
259 Declare all functions, arguments and return values |
264 Declare all functions, arguments and return values |
260 """ |
265 """ |
261 md5 = open(self._GetMD5FileName(), "r").read() |
266 md5 = open(self._GetMD5FileName(), "r").read() |
|
267 self.PLClibraryLock.acquire() |
262 try: |
268 try: |
263 self._PLClibraryHandle = dlopen(self._GetLibFileName()) |
269 self._PLClibraryHandle = dlopen(self._GetLibFileName()) |
264 self.PLClibraryHandle = ctypes.CDLL(self.CurrentPLCFilename, handle=self._PLClibraryHandle) |
270 self.PLClibraryHandle = ctypes.CDLL(self.CurrentPLCFilename, handle=self._PLClibraryHandle) |
265 |
271 |
266 self.PLC_ID = ctypes.c_char_p.in_dll(self.PLClibraryHandle, "PLC_ID") |
272 self.PLC_ID = ctypes.c_char_p.in_dll(self.PLClibraryHandle, "PLC_ID") |
333 self._GetLogMessage.restype = ctypes.c_uint32 |
339 self._GetLogMessage.restype = ctypes.c_uint32 |
334 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)] |
340 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)] |
335 |
341 |
336 self._loading_error = None |
342 self._loading_error = None |
337 |
343 |
338 self.PythonRuntimeInit() |
|
339 |
|
340 return True |
|
341 except Exception: |
344 except Exception: |
342 self._loading_error = traceback.format_exc() |
345 self._loading_error = traceback.format_exc() |
343 PLCprint(self._loading_error) |
346 PLCprint(self._loading_error) |
|
347 return False |
|
348 finally: |
|
349 self.PLClibraryLock.release() |
|
350 |
|
351 return True |
|
352 |
|
353 @RunInMain |
|
354 def LoadPLC(self): |
|
355 res = self._LoadPLC() |
|
356 if res: |
|
357 self.PythonRuntimeInit() |
|
358 else: |
344 self._FreePLC() |
359 self._FreePLC() |
345 return False |
360 |
|
361 return res |
346 |
362 |
347 @RunInMain |
363 @RunInMain |
348 def UnLoadPLC(self): |
364 def UnLoadPLC(self): |
349 self.PythonRuntimeCleanup() |
365 self.PythonRuntimeCleanup() |
350 self._FreePLC() |
366 self._FreePLC() |
373 """ |
389 """ |
374 Unload PLC library. |
390 Unload PLC library. |
375 This is also called by __init__ to create dummy C func proxies |
391 This is also called by __init__ to create dummy C func proxies |
376 """ |
392 """ |
377 self.PLClibraryLock.acquire() |
393 self.PLClibraryLock.acquire() |
378 |
394 try: |
379 # Unload library explicitely |
395 # Unload library explicitely |
380 if getattr(self, "_PLClibraryHandle", None) is not None: |
396 if getattr(self, "_PLClibraryHandle", None) is not None: |
381 dlclose(self._PLClibraryHandle) |
397 dlclose(self._PLClibraryHandle) |
382 |
398 |
383 # Forget all refs to library |
399 # Forget all refs to library |
384 self._InitPLCStubCalls() |
400 self._InitPLCStubCalls() |
385 |
401 |
386 self.PLClibraryLock.release() |
402 finally: |
|
403 self.PLClibraryLock.release() |
|
404 |
387 return False |
405 return False |
388 |
406 |
389 def PythonRuntimeCall(self, methodname): |
407 def PythonRuntimeCall(self, methodname): |
390 """ |
408 """ |
391 Calls init, start, stop or cleanup method provided by |
409 Calls init, start, stop or cleanup method provided by |
394 for method in self.python_runtime_vars.get("_runtime_%s" % methodname, []): |
412 for method in self.python_runtime_vars.get("_runtime_%s" % methodname, []): |
395 _res, exp = self.evaluator(method) |
413 _res, exp = self.evaluator(method) |
396 if exp is not None: |
414 if exp is not None: |
397 self.LogMessage(0, '\n'.join(traceback.format_exception(*exp))) |
415 self.LogMessage(0, '\n'.join(traceback.format_exception(*exp))) |
398 |
416 |
|
417 # used internaly |
399 def PythonRuntimeInit(self): |
418 def PythonRuntimeInit(self): |
400 MethodNames = ["init", "start", "stop", "cleanup"] |
419 MethodNames = ["init", "start", "stop", "cleanup"] |
401 self.python_runtime_vars = globals().copy() |
420 self.python_runtime_vars = globals().copy() |
402 self.python_runtime_vars.update(self.pyruntimevars) |
421 self.python_runtime_vars.update(self.pyruntimevars) |
403 parent = self |
422 parent = self |
507 self.PythonThread.join() |
527 self.PythonThread.join() |
508 self.PLCStatus = "Stopped" |
528 self.PLCStatus = "Stopped" |
509 self.StatusChange() |
529 self.StatusChange() |
510 self.PythonRuntimeCall("stop") |
530 self.PythonRuntimeCall("stop") |
511 if self.TraceThread is not None: |
531 if self.TraceThread is not None: |
512 self.TraceWakeup.set() |
|
513 self.TraceThread.join() |
532 self.TraceThread.join() |
514 self.TraceThread = None |
533 self.TraceThread = None |
515 return True |
534 return True |
516 return False |
535 return False |
517 |
536 |
|
537 @RunInMain |
518 def GetPLCstatus(self): |
538 def GetPLCstatus(self): |
519 return self.PLCStatus, map(self.GetLogCount, xrange(LogLevelsCount)) |
539 return self.PLCStatus, map(self.GetLogCount, xrange(LogLevelsCount)) |
520 |
540 |
521 @RunInMain |
541 @RunInMain |
522 def NewPLC(self, md5sum, data, extrafiles): |
542 def NewPLC(self, md5sum, data, extrafiles): |
523 if self.PLCStatus in ["Stopped", "Empty", "Broken"]: |
543 if self.PLCStatus in ["Stopped", "Empty", "Broken"]: |
524 NewFileName = md5sum + lib_ext |
544 NewFileName = md5sum + lib_ext |
525 extra_files_log = os.path.join(self.workingdir, "extra_files.txt") |
545 extra_files_log = os.path.join(self.workingdir, "extra_files.txt") |
526 |
546 |
527 self.UnLoadPLC() |
547 old_PLC_filename = os.path.join(self.workingdir, \ |
|
548 self.CurrentPLCFilename) \ |
|
549 if self.CurrentPLCFilename is not None \ |
|
550 else None |
|
551 new_PLC_filename = os.path.join(self.workingdir, NewFileName) |
|
552 |
|
553 # Some platform (i.e. Xenomai) don't like reloading same .so file |
|
554 replace_PLC_shared_object = new_PLC_filename != old_PLC_filename |
|
555 |
|
556 if replace_PLC_shared_object: |
|
557 self.UnLoadPLC() |
528 |
558 |
529 self.LogMessage("NewPLC (%s)" % md5sum) |
559 self.LogMessage("NewPLC (%s)" % md5sum) |
530 self.PLCStatus = "Empty" |
560 self.PLCStatus = "Empty" |
531 |
561 |
|
562 |
532 try: |
563 try: |
533 os.remove(os.path.join(self.workingdir, |
564 if replace_PLC_shared_object: |
534 self.CurrentPLCFilename)) |
565 os.remove(old_PLC_filename) |
535 for filename in file(extra_files_log, "r").readlines() + [extra_files_log]: |
566 for filename in file(extra_files_log, "r").readlines() + [extra_files_log]: |
536 try: |
567 try: |
537 os.remove(os.path.join(self.workingdir, filename.strip())) |
568 os.remove(os.path.join(self.workingdir, filename.strip())) |
538 except Exception: |
569 except Exception: |
539 pass |
570 pass |
540 except Exception: |
571 except Exception: |
541 pass |
572 pass |
542 |
573 |
543 try: |
574 try: |
544 # Create new PLC file |
575 # Create new PLC file |
545 open(os.path.join(self.workingdir, NewFileName), |
576 if replace_PLC_shared_object: |
546 'wb').write(data) |
577 open(new_PLC_filename, 'wb').write(data) |
547 |
578 |
548 # Store new PLC filename based on md5 key |
579 # Store new PLC filename based on md5 key |
549 open(self._GetMD5FileName(), "w").write(md5sum) |
580 open(self._GetMD5FileName(), "w").write(md5sum) |
550 |
581 |
551 # Then write the files |
582 # Then write the files |
600 self._TracesSwap() |
633 self._TracesSwap() |
601 self._resumeDebug() |
634 self._resumeDebug() |
602 else: |
635 else: |
603 self._suspendDebug(True) |
636 self._suspendDebug(True) |
604 |
637 |
605 def _TracesPush(self, trace): |
|
606 self.TraceLock.acquire() |
|
607 lT = len(self.Traces) |
|
608 if lT != 0 and lT * len(self.Traces[0]) > 1024 * 1024: |
|
609 self.Traces.pop(0) |
|
610 self.Traces.append(trace) |
|
611 self.TraceLock.release() |
|
612 |
638 |
613 def _TracesSwap(self): |
639 def _TracesSwap(self): |
614 self.LastSwapTrace = time() |
640 self.LastSwapTrace = time() |
615 if self.TraceThread is None and self.PLCStatus == "Started": |
641 if self.TraceThread is None and self.PLCStatus == "Started": |
616 self.TraceThread = Thread(target=self.TraceThreadProc) |
642 self.TraceThread = Thread(target=self.TraceThreadProc) |
617 self.TraceThread.start() |
643 self.TraceThread.start() |
618 self.TraceLock.acquire() |
644 self.TraceLock.acquire() |
619 Traces = self.Traces |
645 Traces = self.Traces |
620 self.Traces = [] |
646 self.Traces = [] |
621 self.TraceLock.release() |
647 self.TraceLock.release() |
622 self.TraceWakeup.set() |
|
623 return Traces |
648 return Traces |
624 |
649 |
625 def _TracesAutoSuspend(self): |
650 @RunInMain |
626 # TraceProc stops here if Traces not polled for 3 seconds |
|
627 traces_age = time() - self.LastSwapTrace |
|
628 if traces_age > 3: |
|
629 self.TraceLock.acquire() |
|
630 self.Traces = [] |
|
631 self.TraceLock.release() |
|
632 self._suspendDebug(True) # Disable debugger |
|
633 self.TraceWakeup.clear() |
|
634 self.TraceWakeup.wait() |
|
635 self._resumeDebug() # Re-enable debugger |
|
636 |
|
637 def _TracesFlush(self): |
|
638 self.TraceLock.acquire() |
|
639 self.Traces = [] |
|
640 self.TraceLock.release() |
|
641 |
|
642 def GetTraceVariables(self): |
651 def GetTraceVariables(self): |
643 return self.PLCStatus, self._TracesSwap() |
652 return self.PLCStatus, self._TracesSwap() |
644 |
653 |
645 def TraceThreadProc(self): |
654 def TraceThreadProc(self): |
646 """ |
655 """ |
647 Return a list of traces, corresponding to the list of required idx |
656 Return a list of traces, corresponding to the list of required idx |
648 """ |
657 """ |
|
658 self._resumeDebug() # Re-enable debugger |
649 while self.PLCStatus == "Started": |
659 while self.PLCStatus == "Started": |
650 tick = ctypes.c_uint32() |
660 tick = ctypes.c_uint32() |
651 size = ctypes.c_uint32() |
661 size = ctypes.c_uint32() |
652 buff = ctypes.c_void_p() |
662 buff = ctypes.c_void_p() |
653 TraceBuffer = None |
663 TraceBuffer = None |
654 if self.PLClibraryLock.acquire(False): |
664 |
655 res = self._GetDebugData(ctypes.byref(tick), |
665 self.PLClibraryLock.acquire() |
656 ctypes.byref(size), |
666 |
657 ctypes.byref(buff)) |
667 res = self._GetDebugData(ctypes.byref(tick), |
658 if res == 0: |
668 ctypes.byref(size), |
659 if size.value: |
669 ctypes.byref(buff)) |
660 TraceBuffer = ctypes.string_at(buff.value, size.value) |
670 if res == 0: |
661 self._FreeDebugData() |
671 if size.value: |
662 self.PLClibraryLock.release() |
672 TraceBuffer = ctypes.string_at(buff.value, size.value) |
|
673 self._FreeDebugData() |
|
674 |
|
675 self.PLClibraryLock.release() |
663 |
676 |
664 if res != 0: |
677 # leave thread if GetDebugData isn't happy. |
665 break |
678 if res != 0: |
|
679 break |
666 |
680 |
667 if TraceBuffer is not None: |
681 if TraceBuffer is not None: |
668 self._TracesPush((tick.value, TraceBuffer)) |
682 self.TraceLock.acquire() |
669 self._TracesAutoSuspend() |
683 lT = len(self.Traces) |
670 self._TracesFlush() |
684 if lT != 0 and lT * len(self.Traces[0]) > 1024 * 1024: |
|
685 self.Traces.pop(0) |
|
686 self.Traces.append((tick.value, TraceBuffer)) |
|
687 self.TraceLock.release() |
|
688 |
|
689 # TraceProc stops here if Traces not polled for 3 seconds |
|
690 traces_age = time() - self.LastSwapTrace |
|
691 if traces_age > 3: |
|
692 self.TraceLock.acquire() |
|
693 self.Traces = [] |
|
694 self.TraceLock.release() |
|
695 self._suspendDebug(True) # Disable debugger |
|
696 break |
|
697 |
|
698 self.TraceThread = None |
671 |
699 |
672 def RemoteExec(self, script, *kwargs): |
700 def RemoteExec(self, script, *kwargs): |
673 try: |
701 try: |
674 exec script in kwargs |
702 exec script in kwargs |
675 except Exception: |
703 except Exception: |