32 import _ctypes # pylint: disable=wrong-import-order |
32 import _ctypes # pylint: disable=wrong-import-order |
33 from past.builtins import execfile |
33 from past.builtins import execfile |
34 import Pyro.core as pyro |
34 import Pyro.core as pyro |
35 import six |
35 import six |
36 from six.moves import _thread, xrange |
36 from six.moves import _thread, xrange |
|
37 import md5 |
|
38 from tempfile import mkstemp |
|
39 import shutil |
37 |
40 |
38 from runtime.typemapping import TypeTranslator |
41 from runtime.typemapping import TypeTranslator |
39 from runtime.loglevels import LogLevelsDefault, LogLevelsCount |
42 from runtime.loglevels import LogLevelsDefault, LogLevelsCount |
40 from runtime.Stunnel import getPSKID |
43 from runtime.Stunnel import getPSKID |
41 from runtime import PlcStatus |
44 from runtime import PlcStatus |
42 from runtime import MainWorker |
45 from runtime import MainWorker |
|
46 |
|
47 empty_md5_digest = md5.new().digest() |
43 |
48 |
44 if os.name in ("nt", "ce"): |
49 if os.name in ("nt", "ce"): |
45 dlopen = _ctypes.LoadLibrary |
50 dlopen = _ctypes.LoadLibrary |
46 dlclose = _ctypes.FreeLibrary |
51 dlclose = _ctypes.FreeLibrary |
47 elif os.name == "posix": |
52 elif os.name == "posix": |
72 return func_wrapper |
77 return func_wrapper |
73 |
78 |
74 |
79 |
75 class PLCObject(object): |
80 class PLCObject(object): |
76 def __init__(self, WorkingDir, argv, statuschange, evaluator, pyruntimevars): |
81 def __init__(self, WorkingDir, argv, statuschange, evaluator, pyruntimevars): |
77 self.workingdir = WorkingDir |
82 self.workingdir = WorkingDir # must exits already |
|
83 self.tmpdir = os.path.join(WorkingDir, 'tmp') |
|
84 if os.path.exists(self.tmpdir): |
|
85 shutil.rmtree(self.tmpdir) |
|
86 os.mkdir(self.tmpdir) |
78 # FIXME : is argv of any use nowadays ? |
87 # FIXME : is argv of any use nowadays ? |
79 self.argv = [WorkingDir] + argv # force argv[0] to be "path" to exec... |
88 self.argv = [WorkingDir] + argv # force argv[0] to be "path" to exec... |
80 self.statuschange = statuschange |
89 self.statuschange = statuschange |
81 self.evaluator = evaluator |
90 self.evaluator = evaluator |
82 self.pyruntimevars = pyruntimevars |
91 self.pyruntimevars = pyruntimevars |
445 |
456 |
446 @RunInMain |
457 @RunInMain |
447 def GetPLCID(self): |
458 def GetPLCID(self): |
448 return getPSKID() |
459 return getPSKID() |
449 |
460 |
450 @RunInMain |
461 def _init_blobs(self): |
451 def NewPLC(self, md5sum, data, extrafiles): |
462 self.blobs = {} |
|
463 if os.path.exists(self.tmpdir): |
|
464 shutil.rmtree(self.tmpdir) |
|
465 os.mkdir(self.tmpdir) |
|
466 |
|
467 @RunInMain |
|
468 def AppendChunkToBlob(self, data, blobID): |
|
469 blob = ((mkstemp(dir=self.tmpdir) if data else None)\ |
|
470 + (md5.new(),)) \ |
|
471 if blobID == empty_md5_digest \ |
|
472 else self.blobs.pop(blobID, None) |
|
473 |
|
474 if blob is None: |
|
475 return None |
|
476 |
|
477 fobj, path, md5sum = blob |
|
478 md5sum.update(data) |
|
479 newBlobID = md5sum.digest() |
|
480 if data: |
|
481 os.write(fobj,data) |
|
482 self.blobs[newBlobID] = blob |
|
483 return newBlobID |
|
484 |
|
485 @RunInMain |
|
486 def PurgeBlobs(self): |
|
487 for fobj, path, md5sum in self.blobs: |
|
488 os.close(fobj) |
|
489 self._init_blobs() |
|
490 |
|
491 def _BlobAsFile(self, blobID, newpath): |
|
492 blob = self.blobs.pop(blobID, None) |
|
493 |
|
494 if blob is None: |
|
495 if blobID == md5.new().digest(): |
|
496 # create empty file |
|
497 open(newpath,'r').close() |
|
498 return |
|
499 raise Exception(_("Missing data to create file: {}").format(newpath)) |
|
500 |
|
501 fobj, path, md5sum = blob |
|
502 os.close(fobj) |
|
503 shutil.move(path, newpath) |
|
504 |
|
505 @RunInMain |
|
506 def NewPLC(self, md5sum, plc_object, extrafiles): |
452 if self.PLCStatus in [PlcStatus.Stopped, PlcStatus.Empty, PlcStatus.Broken]: |
507 if self.PLCStatus in [PlcStatus.Stopped, PlcStatus.Empty, PlcStatus.Broken]: |
453 NewFileName = md5sum + lib_ext |
508 NewFileName = md5sum + lib_ext |
454 extra_files_log = os.path.join(self.workingdir, "extra_files.txt") |
509 extra_files_log = os.path.join(self.workingdir, "extra_files.txt") |
455 |
510 |
456 old_PLC_filename = os.path.join(self.workingdir, self.CurrentPLCFilename) \ |
511 old_PLC_filename = os.path.join(self.workingdir, self.CurrentPLCFilename) \ |
457 if self.CurrentPLCFilename is not None \ |
512 if self.CurrentPLCFilename is not None \ |
458 else None |
513 else None |
459 new_PLC_filename = os.path.join(self.workingdir, NewFileName) |
514 new_PLC_filename = os.path.join(self.workingdir, NewFileName) |
460 |
515 |
461 # Some platform (i.e. Xenomai) don't like reloading same .so file |
516 self.UnLoadPLC() |
462 replace_PLC_shared_object = new_PLC_filename != old_PLC_filename |
|
463 |
|
464 if replace_PLC_shared_object: |
|
465 self.UnLoadPLC() |
|
466 |
517 |
467 self.LogMessage("NewPLC (%s)" % md5sum) |
518 self.LogMessage("NewPLC (%s)" % md5sum) |
468 self.PLCStatus = PlcStatus.Empty |
519 self.PLCStatus = PlcStatus.Empty |
469 |
520 |
470 try: |
521 try: |
471 if replace_PLC_shared_object: |
522 os.remove(old_PLC_filename) |
472 os.remove(old_PLC_filename) |
|
473 for filename in open(extra_files_log, "rt").readlines() + [extra_files_log]: |
523 for filename in open(extra_files_log, "rt").readlines() + [extra_files_log]: |
474 try: |
524 try: |
475 os.remove(os.path.join(self.workingdir, filename.strip())) |
525 os.remove(os.path.join(self.workingdir, filename.strip())) |
476 except Exception: |
526 except Exception: |
477 pass |
527 pass |
478 except Exception: |
528 except Exception: |
479 pass |
529 pass |
480 |
530 |
481 try: |
531 try: |
482 # Create new PLC file |
532 # Create new PLC file |
483 if replace_PLC_shared_object: |
533 self._BlobAsFile(plc_object, new_PLC_filename) |
484 open(new_PLC_filename, 'wb').write(data) |
|
485 |
534 |
486 # Store new PLC filename based on md5 key |
535 # Store new PLC filename based on md5 key |
487 open(self._GetMD5FileName(), "w").write(md5sum) |
536 open(self._GetMD5FileName(), "w").write(md5sum) |
488 |
537 |
489 # Then write the files |
538 # Then write the files |
490 log = open(extra_files_log, "w") |
539 log = open(extra_files_log, "w") |
491 for fname, fdata in extrafiles: |
540 for fname, blobID in extrafiles: |
492 fpath = os.path.join(self.workingdir, fname) |
541 fpath = os.path.join(self.workingdir, fname) |
493 open(fpath, "wb").write(fdata) |
542 self._BlobAsFile(blobID, fpath) |
494 log.write(fname+'\n') |
543 log.write(fname+'\n') |
495 |
544 |
496 # Store new PLC filename |
545 # Store new PLC filename |
497 self.CurrentPLCFilename = NewFileName |
546 self.CurrentPLCFilename = NewFileName |
498 except Exception: |
547 except Exception: |
499 self.PLCStatus = PlcStatus.Broken |
548 self.PLCStatus = PlcStatus.Broken |
500 self.StatusChange() |
549 self.StatusChange() |
501 PLCprint(traceback.format_exc()) |
550 PLCprint(traceback.format_exc()) |
502 return False |
551 return False |
503 |
552 |
504 if not replace_PLC_shared_object: |
553 if self.LoadPLC(): |
505 self.PLCStatus = PlcStatus.Stopped |
|
506 elif self.LoadPLC(): |
|
507 self.PLCStatus = PlcStatus.Stopped |
554 self.PLCStatus = PlcStatus.Stopped |
508 else: |
555 else: |
509 self.PLCStatus = PlcStatus.Broken |
556 self.PLCStatus = PlcStatus.Broken |
510 self.StatusChange() |
557 self.StatusChange() |
511 |
558 |