class BootStrap(object): config_specification = """ [General] sample_rate = float [Data] [[__many__]] type = option('sense.xml') [[[Identifier]]] type = option("name", "mac", "uuid") adapter = string(default='') id = string(default='') file = string(default='') [[[Location]]] type = option("virtual", "gps", "none", default="none") longitude = string(default='') latitude = string(default='') altitude = string(default='') speed = string(default='') climb = string(default='') track = string(default='') longutide_error = string(default='') latitude_error = string(default='') altitude_error = string(default='') speed_error = string(default='') climb_track = string(default='') track_error = string(default='') [Transport] [[__many__]] url = string pause_rate = float timeout = float type = option('http') [[[Queue]]] type = option('memory','sqlite') data = string(default='') [[[Security]]] type = option('none','rsa','m2') data_dir = string(default='') key_file = string(default='') key_size = integer(default=None) [Handlers] [[__many__]] type = option('virtual','1wire') sensors = list(default=None) device = string(default=None) [Sensors] [[__many__]] type = option('virtual/temp') id = string units = string rangeMin = integer rangeMax = integer """ # Taken from # http://stackoverflow.com/questions/547829/how-to-dynamically-load-a-python-class def _load_obj(self, name): components = name.split('.') path = '.'.join(components[:-1]) clss = components[-1:][0] mod = __import__(path, globals(), locals(), clss) return getattr(mod, clss)() def _set_args(self, obj, key, value): logging.debug('Setting attribute {0} to {1} for class {2} (type: {3})'.format(key, value, obj, type(value))) setattr(obj, key, value) def __add_delay_eval(self, var, attr, val): if var not in self._delayed_eval: self._delayed_eval[var] = {} self._delayed_eval[var][attr] = val def __create_variable(self, cfg, section, var): tp = cfg['type'] logging.debug('Creating variable {0} with type {1}'.format(var, tp)) if tp == 'none': self._object_map[var] = None else: logging.debug('\t>>ltsense.{0}.{1}'.format(section.lower(), self.types[section][tp])) self._object_map[var] = self._load_obj('{0}.{1}'.format(section.lower(), self.types[section][tp])) # special types for the controller if section == 'Data': self._data_handlers.append(self._object_map[var]) elif section == 'Handlers': self._sensor_handlers.append(self._object_map[var]) elif section == 'Transport': self._transports.append(self._object_map[var]) def __process(self, cfg, section=None, variable=None): """Recursively processes a configuration object""" for c in cfg: if type(cfg[c]) is Section: if c == 'General': logging.debug('Loading General Section') logging.debug('Sample Rate: {0}'.format(cfg[c]['sample_rate'])) self._controller.sample_rate = cfg[c]['sample_rate'] elif c[0].isupper() and section is None: logging.debug('Loading Section: {0} '.format(c)) self.__process(cfg[c], c) elif c[0].isupper() and section is not None: av = "$${0}.{1}".format(section.lower(), c) logging.debug('Loading Anonymous Variable {0} with Type: {1}'.format(av, c)) self.__create_variable(cfg[c], c, av) self.__add_delay_eval(variable, c.lower(), av) self.__process(cfg[c], c, av) else: var = "${0}".format(c) logging.debug('Loading Variable: {0}'.format(var)) self.__create_variable(cfg[c], section, var) self.__process(cfg[c], section, var) else: if cfg[c] is None or cfg[c] == '': # limitation of configobj requires defaults for non-mandatory attributes # so we'll ignore the empty string and None pass # Anything starting with $ or is a list has delayed evaluation elif (isinstance(cfg[c], basestring) and cfg[c][0] == '$') or isinstance(cfg[c], list): logging.debug("Delay evaluation for {0}".format(cfg[c])) self.__add_delay_eval(variable, c, cfg[c]) elif c == 'type': pass else: self._set_args(self._object_map[variable], c, cfg[c]) def __eval_item(self, item): """Used vy __eval_delays for deferred evaluation variables (those that start with $) and lists. Items in list starting with $ will be substituted for defined objects. Non-list items don. """ if isinstance(item, list): # Substitute all variables (start with $) for i, j in enumerate(item): if j[0] == '$': item[i] = self._object_map[j] return item else: return self._object_map[item] def __eval_delays(self): """Call after a successful __process to assign all delayed variables""" for var in self._delayed_eval: for i, j in self._delayed_eval[var].items(): item = self.__eval_item(j) logging.debug("Setting object {0}, {1} = {2}".format(var, i, item)) self._set_args(self._object_map[var], i, item) def __init__(self, filename): self._delayed_eval = {} self._object_map = {} self._controller = DefaultController() self._data_handlers = [] self._sensor_handlers = [] self._transports = [] self.types = {'Identifier': {'name': 'NamedIdentifier', 'mac': 'MacAddressIdentifier', 'uuid': 'UUIDIdentifier'}, 'Location': {'virtual': 'VirtualLocation', 'gps': 'GPSLocation'}, 'Queue': {'memory': 'MemoryQueue', 'sqlite': 'SQLiteQueue'}, 'Security': {'rsa': 'pypiRsa.RSASecurity', 'm2': 'm2crypto.M2Security'}, 'Transport': {'http': 'http.QueuedHttpPostTransport'}, 'Data': {'sense.xml': 'SenseDataHandler'}, 'Sensors': {'virtual/temp': 'virtual.VirtualTemperatureSensor', 'virtual/image': 'virtual.ImageSensor'}, 'Handlers': {'virtual': 'GeneralSensorHandler', '1wire': 'OWFSSensorHandler'}, } cfg = ConfigObj(filename, configspec=BootStrap.config_specification.split('\n')) test = cfg.validate(Validator()) valid = True for (section_list, key, _) in flatten_errors(cfg, test): if key is not None: logging.error('Invalid value for key {0} in section {1}'.format(key, ', '.join(section_list))) valid = False else: logging.error('Section {0} failed validation'.format(', '.join(section_list))) valid = False if not valid: exit(3) self.__process(cfg) self.__eval_delays() logging.debug(self._data_handlers) if len(self._data_handlers) != 1: # TODO: Implement full data/transport/sensor mappings logging.error('Currently, LtSense can only support one Data Handler. Later releases may allow mappings') exit(4) self._controller.data_handler = self._data_handlers[0] self._controller.sensor_handlers = self._sensor_handlers self._controller.transports = self._transports logging.info("Starting Controller") self._controller.start()
class BootStrap(object): config_specification = """ [General] sample_rate = float [Data] [[__many__]] type = option('sense.xml', 'sense.json') [[[Identifier]]] type = option("name", "mac", "uuid") adapter = string(default='') id = string(default='') file = string(default='') [[[Location]]] type = option("virtual", "gps", "none", default="none") longitude = string(default='') latitude = string(default='') altitude = string(default='') speed = string(default='') climb = string(default='') track = string(default='') longutide_error = string(default='') latitude_error = string(default='') altitude_error = string(default='') speed_error = string(default='') climb_track = string(default='') track_error = string(default='') [Transport] [[__many__]] url = string(default='') token = string(default='') fileId = string(default='') pause_rate = float timeout = float type = option('http', 'datazar', 'file') directory = string(default='/tmp') file_extension = string(default='json') [[[Queue]]] type = option('memory','sqlite') data = string(default='') [[[Security]]] type = option('none','rsa','m2') data_dir = string(default='') key_file = string(default='') key_size = integer(default=None) [Handlers] [[__many__]] type = option('virtual','1wire','phidget') sensors = list(default=None) device = string(default=None) attach_timeout = integer(default=None) data_rate = integer(default=None) [Sensors] [[__many__]] type = option('virtual/temp', 'phidget', 'camera/cv2', 'camera/pi') id = string(default=None) units = string(default=None) rangeMin = integer(default=None) rangeMax = integer(default=None) port_num = integer(default=None) port_type = option('analog','digital', default=None) model = string(default=None) """ # Taken from # http://stackoverflow.com/questions/547829/how-to-dynamically-load-a-python-class def _load_obj(self, name): components = name.split('.') path = '.'.join(components[:-1]) clss = components[-1:][0] mod = __import__(path, globals(), locals(), clss) return getattr(mod, clss)() def _set_args(self, obj, key, value): logging.debug( 'Setting attribute {0} to {1} for class {2} (type: {3})'.format( key, value, obj, type(value))) setattr(obj, key, value) def __add_delay_eval(self, var, attr, val): if var not in self._delayed_eval: self._delayed_eval[var] = {} self._delayed_eval[var][attr] = val def __create_variable(self, cfg, section, var): tp = cfg['type'] logging.debug('Creating variable {0} with type {1}'.format(var, tp)) if tp == 'none': self._object_map[var] = None else: logging.debug('\t>>ltsense.{0}.{1}'.format( section.lower(), self.types[section][tp])) self._object_map[var] = self._load_obj('{0}.{1}'.format( section.lower(), self.types[section][tp])) # special types for the controller if section == 'Data': self._data_handlers.append(self._object_map[var]) elif section == 'Handlers': self._sensor_handlers.append(self._object_map[var]) elif section == 'Transport': self._transports.append(self._object_map[var]) def __process(self, cfg, section=None, variable=None): """Recursively processes a configuration object""" for c in cfg: if type(cfg[c]) is Section: if c == 'General': logging.debug('Loading General Section') logging.debug('Sample Rate: {0}'.format( cfg[c]['sample_rate'])) self._controller.sample_rate = cfg[c]['sample_rate'] elif c[0].isupper() and section is None: logging.debug('Loading Section: {0} '.format(c)) self.__process(cfg[c], c) elif c[0].isupper() and section is not None: av = "$${0}.{1}".format(section.lower(), c) logging.debug( 'Loading Anonymous Variable {0} with Type: {1}'.format( av, c)) self.__create_variable(cfg[c], c, av) self.__add_delay_eval(variable, c.lower(), av) self.__process(cfg[c], c, av) else: var = "${0}".format(c) logging.debug('Loading Variable: {0}'.format(var)) self.__create_variable(cfg[c], section, var) self.__process(cfg[c], section, var) else: if cfg[c] is None or cfg[c] == '': # limitation of configobj requires defaults for non-mandatory attributes # so we'll ignore the empty string and None pass # Anything starting with $ or is a list has delayed evaluation elif (isinstance(cfg[c], basestring) and cfg[c][0] == '$') or isinstance(cfg[c], list): logging.debug("Delay evaluation for {0}".format(cfg[c])) self.__add_delay_eval(variable, c, cfg[c]) elif c == 'type': pass else: self._set_args(self._object_map[variable], c, cfg[c]) def __eval_item(self, item): """Used vy __eval_delays for deferred evaluation variables (those that start with $) and lists. Items in list starting with $ will be substituted for defined objects. Non-list items don. """ if isinstance(item, list): # Substitute all variables (start with $) for i, j in enumerate(item): if j[0] == '$': item[i] = self._object_map[j] return item else: return self._object_map[item] def __eval_delays(self): """Call after a successful __process to assign all delayed variables""" for var in self._delayed_eval: for i, j in self._delayed_eval[var].items(): item = self.__eval_item(j) logging.debug("Setting object {0}, {1} = {2}".format( var, i, item)) self._set_args(self._object_map[var], i, item) def __init__(self, filename): self._delayed_eval = {} self._object_map = {} self._controller = DefaultController() self._data_handlers = [] self._sensor_handlers = [] self._transports = [] self.types = { 'Identifier': { 'name': 'NamedIdentifier', 'mac': 'MacAddressIdentifier', 'uuid': 'UUIDIdentifier' }, 'Location': { 'virtual': 'VirtualLocation', 'gps': 'GPSLocation' }, 'Queue': { 'memory': 'MemoryQueue', 'sqlite': 'SQLiteQueue' }, 'Security': { 'rsa': 'pypiRsa.RSASecurity', 'm2': 'm2crypto.M2Security' }, 'Transport': { 'http': 'http.QueuedHttpPostTransport', 'datazar': 'datazar.QueuedDatazarTransport', 'file': 'localfile.LocalFileTransport' }, 'Data': { 'sense.xml': 'SenseXMLDataHandler', 'sense.json': 'SenseJsonDataHandler' }, 'Sensors': { 'virtual/temp': 'virtual.VirtualTemperatureSensor', 'virtual/image': 'virtual.ImageSensor', 'camera/cv2': 'camera.CV2Camera', 'camera/pi': 'pi.PiCamera', 'phidget': 'phidget.PhidgetSensor' }, 'Handlers': { 'virtual': 'GeneralSensorHandler', '1wire': 'OWFSSensorHandler', 'phidget': 'PhidgetSensorHandler' }, } cfg = ConfigObj(filename, configspec=BootStrap.config_specification.split('\n')) test = cfg.validate(Validator()) valid = True for (section_list, key, _) in flatten_errors(cfg, test): if key is not None: logging.error( 'Invalid value for key {0} in section {1}'.format( key, ', '.join(section_list))) valid = False else: logging.error('Section {0} failed validation'.format( ', '.join(section_list))) valid = False if not valid: exit(3) self.__process(cfg) self.__eval_delays() logging.debug(self._data_handlers) if len(self._data_handlers) != 1: # TODO: Implement full data/transport/sensor mappings logging.error( 'Currently, LtSense can only support one Data Handler. Later releases may allow mappings' ) exit(4) self._controller.data_handler = self._data_handlers[0] self._controller.sensor_handlers = self._sensor_handlers self._controller.transports = self._transports logging.info("Starting Controller") self._controller.start()
def __init__(self, filename): self._delayed_eval = {} self._object_map = {} self._controller = DefaultController() self._data_handlers = [] self._sensor_handlers = [] self._transports = [] self.types = {'Identifier': {'name': 'NamedIdentifier', 'mac': 'MacAddressIdentifier', 'uuid': 'UUIDIdentifier'}, 'Location': {'virtual': 'VirtualLocation', 'gps': 'GPSLocation'}, 'Queue': {'memory': 'MemoryQueue', 'sqlite': 'SQLiteQueue'}, 'Security': {'rsa': 'pypiRsa.RSASecurity', 'm2': 'm2crypto.M2Security'}, 'Transport': {'http': 'http.QueuedHttpPostTransport'}, 'Data': {'sense.xml': 'SenseDataHandler'}, 'Sensors': {'virtual/temp': 'virtual.VirtualTemperatureSensor', 'virtual/image': 'virtual.ImageSensor'}, 'Handlers': {'virtual': 'GeneralSensorHandler', '1wire': 'OWFSSensorHandler'}, } cfg = ConfigObj(filename, configspec=BootStrap.config_specification.split('\n')) test = cfg.validate(Validator()) valid = True for (section_list, key, _) in flatten_errors(cfg, test): if key is not None: logging.error('Invalid value for key {0} in section {1}'.format(key, ', '.join(section_list))) valid = False else: logging.error('Section {0} failed validation'.format(', '.join(section_list))) valid = False if not valid: exit(3) self.__process(cfg) self.__eval_delays() logging.debug(self._data_handlers) if len(self._data_handlers) != 1: # TODO: Implement full data/transport/sensor mappings logging.error('Currently, LtSense can only support one Data Handler. Later releases may allow mappings') exit(4) self._controller.data_handler = self._data_handlers[0] self._controller.sensor_handlers = self._sensor_handlers self._controller.transports = self._transports logging.info("Starting Controller") self._controller.start()
def __init__(self, filename): self._delayed_eval = {} self._object_map = {} self._controller = DefaultController() self._data_handlers = [] self._sensor_handlers = [] self._transports = [] self.types = { 'Identifier': { 'name': 'NamedIdentifier', 'mac': 'MacAddressIdentifier', 'uuid': 'UUIDIdentifier' }, 'Location': { 'virtual': 'VirtualLocation', 'gps': 'GPSLocation' }, 'Queue': { 'memory': 'MemoryQueue', 'sqlite': 'SQLiteQueue' }, 'Security': { 'rsa': 'pypiRsa.RSASecurity', 'm2': 'm2crypto.M2Security' }, 'Transport': { 'http': 'http.QueuedHttpPostTransport', 'datazar': 'datazar.QueuedDatazarTransport', 'file': 'localfile.LocalFileTransport' }, 'Data': { 'sense.xml': 'SenseXMLDataHandler', 'sense.json': 'SenseJsonDataHandler' }, 'Sensors': { 'virtual/temp': 'virtual.VirtualTemperatureSensor', 'virtual/image': 'virtual.ImageSensor', 'camera/cv2': 'camera.CV2Camera', 'camera/pi': 'pi.PiCamera', 'phidget': 'phidget.PhidgetSensor' }, 'Handlers': { 'virtual': 'GeneralSensorHandler', '1wire': 'OWFSSensorHandler', 'phidget': 'PhidgetSensorHandler' }, } cfg = ConfigObj(filename, configspec=BootStrap.config_specification.split('\n')) test = cfg.validate(Validator()) valid = True for (section_list, key, _) in flatten_errors(cfg, test): if key is not None: logging.error( 'Invalid value for key {0} in section {1}'.format( key, ', '.join(section_list))) valid = False else: logging.error('Section {0} failed validation'.format( ', '.join(section_list))) valid = False if not valid: exit(3) self.__process(cfg) self.__eval_delays() logging.debug(self._data_handlers) if len(self._data_handlers) != 1: # TODO: Implement full data/transport/sensor mappings logging.error( 'Currently, LtSense can only support one Data Handler. Later releases may allow mappings' ) exit(4) self._controller.data_handler = self._data_handlers[0] self._controller.sensor_handlers = self._sensor_handlers self._controller.transports = self._transports logging.info("Starting Controller") self._controller.start()