def startOccupancyCheck( self, msg ) :
		"""
		Starts a new thread with a short 100 event run to check the occupancies for the current settings.
		"""
		# Make sure I'm not currently taking data
		if self.dataTakingThread!=None : raise Exception("Currently taking data")

		self.dataTakingFractionComplete=0
		self.dataTakingStatusString="Initiating occupancy check"

		self.analysisControl.reset()
		self.dataTakingThread=OccupancyCheck( GlibControlService._DataTakingStatusReceiver(self), self.program, self.analysisControl )
		self.dataTakingThread.start()
	def startTrimCalibration( self, msg ) :
		"""
		Starts a new thread that tries to calibrate the channel trims.
		"""
		# Make sure I'm not currently taking data
		if self.dataTakingThread!=None : raise Exception("Currently taking data")

		self.dataTakingFractionComplete=0
		self.dataTakingStatusString="Initiating trim calibration"

		self.analysisControl.reset()
		self.dataTakingThread=CalibrateChannelTrims( GlibControlService._DataTakingStatusReceiver(self), self.program, self.analysisControl, range(100,150), msg['midPointTarget'], maxLoops=msg['maxLoops'] )
		self.dataTakingThread.start()
	def startSCurveRun( self, msg ) :
		"""
		Starts a new thread taking s-curve data
		"""
		# Make sure I'm not currently taking data
		if self.dataTakingThread!=None : raise Exception("Currently taking data")

		self.dataTakingFractionComplete=0
		self.dataTakingStatusString="Initiating s-curve run"

		thresholds=msg
		self.analysisControl.reset()
		self.dataTakingThread=SCurveRun( GlibControlService._DataTakingStatusReceiver(self), self.program, self.analysisControl, thresholds )
		self.dataTakingThread.start()
class GlibControlService:
	"""
	Class that invokes the Glib control methods in response to JSON RPC calls.
	
	There should be no logic pertaining to the Glib here - this should solely
	pass on any commands that you want externally visible to the correct method
	in the python control library.
	
	@author Mark Grimes ([email protected])
	@date 11/Jan/2014
	"""
	class _DataTakingStatusReceiver(object) :
		"""
		Simple class to receive the status notifications from the data taking
		thread. Sets parameters in the GlibControlService provided in the constructor,
		so intended to be created by GlibControlService passing "self".
		"""
		def __init__( self, parentControlService ) :
			self.parentControlService=parentControlService
		def currentStatus( self, fractionComplete, statusString ) :
			self.parentControlService.dataTakingFractionComplete=fractionComplete
			self.parentControlService.dataTakingStatusString=statusString
		def finished( self ) :
			self.parentControlService.dataTakingThread=None
			self.parentControlService.dataTakingFractionComplete=1
			self.parentControlService.dataTakingStatusString="Not taking data"

	def __init__(self):
		self.boardAddress = "192.168.0.175"
		self.program = SimpleGlibProgram( os.path.join( INSTALLATION_PATH, "runcontrol", "GlibSuper.xml" ) )
		
		# Need to specify the environment variables required to run XDAQ. To get them use the system
		# settings in runcontrol/environmentVariables.py. I can't get them from the current environment
		# because they might not be set for the current user (apache won't have any of them set).
		environmentVariables=getEnvironmentVariables()
		self.program.setEnvironmentVariables( environmentVariables )
		self.program.initialiseCBCs() # Do the necessary initialisation to get information about the CBCs

		# Need to provide the full executable path because this might not be in the path for different users
		# When the analyser is spawned it takes on the environment of this script, so I'll modify that directly
		self.analysisControl = AnalyserControl( "127.0.0.1", "50000", True, environmentVariables )
		self.analysisControl.reset()
		
		# Directory where the user can save their I2C files
		self.userI2cDirectory=INSTALLATION_PATH+"/runcontrol/user_i2c/"
		
		# The members below are for handling the thread that takes data
		self.dataTakingThread=None
		self.dataTakingFractionComplete=1
		self.dataTakingStatusString="Not taking data"
		
	def getStates(self, msg):
		"""
		Returns the states of all the active XDAQ applications as an array. Each element is
		itself a two element array of the application name and the state.
		"""
		try:
			results = []
			for context in self.program.contexts :
				for application in context.applications :
					results.append( [application.className,application.getState()] )
			return results
		except Exception as error:
			return "Exception: "+str(error)

	def connectedCBCNames(self, msg):
		"""
		Returns the names of the connected CBCs.
		"""
		return self.program.supervisor.connectedCBCNames()
	
	def I2CRegisterValues(self, msg):
		return self.program.supervisor.I2CRegisterValues(msg)
			
	def setI2CRegisterValues(self, msg):
		# Make sure I'm not currently taking data
		if self.dataTakingThread!=None : raise Exception("Currently taking data")

		chipNames = msg.keys()
		registerNameValueTuple = msg[chipNames[0]]


		return self.program.supervisor.setI2c( registerNameValueTuple, chipNames )
	
	def saveI2cRegisterValues(self, msg):
		self.program.supervisor.saveI2c( self.userI2cDirectory+msg )
		return msg
	
	def loadI2cRegisterValues(self, msg):
		self.program.supervisor.loadI2c( self.userI2cDirectory+msg )
		return msg
	
	def startProcesses(self, msg):
		"""
		Starts all of the XDAQ processes
		"""
		# Make sure I'm not currently taking data
		if self.dataTakingThread!=None : raise Exception("Currently taking data")
		
		try:
			self.program.startAllProcesses()
			return None
		except Exception as error:
			return "Exception: "+str(error)

	def killProcesses(self, msg):
		"""
		Kills all of the XDAQ processes
		"""
		# Make sure I'm not currently taking data
		if self.dataTakingThread!=None : raise Exception("Currently taking data")

		try:
			self.program.killAllProcesses()
			return None
		except Exception as error:
			return "Exception: "+str(error)
	
	def boardIsReachable( self, msg ):
		"""
		Pings the board to see if it is available
		"""
		# return true or false depending on whether the board can be pinged
		return testStandTools.ping( self.boardAddress )

	def stopTakingData( self, msg ) :
		"""
		Tell the data taking thread to stop whatever it's doing.
		"""
		if self.dataTakingThread!=None :
			self.dataTakingThread.quit=True
	
	def startSCurveRun( self, msg ) :
		"""
		Starts a new thread taking s-curve data
		"""
		# Make sure I'm not currently taking data
		if self.dataTakingThread!=None : raise Exception("Currently taking data")

		self.dataTakingFractionComplete=0
		self.dataTakingStatusString="Initiating s-curve run"

		thresholds=msg
		self.analysisControl.reset()
		self.dataTakingThread=SCurveRun( GlibControlService._DataTakingStatusReceiver(self), self.program, self.analysisControl, thresholds )
		self.dataTakingThread.start()

	def startOccupancyCheck( self, msg ) :
		"""
		Starts a new thread with a short 100 event run to check the occupancies for the current settings.
		"""
		# Make sure I'm not currently taking data
		if self.dataTakingThread!=None : raise Exception("Currently taking data")

		self.dataTakingFractionComplete=0
		self.dataTakingStatusString="Initiating occupancy check"

		self.analysisControl.reset()
		self.dataTakingThread=OccupancyCheck( GlibControlService._DataTakingStatusReceiver(self), self.program, self.analysisControl )
		self.dataTakingThread.start()

	def startTrimCalibration( self, msg ) :
		"""
		Starts a new thread that tries to calibrate the channel trims.
		"""
		# Make sure I'm not currently taking data
		if self.dataTakingThread!=None : raise Exception("Currently taking data")

		self.dataTakingFractionComplete=0
		self.dataTakingStatusString="Initiating trim calibration"

		self.analysisControl.reset()
		self.dataTakingThread=CalibrateChannelTrims( GlibControlService._DataTakingStatusReceiver(self), self.program, self.analysisControl, range(100,150), msg['midPointTarget'], maxLoops=msg['maxLoops'] )
		self.dataTakingThread.start()

	def getDataTakingStatus( self, msg ) :
		return {"fractionComplete":self.dataTakingFractionComplete,"statusString":self.dataTakingStatusString}
	
	def getOccupancies( self, msg ) :
		returnValue={}
		#self.analysisControl.analyseFile( "/tmp/cbc2SCurveRun_OutputFile.dat" )
		occupancies=self.analysisControl.occupancies()
		# The C++ code doesn't know which CBCs are connected. Dummy data is in the output files
		# for unconnected CBCs. I'll check which CBCs are connected and only return the data for
		# those.
		for cbcName in  self.program.supervisor.connectedCBCNames() :
			try :
				returnValue[cbcName]=occupancies[cbcName]
			except KeyError :
				# No data for that CBC
				returnValue[cbcName]=None

		return returnValue

	def createHistogram( self, msg ) :
		self.analysisControl.saveHistogramPicture( INSTALLATION_PATH+"/gui/output/"+msg['outputFilename'], msg['cbcChannelRange'] )

	def saveHistograms( self, msg ) :
		self.analysisControl.saveHistograms( msg )