Exemplo n.º 1
0
    def __init__(self, step=None, id=None, marshal=None):
        self._id = None

        self._machines = set()

        self._logging = False
        self._log_variables = {}
        self._time_zero = util.now()

        self.step = step

        self.interface = InterfaceSectionSet()
        self.interface["experiment"] = InterfaceSection()

        if marshal is None:
            self._marshal = Marshal(self)
        else:
            self._marshal = marshal
            self._marshal.push(self)

        self.state = State.READY

        if id is not None:
            self.id = id
Exemplo n.º 2
0
	def __init__ (self, step = None, id = None, marshal = None):
		self._id = None

		self._machines = set()

		self._logging = False
		self._log_variables = {}
		self._time_zero = util.now()

		self.step = step

		self.interface = InterfaceSectionSet()
		self.interface["experiment"] = InterfaceSection()

		if marshal is None:
			self._marshal = Marshal(self)
		else:
			self._marshal = marshal
			self._marshal.push(self)

		self.state = State.READY

		if id is not None:
			self.id = id
Exemplo n.º 3
0
class Experiment(object):

    title = "Untitled Experiment"
    default_log_output = "main"

    @property
    def id(self):
        return self._id

    @id.setter
    def id(self, value):
        if self._id is not None:
            raise ValueError("ID has already been set")

        if not re.match(r'^[a-zA-Z0-9\-\_]+$', value):
            raise ValueError(
                "ID can only contain alphanumeric characters, _ and -")

        self._id = value
        self._log("ID set", logging.DEBUG)

        self.id_set(self, value)

    @property
    def state(self):
        return self._state

    @state.setter
    def state(self, value):
        self._state = value
        self.marshal.event(Event.EXPERIMENT, {"state": value.value})

        try:
            # todo... collect in a list to write later.
            self._event_log.write(util.now(), "expt-state:%s" % value.value)
        except AttributeError:
            pass

    @property
    def marshal(self):
        return self._marshal

    def _log(self, msg, level=None):
        log.msg("Experiment [%s]: %s" % (self.id, msg), logLevel=level)

    id_set = util.Event()
    started = util.Event()
    finished = util.Event()
    cancelled = util.Event()
    paused = util.Event()
    resumed = util.Event()
    error = util.Event()

    #
    # event types:
    #	[e] experiment-change: { *delta: id, title, state }
    #	[i] interface-change: { item: x, *delta - disabled / enabled, text | delete-item: x }
    #	[s] step-change: { id: n, *delta - state, duration, child, expr ...  }
    #   [l] log { type: x, message: x } No - use the log handler
    #       (via experiment.log.message)
    #   [z] timezero { time: x }
    #

    def __init__(self, step=None, id=None, marshal=None):
        self._id = None

        self._machines = set()

        self._logging = False
        self._log_variables = {}
        self._time_zero = util.now()

        self.step = step

        self.interface = InterfaceSectionSet()
        self.interface["experiment"] = InterfaceSection()

        if marshal is None:
            self._marshal = Marshal(self)
        else:
            self._marshal = marshal
            self._marshal.push(self)

        self.state = State.READY

        if id is not None:
            self.id = id

    # Todo: extract from Steps? (Difficult!)
    def register_machine(self, machine):
        """
		Registers a machine with the experiment.

		Registered machines are started before the experiment is run,
		and their variables and controls are registered automatically
		with the experiment.
		"""

        self._machines.add(machine)
        self.interface[machine.alias] = machine.ui

    def run(self):
        """
		Start running the experiment.
		"""

        if self.state is not State.READY:
            raise Exception("Already Started")

        if self.step is None:
            raise Exception("No Step")

        self.state = State.RUNNING
        finished = defer.Deferred()

        @defer.inlineCallbacks
        def run_experiment():
            # wait for machines to be ready
            # todo: with some timeout
            self._log("Waiting for machines")
            try:
                result = yield defer.gatherResults(
                    [m.ready for m in self._machines])
            except:
                self._log("Error")
                raise  # deal with Busy / errback.

            # reset machines
            # todo: with some timeout
            self._log("Resetting machines")
            try:
                result = yield defer.gatherResults(
                    [defer.maybeDeferred(m.reset) for m in self._machines])
            except:
                self._log("Error")
                raise  # deal with errback.

            # start logging
            # add event listeners to step
            self._log("Starting logging")
            self.set_log_output(self.default_log_output)

            self.interface.event += self._interface_event  #(log, passthrough to marshal)
            self.step.event += self._step_event  #(log, passthrough to marshal)
            self.step.log += self._step_log  #(log, passthrough to marshal)

            # run step
            self._log("Running experiment sequence")
            try:
                self.started()
                yield self.step.run()
                self.state = State.COMPLETE

            except Exception as error:
                self._log(error)
                self.error(error)
                self.state = State.ERROR
                raise

            finally:
                # remove event listeners
                self.interface.event -= self._interface_event
                self.step.event -= self._step_event
                self.step.log -= self._step_log

                # pop experiment from marshal
                self._log("Waiting for marshal")
                yield self._marshal.popExperiment()

                # stop logging
                self.stop_logging()
                self.finished()

        d = run_experiment()
        d.addCallbacks(finished.callback, finished.errback)
        d.addErrback(log.err)

        # TODO:
        # log for events
        # log for log events
        # make a new dir for each invocation of run
        # store child.log in this dir
        # store control python file in this dir (runtime).
        # periodically flush all log files.

        # Finished will callback once the experiment is complete
        return finished

    def _interface_event(self, item, **data):
        data["item"] = item.name
        self._marshal.event(Event.INTERFACE, data)

    def _step_event(self, item, **data):
        data["step"] = item.id
        data["type"] = item.type

        if "child" in data and data["child"] is not None:
            data["child"] = data["child"].serialize()

        if "children" in data and data["children"] is not None:
            data["children"] = [c.serialize() for c in data["children"]]

        if "state" in data:
            data["state"] = data["state"].value

        # send to event log
        self._event_log.write(util.now(), "step:" + str(data))

        self._marshal.event(Event.STEP, data)

    def _step_log(self, message, level=None):
        data = {"level": level or "info", "message": message}

        # send to log
        self._msg_log.write(util.now(), message)

        self._marshal.event(Event.LOG, data)

    def pause(self):
        """
		Pause the experiment.

		This pauses the currently running step, and calls the pause method on
		all registered machines.
		"""

        if self.state is State.RUNNING:
            self.state = State.PAUSED
            self.paused(self)

            return defer.gatherResults(
                [defer.maybeDeferred(self.step.pause)] +
                [defer.maybeDeferred(m.pause) for m in self._machines])

    def resume(self):
        """
		Resume the experiment.

		Resumes after pause() has been called.
		"""

        if self.state is State.PAUSED:
            self.state = State.RUNNING
            self.resumed(self)

            return defer.gatherResults(
                [defer.maybeDeferred(self.step.resume)] +
                [defer.maybeDeferred(m.resume) for m in self._machines])

    def stop(self):
        """
		(Emergency) stop the experiment.

		Immediately halts all processing and exits with an error.
		"""

        if self.state in (State.RUNNING, State.PAUSED):
            self.cancelled(self)

            return defer.gatherResults(
                [defer.maybeDeferred(self.step.abort)] +
                [defer.maybeDeferred(m.pause) for m in self._machines])

    #
    # Logging
    #
    # TODO - replace logger with batch logger.
    #
    def log_variables(self, *variables):
        if self._logging is True:
            raise Error("Cannot set variables whilst actively logging")

        self._log_variables = {}

        for v in variables:
            if isinstance(v, data.Variable) and v.alias not in self._variables:
                self._log_variables[v.alias] = v

            elif isinstance(v, Component):
                self._log_variables.update(v.variables)

    def set_log_output(self, name):
        time_zero = util.now()
        name = LogFile.get_dir(name)

        if len(self._log_variables) is 0:
            for m in self._machines:
                self._log_variables.update(m.variables)

            self._log_variables.update(self.interface.properties)

        items = self._log_variables.iteritems()

        for key, var in items:
            try:
                var.setLogFile(LogFile(name, key, time_zero))
                var.truncate()
            except AttributeError:
                pass

        try:
            self._event_log.close()
            self._msg_log.close()
        except AttributeError:
            pass

        self._event_log = LogFile(name, "events", time_zero)
        self._msg_log = LogFile(name, "log", time_zero)

        self._logging = True
        self._time_zero = time_zero

        self._event_log.write(time_zero, "tz")
        self.marshal.event(Event.TIMEZERO, {"time": time_zero, "clear": True})

    def stop_logging(self):
        self._logging = False

        items = self._log_variables.iteritems()

        for key, var in items:
            try:
                var.stopLogging()
            except AttributeError:
                pass

        try:
            self._event_log.close()
            self._event_log = None
        except AttributeError:
            pass

        try:
            self._msg_log.close()
            self._msg_log = None
        except AttributeError:
            pass
Exemplo n.º 4
0
class Experiment (object):

	title = "Untitled Experiment"
	default_log_output = "main"

	@property
	def id (self):
		return self._id

	@id.setter
	def id (self, value):
		if self._id is not None:
			raise ValueError("ID has already been set")

		if not re.match(r'^[a-zA-Z0-9\-\_]+$', value):
			raise ValueError("ID can only contain alphanumeric characters, _ and -")

		self._id = value
		self._log("ID set", logging.DEBUG)

		self.id_set(self, value)

	@property
	def state (self):
		return self._state

	@state.setter
	def state (self, value):
		self._state = value
		self.marshal.event(Event.EXPERIMENT, { "state": value.value })

		try:
			# todo... collect in a list to write later.
			self._event_log.write(util.now(), "expt-state:%s" % value.value)
		except AttributeError:
			pass

	@property
	def marshal (self):
		return self._marshal

	def _log (self, msg, level = None):
		log.msg("Experiment [%s]: %s" % (self.id, msg),	logLevel = level)

	id_set = util.Event()
	started = util.Event()
	finished = util.Event()
	cancelled = util.Event()
	paused = util.Event()
	resumed = util.Event()
	error = util.Event()

	#
	# event types:
	#	[e] experiment-change: { *delta: id, title, state }
	#	[i] interface-change: { item: x, *delta - disabled / enabled, text | delete-item: x }
	#	[s] step-change: { id: n, *delta - state, duration, child, expr ...  }
	#   [l] log { type: x, message: x } No - use the log handler
	#       (via experiment.log.message)
	#   [z] timezero { time: x }
	#

	def __init__ (self, step = None, id = None, marshal = None):
		self._id = None

		self._machines = set()

		self._logging = False
		self._log_variables = {}
		self._time_zero = util.now()

		self.step = step

		self.interface = InterfaceSectionSet()
		self.interface["experiment"] = InterfaceSection()

		if marshal is None:
			self._marshal = Marshal(self)
		else:
			self._marshal = marshal
			self._marshal.push(self)

		self.state = State.READY

		if id is not None:
			self.id = id


	# Todo: extract from Steps? (Difficult!)
	def register_machine (self, machine):
		"""
		Registers a machine with the experiment.

		Registered machines are started before the experiment is run,
		and their variables and controls are registered automatically
		with the experiment.
		"""

		self._machines.add(machine)
		self.interface[machine.alias] = machine.ui

	def run (self):
		"""
		Start running the experiment.
		"""

		if self.state is not State.READY:
			raise Exception("Already Started")

		if self.step is None:
			raise Exception("No Step")

		self.state = State.RUNNING
		finished = defer.Deferred()

		@defer.inlineCallbacks
		def run_experiment ():
			# wait for machines to be ready
			# todo: with some timeout
			self._log("Waiting for machines")
			try:
				result = yield defer.gatherResults(
					[m.ready for m in self._machines]
				)
			except:
				self._log("Error")
				raise # deal with Busy / errback.

			# reset machines
			# todo: with some timeout
			self._log("Resetting machines")
			try:
				result = yield defer.gatherResults(
					[defer.maybeDeferred(m.reset) for m in self._machines]
				)
			except:
				self._log("Error")
				raise # deal with errback.

			# start logging
			# add event listeners to step
			self._log("Starting logging")
			self.set_log_output(self.default_log_output)

			self.interface.event += self._interface_event #(log, passthrough to marshal)
			self.step.event += self._step_event #(log, passthrough to marshal)
			self.step.log += self._step_log #(log, passthrough to marshal)

			# run step
			self._log("Running experiment sequence")
			try:
				self.started()
				yield self.step.run()
				self.state = State.COMPLETE

			except Exception as error:
				self._log(error)
				self.error(error)
				self.state = State.ERROR
				raise

			finally:
				# remove event listeners
				self.interface.event -= self._interface_event
				self.step.event -= self._step_event
				self.step.log -= self._step_log

				# pop experiment from marshal
				self._log("Waiting for marshal")
				yield self._marshal.popExperiment()

				# stop logging
				self.stop_logging()
				self.finished()

		d = run_experiment()
		d.addCallbacks(finished.callback, finished.errback)
		d.addErrback(log.err)

		# TODO:
		# log for events 
		# log for log events
		# make a new dir for each invocation of run
		# store child.log in this dir
		# store control python file in this dir (runtime).
		# periodically flush all log files.

		# Finished will callback once the experiment is complete
		return finished

	def _interface_event (self, item, **data):
		data["item"] = item.name
		self._marshal.event(Event.INTERFACE, data)

	def _step_event (self, item, **data):
		data["step"] = item.id
		data["type"] = item.type

		if "child" in data and data["child"] is not None:
			data["child"] = data["child"].serialize()

		if "children" in data and data["children"] is not None:
			data["children"] = [c.serialize() for c in data["children"]]

		if "state" in data:
			data["state"] = data["state"].value

		# send to event log
		self._event_log.write(util.now(), "step:" + str(data))

		self._marshal.event(Event.STEP, data)

	def _step_log (self, message, level = None):
		data = {
			"level": level or "info",
			"message": message
		}

		# send to log
		self._msg_log.write(util.now(), message)

		self._marshal.event(Event.LOG, data)

	def pause (self):
		"""
		Pause the experiment.

		This pauses the currently running step, and calls the pause method on
		all registered machines.
		"""

		if self.state is State.RUNNING:
			self.state = State.PAUSED
			self.paused(self)

			return defer.gatherResults(
				[defer.maybeDeferred(self.step.pause)]
				+ [defer.maybeDeferred(m.pause) for m in self._machines]
			)

	def resume (self):
		"""
		Resume the experiment.

		Resumes after pause() has been called.
		"""

		if self.state is State.PAUSED:
			self.state = State.RUNNING
			self.resumed(self)

			return defer.gatherResults(
				[defer.maybeDeferred(self.step.resume)]
				+ [defer.maybeDeferred(m.resume) for m in self._machines]
			)

	def stop (self):
		"""
		(Emergency) stop the experiment.

		Immediately halts all processing and exits with an error.
		"""

		if self.state in (State.RUNNING, State.PAUSED):
			self.cancelled(self)

			return defer.gatherResults(
				[defer.maybeDeferred(self.step.abort)]
				+ [defer.maybeDeferred(m.pause) for m in self._machines]
			)
	#
	# Logging
	#
	# TODO - replace logger with batch logger.
	#
	def log_variables (self, *variables):
		if self._logging is True:
			raise Error ("Cannot set variables whilst actively logging")

		self._log_variables = {}

		for v in variables:
			if isinstance(v, data.Variable) and v.alias not in self._variables:
				self._log_variables[v.alias] = v

			elif isinstance(v, Component):
				self._log_variables.update(v.variables)

	def set_log_output (self, name):
		time_zero = util.now()
		name = LogFile.get_dir(name)

		if len(self._log_variables) is 0:
			for m in self._machines:
				self._log_variables.update(m.variables)

			self._log_variables.update(self.interface.properties)

		items = self._log_variables.iteritems()

		for key, var in items:
			try:
				var.setLogFile(LogFile(name, key, time_zero))
				var.truncate()
			except AttributeError:
				pass

		try:
			self._event_log.close()
			self._msg_log.close()
		except AttributeError:
			pass

		self._event_log = LogFile(name, "events", time_zero)
		self._msg_log = LogFile(name, "log", time_zero)

		self._logging = True
		self._time_zero = time_zero

		self._event_log.write(time_zero, "tz")
		self.marshal.event(Event.TIMEZERO, { "time": time_zero, "clear": True } )

	def stop_logging (self):
		self._logging = False

		items = self._log_variables.iteritems()

		for key, var in items:
			try:
				var.stopLogging()
			except AttributeError:
				pass

		try:
			self._event_log.close()
			self._event_log = None
		except AttributeError:
			pass

		try:
			self._msg_log.close()
			self._msg_log = None
		except AttributeError:
			pass