
# Invivo AI challenge simulation environment
# Copyright 2009 Simon Funk
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.

try:
	import json
except ImportError:
	print "WARNING: json library not available.  Network-related methods may fail."
	print "(Update to Python 2.6?)"


class Interface(object):
	"""Interfaces are mostly passive containers of state/input/output values
	passed back and forth between controllers and models.   E.g., an Interface
	for a robot might contain, among other things, joint angle sensors (which
	the controller reads from the model), and joint torque controls (which
	the controller sends to the model).

	Normally, the Controller just reads sensors and writes controls in the
	Interface directly (during each update cycle, between simulation time
	slices), but this class also provides methods to facilitate marshalling
	of the values in case the bulk of the Controller is to be run in a separate
	process (perhaps in another language, or on anothre machine).

	Json is the default representation since compatable libraries are likely
	to exist in other languages.  (And it isn't the abomination that is XML.)
	"""

	# Subclass should override these with sets of field names for each class of field:
	staticfields  = set()	# Configuration info that doesn't change.
	sensorfields  = set()	# Sensory information, from model to controller.
	controlfields = set()	# Control values, from controller to model.

	def toJson(self, fields):
		"""Returns a json representation of the named fields of self.
		I.e., pass ('foo', 'bar') to get a json representation of the dict
		{ 'foo': self.foo, 'bar': self.bar }
		"""
		return json.dumps(dict((f, getattr(self, f)) for f in fields))

	def fromJson(self, jsonvals, limit=None):
		"""Sets whatever fields are provided in the dict json-encoded in jsonvals.
		If limit is provided (typically a set), any fieldname not in limit will raise
		an exception.
		"""
		for f, val in json.loads(jsonvals).items():
			if not hasattr(self, f):
				raise Exception("fromJson(%s=...): This Interface has no %s attribute."%(f, f))
			if limit and f not in limit:
				raise Exception("fromJson(%s=...): %s not in %s"%(f, f, limit))
			setattr(self, f, val)

	def staticToJson(self):
		"Returns a json representation of the Static components."
		return self.toJson(self.staticfields)

	def staticFromJson(self, jsonvals):
		"Sets the Static components from a json string as returned by staticToJson."
		self.fromJson(jsonvals, self.staticfields)

	def sensorsToJson(self):
		"Returns a json representation of the Sensor components."
		return self.toJson(self.sensorfields)

	def sensorsFromJson(self, jsonvals):
		"Sets the Sensor components from a json string as returned by sensorsToJson."
		self.fromJson(jsonvals, self.sensorfields)

	def controlsToJson(self):
		"Returns a json representation of the Control components."
		return self.toJson(self.controlfields)

	def controlsFromJson(self, jsonvals):
		"Sets the Control components from a json string as returned by controlsToJson."
		self.fromJson(jsonvals, self.controlfields)

