runtime/PLCObject.py
changeset 2463 8742337a9fe3
parent 2459 21164625b393
child 2485 ef327451d067
equal deleted inserted replaced
2462:ed6b0e905fcb 2463:8742337a9fe3
    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
    88         self._loading_error = None
    97         self._loading_error = None
    89         self.python_runtime_vars = None
    98         self.python_runtime_vars = None
    90         self.TraceThread = None
    99         self.TraceThread = None
    91         self.TraceLock = Lock()
   100         self.TraceLock = Lock()
    92         self.Traces = []
   101         self.Traces = []
       
   102 
       
   103         self._init_blobs()
    93 
   104 
    94     # First task of worker -> no @RunInMain
   105     # First task of worker -> no @RunInMain
    95     def AutoLoad(self, autostart):
   106     def AutoLoad(self, autostart):
    96         # Get the last transfered PLC
   107         # Get the last transfered PLC
    97         try:
   108         try:
   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