Locking Scheme for Scripting¶
Overview¶
When the user acquires and uses instruments in procedures' functions that are called from a script, there is a subroutine that disabled access to those instruments and ‘locks’ the specified instruments for the duration of the script. When the script is finished running, the instruments will appear in the drop down menu again.
Usage¶
The workflow for locking an instrument occurs as follows:
- Every time the user runs a script a new instance of the
ScriptControlleris instantiated to undertake the task. Each script contoller is associated with a uniqueID. - The Script controller will serially go through the entries of the measurement script, executing the specified
procedurefunction. - The graphical user interface is updated, blocking the locked instruments from the
Stages to Executeoption menu. - When an instrument request is made within a
procedurefunction through theGlobal_MeasureHandler, theGlobal_MeasureHandlertraverses the stack of the request, looking for theIDof the ScriptController that made this call. - When the
IDis determined, theGlobal_MeasureHandlercan check if that instrument is available in the__lockedcollection for that specificID. - When the
Global_MeasureHandlertraverses the stack, it is looking for theIDof the instance ofScriptHandler. It is not necessarily looking for a specificID, but looking for theIDof the object that has invoked this exact instance of theget_instrumentcall. Due to the design of the locking scheme, the only functions that could have invokedget_instrumentare stored in the listfunctions. If theIDof the local namespace forself, i.e. the object that invoked this function, appears in out__lockeddictionary’s keys, then it is in fact theIDof an instance of aScriptControllerthat locked the instrument during the initial setup.
- When the
- When an instrument request is made within a
functions = ['_structureProcedure','__executeCommand','_procedure']
for entry in inspect.stack(context=0):
if entry[3] in functions:
id_ = id(entry[0].f_locals['self'])
if (id_ in self.__locked.keys()):
- When the
procedurefunction is done running, theGlobal_MeasureHandlerwill release all of the locked instruments associated with thatID(
def release_current_user_instruments(self):
'''
Releases all current user instruments
Should be called at the end of a test entity and NOT normally
inside Measure functions
'''
owner_id = self._get_owner()
with self.__access_lock:
owned = self.__locked.get(owner_id)
chain = self.__chainSets.get(owner_id)
if owned is not None:
self.__locked[owner_id] = []
if chain is not None:
self.__chainSets[owner_id] = ChainList()
Snapshot¶
- Code that executes the locking scheme from the
Global_MeasureHandler: - Note:
_look_for_objis just a convenience function that will return the first occurence of an element in a list, for which a lambda function, provided as an argument, returns true.
- Note:
def get_instrument(self, specifiedDevice, additional=False):
'''
Finds and returns an unactive instrument corresponding to the one specified
Returns None if such instrument was found/available.
'''
owner_id = self._get_owner() # This is where we get the ID
# serialize access to global ownership dictionary
with self.__access_lock:
if not additional:
# Try the owned instruments first
owned_list = self.__locked.get(owner_id)
sdebug('OwnedList<{}>: {}'.format(owner_id, owned_list))
if owned_list != None:
found = _look_for_obj(owned_list, lambda x: x.whoAmI() == specifiedDevice)
if found != None:
return found
# Then take a look for available instruments
used = [inst for sub_l in self.__locked.values() for inst in sub_l]
sdebug('used instruments: {}'.format(used))
def isUnused(instrument):
return instrument not in used and instrument.whoAmI() == specifiedDevice
found = _look_for_obj(self.Stages.values(), isUnused)
if found != None:
self._lock_instrument(found, owner_id)
# self._connect_to_chain(found, owner_id, fiber_id)
return found