105 tick = ctypes.c_uint32() |
105 tick = ctypes.c_uint32() |
106 tv_sec = ctypes.c_uint32() |
106 tv_sec = ctypes.c_uint32() |
107 tv_nsec = ctypes.c_uint32() |
107 tv_nsec = ctypes.c_uint32() |
108 if self._GetLogMessage is not None: |
108 if self._GetLogMessage is not None: |
109 maxsz = len(self._log_read_buffer)-1 |
109 maxsz = len(self._log_read_buffer)-1 |
110 sz = self._GetLogMessage(level, msgid, |
110 sz = self._GetLogMessage(level, msgid, |
111 self._log_read_buffer, maxsz, |
111 self._log_read_buffer, maxsz, |
112 ctypes.byref(tick), |
112 ctypes.byref(tick), |
113 ctypes.byref(tv_sec), |
113 ctypes.byref(tv_sec), |
114 ctypes.byref(tv_nsec)) |
114 ctypes.byref(tv_nsec)) |
115 if sz and sz <= maxsz: |
115 if sz and sz <= maxsz: |
132 Declare all functions, arguments and return values |
132 Declare all functions, arguments and return values |
133 """ |
133 """ |
134 try: |
134 try: |
135 self._PLClibraryHandle = dlopen(self._GetLibFileName()) |
135 self._PLClibraryHandle = dlopen(self._GetLibFileName()) |
136 self.PLClibraryHandle = ctypes.CDLL(self.CurrentPLCFilename, handle=self._PLClibraryHandle) |
136 self.PLClibraryHandle = ctypes.CDLL(self.CurrentPLCFilename, handle=self._PLClibraryHandle) |
137 |
137 |
138 self._startPLC = self.PLClibraryHandle.startPLC |
138 self._startPLC = self.PLClibraryHandle.startPLC |
139 self._startPLC.restype = ctypes.c_int |
139 self._startPLC.restype = ctypes.c_int |
140 self._startPLC.argtypes = [ctypes.c_int, ctypes.POINTER(ctypes.c_char_p)] |
140 self._startPLC.argtypes = [ctypes.c_int, ctypes.POINTER(ctypes.c_char_p)] |
141 |
141 |
142 self._stopPLC_real = self.PLClibraryHandle.stopPLC |
142 self._stopPLC_real = self.PLClibraryHandle.stopPLC |
143 self._stopPLC_real.restype = None |
143 self._stopPLC_real.restype = None |
144 |
144 |
145 self._PythonIterator = getattr(self.PLClibraryHandle, "PythonIterator", None) |
145 self._PythonIterator = getattr(self.PLClibraryHandle, "PythonIterator", None) |
146 if self._PythonIterator is not None: |
146 if self._PythonIterator is not None: |
147 self._PythonIterator.restype = ctypes.c_char_p |
147 self._PythonIterator.restype = ctypes.c_char_p |
148 self._PythonIterator.argtypes = [ctypes.c_char_p, ctypes.POINTER(ctypes.c_void_p)] |
148 self._PythonIterator.argtypes = [ctypes.c_char_p, ctypes.POINTER(ctypes.c_void_p)] |
149 |
149 |
150 self._stopPLC = self._stopPLC_real |
150 self._stopPLC = self._stopPLC_real |
151 else: |
151 else: |
152 # If python confnode is not enabled, we reuse _PythonIterator |
152 # If python confnode is not enabled, we reuse _PythonIterator |
153 # as a call that block pythonthread until StopPLC |
153 # as a call that block pythonthread until StopPLC |
154 self.PythonIteratorLock = Lock() |
154 self.PythonIteratorLock = Lock() |
155 self.PythonIteratorLock.acquire() |
155 self.PythonIteratorLock.acquire() |
156 def PythonIterator(res, blkid): |
156 def PythonIterator(res, blkid): |
157 self.PythonIteratorLock.acquire() |
157 self.PythonIteratorLock.acquire() |
158 self.PythonIteratorLock.release() |
158 self.PythonIteratorLock.release() |
159 return None |
159 return None |
160 self._PythonIterator = PythonIterator |
160 self._PythonIterator = PythonIterator |
161 |
161 |
162 def __StopPLC(): |
162 def __StopPLC(): |
163 self._stopPLC_real() |
163 self._stopPLC_real() |
164 self.PythonIteratorLock.release() |
164 self.PythonIteratorLock.release() |
165 self._stopPLC = __StopPLC |
165 self._stopPLC = __StopPLC |
166 |
166 |
167 |
167 |
168 self._ResetDebugVariables = self.PLClibraryHandle.ResetDebugVariables |
168 self._ResetDebugVariables = self.PLClibraryHandle.ResetDebugVariables |
169 self._ResetDebugVariables.restype = None |
169 self._ResetDebugVariables.restype = None |
170 |
170 |
171 self._RegisterDebugVariable = self.PLClibraryHandle.RegisterDebugVariable |
171 self._RegisterDebugVariable = self.PLClibraryHandle.RegisterDebugVariable |
172 self._RegisterDebugVariable.restype = None |
172 self._RegisterDebugVariable.restype = None |
173 self._RegisterDebugVariable.argtypes = [ctypes.c_int, ctypes.c_void_p] |
173 self._RegisterDebugVariable.argtypes = [ctypes.c_int, ctypes.c_void_p] |
174 |
174 |
175 self._FreeDebugData = self.PLClibraryHandle.FreeDebugData |
175 self._FreeDebugData = self.PLClibraryHandle.FreeDebugData |
176 self._FreeDebugData.restype = None |
176 self._FreeDebugData.restype = None |
177 |
177 |
178 self._GetDebugData = self.PLClibraryHandle.GetDebugData |
178 self._GetDebugData = self.PLClibraryHandle.GetDebugData |
179 self._GetDebugData.restype = ctypes.c_int |
179 self._GetDebugData.restype = ctypes.c_int |
180 self._GetDebugData.argtypes = [ctypes.POINTER(ctypes.c_uint32), ctypes.POINTER(ctypes.c_uint32), ctypes.POINTER(ctypes.c_void_p)] |
180 self._GetDebugData.argtypes = [ctypes.POINTER(ctypes.c_uint32), ctypes.POINTER(ctypes.c_uint32), ctypes.POINTER(ctypes.c_void_p)] |
181 |
181 |
182 self._suspendDebug = self.PLClibraryHandle.suspendDebug |
182 self._suspendDebug = self.PLClibraryHandle.suspendDebug |
183 self._suspendDebug.restype = ctypes.c_int |
183 self._suspendDebug.restype = ctypes.c_int |
184 self._suspendDebug.argtypes = [ctypes.c_int] |
184 self._suspendDebug.argtypes = [ctypes.c_int] |
231 self._FreeDebugData = lambda:None |
231 self._FreeDebugData = lambda:None |
232 self._GetDebugData = lambda:-1 |
232 self._GetDebugData = lambda:-1 |
233 self._suspendDebug = lambda x:-1 |
233 self._suspendDebug = lambda x:-1 |
234 self._resumeDebug = lambda:None |
234 self._resumeDebug = lambda:None |
235 self._PythonIterator = lambda:"" |
235 self._PythonIterator = lambda:"" |
236 self._GetLogCount = None |
236 self._GetLogCount = None |
237 self._LogMessage = lambda l,m,s:PLCprint("OFF LOG :"+m) |
237 self._LogMessage = lambda l,m,s:PLCprint("OFF LOG :"+m) |
238 self._GetLogMessage = None |
238 self._GetLogMessage = None |
239 self.PLClibraryHandle = None |
239 self.PLClibraryHandle = None |
240 # Unload library explicitely |
240 # Unload library explicitely |
241 if getattr(self,"_PLClibraryHandle",None) is not None: |
241 if getattr(self,"_PLClibraryHandle",None) is not None: |
242 dlclose(self._PLClibraryHandle) |
242 dlclose(self._PLClibraryHandle) |
243 self._PLClibraryHandle = None |
243 self._PLClibraryHandle = None |
244 |
244 |
245 self.PLClibraryLock.release() |
245 self.PLClibraryLock.release() |
246 return False |
246 return False |
247 |
247 |
248 def PythonRuntimeCall(self, methodname): |
248 def PythonRuntimeCall(self, methodname): |
249 """ |
249 """ |
250 Calls init, start, stop or cleanup method provided by |
250 Calls init, start, stop or cleanup method provided by |
251 runtime python files, loaded when new PLC uploaded |
251 runtime python files, loaded when new PLC uploaded |
252 """ |
252 """ |
253 for method in self.python_runtime_vars.get("_runtime_%s"%methodname, []): |
253 for method in self.python_runtime_vars.get("_runtime_%s"%methodname, []): |
254 res,exp = self.evaluator(method) |
254 res,exp = self.evaluator(method) |
255 if exp is not None: |
255 if exp is not None: |
256 self.LogMessage(0,'\n'.join(traceback.format_exception(*exp))) |
256 self.LogMessage(0,'\n'.join(traceback.format_exception(*exp))) |
257 |
257 |
258 def PythonRuntimeInit(self): |
258 def PythonRuntimeInit(self): |
259 MethodNames = ["init", "start", "stop", "cleanup"] |
259 MethodNames = ["init", "start", "stop", "cleanup"] |
260 self.python_runtime_vars = globals().copy() |
260 self.python_runtime_vars = globals().copy() |
284 try: |
284 try: |
285 for filename in os.listdir(self.workingdir): |
285 for filename in os.listdir(self.workingdir): |
286 name, ext = os.path.splitext(filename) |
286 name, ext = os.path.splitext(filename) |
287 if name.upper().startswith("RUNTIME") and ext.upper() == ".PY": |
287 if name.upper().startswith("RUNTIME") and ext.upper() == ".PY": |
288 execfile(os.path.join(self.workingdir, filename), self.python_runtime_vars) |
288 execfile(os.path.join(self.workingdir, filename), self.python_runtime_vars) |
289 for methodname in MethodNames: |
289 for methodname in MethodNames: |
290 method = self.python_runtime_vars.get("_%s_%s" % (name, methodname), None) |
290 method = self.python_runtime_vars.get("_%s_%s" % (name, methodname), None) |
291 if method is not None: |
291 if method is not None: |
292 self.python_runtime_vars["_runtime_%s"%methodname].append(method) |
292 self.python_runtime_vars["_runtime_%s"%methodname].append(method) |
293 except: |
293 except: |
294 self.LogMessage(0,traceback.format_exc()) |
294 self.LogMessage(0,traceback.format_exc()) |
295 raise |
295 raise |
296 |
296 |
297 self.PythonRuntimeCall("init") |
297 self.PythonRuntimeCall("init") |
298 |
298 |
299 if self.website is not None: |
299 if self.website is not None: |
300 self.website.PLCStarted() |
300 self.website.PLCStarted() |
301 |
301 |
317 res,cmd,blkid = "None","None",ctypes.c_void_p() |
317 res,cmd,blkid = "None","None",ctypes.c_void_p() |
318 compile_cache={} |
318 compile_cache={} |
319 while True: |
319 while True: |
320 # print "_PythonIterator(", res, ")", |
320 # print "_PythonIterator(", res, ")", |
321 cmd = self._PythonIterator(res,blkid) |
321 cmd = self._PythonIterator(res,blkid) |
322 FBID = blkid.value |
322 FBID = blkid.value |
323 # print " -> ", cmd, blkid |
323 # print " -> ", cmd, blkid |
324 if cmd is None: |
324 if cmd is None: |
325 break |
325 break |
326 try : |
326 try : |
327 self.python_runtime_vars["FBID"]=FBID |
327 self.python_runtime_vars["FBID"]=FBID |
328 ccmd,AST =compile_cache.get(FBID, (None,None)) |
328 ccmd,AST =compile_cache.get(FBID, (None,None)) |
329 if ccmd is None or ccmd!=cmd: |
329 if ccmd is None or ccmd!=cmd: |
330 AST = compile(cmd, '<plc>', 'eval') |
330 AST = compile(cmd, '<plc>', 'eval') |
331 compile_cache[FBID]=(cmd,AST) |
331 compile_cache[FBID]=(cmd,AST) |
332 result,exp = self.evaluator(eval,AST,self.python_runtime_vars) |
332 result,exp = self.evaluator(eval,AST,self.python_runtime_vars) |
333 if exp is not None: |
333 if exp is not None: |
334 res = "#EXCEPTION : "+str(exp[1]) |
334 res = "#EXCEPTION : "+str(exp[1]) |
335 self.LogMessage(1,('PyEval@0x%x(Code="%s") Exception "%s"')%(FBID,cmd, |
335 self.LogMessage(1,('PyEval@0x%x(Code="%s") Exception "%s"')%(FBID,cmd, |
336 '\n'.join(traceback.format_exception(*exp)))) |
336 '\n'.join(traceback.format_exception(*exp)))) |
337 else: |
337 else: |
338 res=str(result) |
338 res=str(result) |
341 res = "#EXCEPTION : "+str(e) |
341 res = "#EXCEPTION : "+str(e) |
342 self.LogMessage(1,('PyEval@0x%x(Code="%s") Exception "%s"')%(FBID,cmd,str(e))) |
342 self.LogMessage(1,('PyEval@0x%x(Code="%s") Exception "%s"')%(FBID,cmd,str(e))) |
343 self.PLCStatus = "Stopped" |
343 self.PLCStatus = "Stopped" |
344 self.StatusChange() |
344 self.StatusChange() |
345 self.PythonRuntimeCall("stop") |
345 self.PythonRuntimeCall("stop") |
346 |
346 |
347 def StartPLC(self): |
347 def StartPLC(self): |
348 if self.CurrentPLCFilename is not None and self.PLCStatus == "Stopped": |
348 if self.CurrentPLCFilename is not None and self.PLCStatus == "Stopped": |
349 c_argv = ctypes.c_char_p * len(self.argv) |
349 c_argv = ctypes.c_char_p * len(self.argv) |
350 error = None |
350 error = None |
351 res = self._startPLC(len(self.argv),c_argv(*self.argv)) |
351 res = self._startPLC(len(self.argv),c_argv(*self.argv)) |
380 Timer(0.1,self._Reload).start() |
380 Timer(0.1,self._Reload).start() |
381 return True |
381 return True |
382 |
382 |
383 def GetPLCstatus(self): |
383 def GetPLCstatus(self): |
384 return self.PLCStatus, map(self.GetLogCount,xrange(LogLevelsCount)) |
384 return self.PLCStatus, map(self.GetLogCount,xrange(LogLevelsCount)) |
385 |
385 |
386 def NewPLC(self, md5sum, data, extrafiles): |
386 def NewPLC(self, md5sum, data, extrafiles): |
387 if self.PLCStatus in ["Stopped", "Empty", "Broken"]: |
387 if self.PLCStatus in ["Stopped", "Empty", "Broken"]: |
388 NewFileName = md5sum + lib_ext |
388 NewFileName = md5sum + lib_ext |
389 extra_files_log = os.path.join(self.workingdir,"extra_files.txt") |
389 extra_files_log = os.path.join(self.workingdir,"extra_files.txt") |
390 |
390 |
401 os.remove(os.path.join(self.workingdir, filename.strip())) |
401 os.remove(os.path.join(self.workingdir, filename.strip())) |
402 except: |
402 except: |
403 pass |
403 pass |
404 except: |
404 except: |
405 pass |
405 pass |
406 |
406 |
407 try: |
407 try: |
408 # Create new PLC file |
408 # Create new PLC file |
409 open(os.path.join(self.workingdir,NewFileName), |
409 open(os.path.join(self.workingdir,NewFileName), |
410 'wb').write(data) |
410 'wb').write(data) |
411 |
411 |
412 # Store new PLC filename based on md5 key |
412 # Store new PLC filename based on md5 key |
413 open(self._GetMD5FileName(), "w").write(md5sum) |
413 open(self._GetMD5FileName(), "w").write(md5sum) |
414 |
414 |
415 # Then write the files |
415 # Then write the files |
416 log = file(extra_files_log, "w") |
416 log = file(extra_files_log, "w") |
417 for fname,fdata in extrafiles: |
417 for fname,fdata in extrafiles: |
418 fpath = os.path.join(self.workingdir,fname) |
418 fpath = os.path.join(self.workingdir,fname) |
419 open(fpath, "wb").write(fdata) |
419 open(fpath, "wb").write(fdata) |
460 for idx,iectype,force in idxs: |
458 for idx,iectype,force in idxs: |
461 if force !=None: |
459 if force !=None: |
462 c_type,unpack_func, pack_func = \ |
460 c_type,unpack_func, pack_func = \ |
463 TypeTranslator.get(iectype, |
461 TypeTranslator.get(iectype, |
464 (None,None,None)) |
462 (None,None,None)) |
465 force = ctypes.byref(pack_func(c_type,force)) |
463 force = ctypes.byref(pack_func(c_type,force)) |
466 self._RegisterDebugVariable(idx, force) |
464 self._RegisterDebugVariable(idx, force) |
467 self._resumeDebug() |
465 self._resumeDebug() |
468 else: |
466 else: |
469 self._suspendDebug(True) |
467 self._suspendDebug(True) |
470 self._Idxs = [] |
468 self._Idxs = [] |
475 """ |
473 """ |
476 if self.PLCStatus == "Started": |
474 if self.PLCStatus == "Started": |
477 tick = ctypes.c_uint32() |
475 tick = ctypes.c_uint32() |
478 size = ctypes.c_uint32() |
476 size = ctypes.c_uint32() |
479 buff = ctypes.c_void_p() |
477 buff = ctypes.c_void_p() |
480 TraceVariables = None |
478 TraceBuffer = None |
481 if self.PLClibraryLock.acquire(False): |
479 if self.PLClibraryLock.acquire(False): |
482 if self._GetDebugData(ctypes.byref(tick), |
480 if self._GetDebugData(ctypes.byref(tick), |
483 ctypes.byref(size), |
481 ctypes.byref(size), |
484 ctypes.byref(buff)) == 0: |
482 ctypes.byref(buff)) == 0: |
485 if size.value: |
483 if size.value: |
486 TraceVariables = UnpackDebugBuffer(buff, size.value, self._Idxs) |
484 TraceBuffer = ctypes.string_at(buff.value, size.value) |
487 self._FreeDebugData() |
485 self._FreeDebugData() |
488 self.PLClibraryLock.release() |
486 self.PLClibraryLock.release() |
489 if TraceVariables is not None: |
487 if TraceBuffer is not None: |
490 return self.PLCStatus, tick.value, TraceVariables |
488 return self.PLCStatus, tick.value, TraceBuffer |
491 return self.PLCStatus, None, [] |
489 return self.PLCStatus, None, None |
492 |
490 |
493 def RemoteExec(self, script, **kwargs): |
491 def RemoteExec(self, script, **kwargs): |
494 try: |
492 try: |
495 exec script in kwargs |
493 exec script in kwargs |
496 except: |
494 except: |
497 e_type, e_value, e_traceback = sys.exc_info() |
495 e_type, e_value, e_traceback = sys.exc_info() |
498 line_no = traceback.tb_lineno(get_last_traceback(e_traceback)) |
496 line_no = traceback.tb_lineno(get_last_traceback(e_traceback)) |
499 return (-1, "RemoteExec script failed!\n\nLine %d: %s\n\t%s" % |
497 return (-1, "RemoteExec script failed!\n\nLine %d: %s\n\t%s" % |
500 (line_no, e_value, script.splitlines()[line_no - 1])) |
498 (line_no, e_value, script.splitlines()[line_no - 1])) |
501 return (0, kwargs.get("returnVal", None)) |
499 return (0, kwargs.get("returnVal", None)) |
502 |
500 |
503 |
501 |