import time
from Service import Service

class AdaptiveLoadController(Service):
	"""The AdaptiveLoadController is a separate thread which periodically checks the system load and adapts the sleep time for AdaptiveSleeper-inherited threads.  Do not instantiate directly: use the get_adaptive_load_controller singleton factory."""
	throttle_ms = 0.0
	error_margin = None
	target_load = None
	max_throttling = None
	sleeper_granularity = 0.5 # this is because you might actually start a sleeper without having started the controller yet
	check_frequency = None

	def __init__(self,target_load=1,error_margin=0.1,max_throttling=5,sleeper_granularity=0.5,check_frequency=1.0):
		""" Initializes the load controller.  target_load is the maximum load before starting to throttle, error_margin is the allowed +- margin on the load, max_throtttling is the maximum number of seconds Sleeper threads should sleep, sleeper_granularity is the granularity with which the Sleepers check if it's time to continue, and check_frequency is the time between recalculations of the throttling value"""
		Service.__init__(self,public_interface=AdaptiveLoadControllerInterface("loadController",self))
		self.set_component_name("AdaptiveLoadController")
		self.error_margin = error_margin
		self.target_load = target_load
		self.max_throttling = max_throttling
		AdaptiveLoadController.sleeper_granularity = sleeper_granularity
		self.check_frequency = check_frequency

	def set_target_load(self,target_load):
		assert type(target_load) is float
		if target_load <= 0.09 or target_load > 10:
			raise ValueError, "target_load must be between 0.1 and 10"
		self.target_load = target_load
	
	def run(self):
		while not self.killed():
			self.recalculate_throttling()
			time.sleep(self.check_frequency)
		AdaptiveLoadController.throttle_ms = 0.0

	def recalculate_throttling(self):

		old_throttle_ms = AdaptiveLoadController.throttle_ms
		def inrange(number,target,plusminus):
			return (number < target +plusminus and number > target - plusminus)

		import os
		load= os.getloadavg()[0]
		if inrange(load,self.target_load,self.error_margin):
			pass
		elif load > self.target_load:
			if AdaptiveLoadController.throttle_ms < self.max_throttling:
				n = AdaptiveLoadController.throttle_ms + (load - self.target_load)*10
				AdaptiveLoadController.throttle_ms = min(self.max_throttling,n)
		elif load < self.target_load:
			if AdaptiveLoadController.throttle_ms > 0.0:
				n = AdaptiveLoadController.throttle_ms - (self.target_load - load)*10
				AdaptiveLoadController.throttle_ms = max(0.0,n)
				
		if old_throttle_ms != AdaptiveLoadController.throttle_ms:
			self.log.debug("Target load %s, current load %s, throttle at %s seconds",self.target_load,load,AdaptiveLoadController.throttle_ms)
		

from PublicInterface import PublicInterface

class AdaptiveLoadControllerInterface(PublicInterface):

	def setTargetLoad(self,target_load):
		target_load = float(target_load)
		self.get_component().set_target_load(target_load)
		return True