123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515 |
- '''
- Created on Nov 28, 2012
- @author: Repnikov Dmitry
- '''
- import os, logging, threading, re, copy
- from jsonrpc_server import JSONRPCServer
- from module_loader import ModuleLoader
- from helperBTE import HelperBTE
- from helperIPcamera import HelperIPcamera
- from message_queue import MessageQueue
- from helperSNMPSensors import HelperSNMPSensors
- from helperElectricityMeter import HelperElectricityMeter
- from helperDGS import HelperDGS
- from helperMPSU import HelperMPSU
- from helperInverPack import HelperInverPackMeter
- from helperCRS import HelperCRS
- from helperPulsar import HelperPulsar
- from helperUPKB import HelperUPKB
- from configman import make_copy
- from sensor_scheduler import SensorScheduler
- class SensorHolder:
- """ Class to hold sensor, cache and other """
-
- sensors_lock = threading.Lock() # Global lock for sensors
- def __init__(self, _sensor):
- self.state = None # Current state of this sensor
- self.value = None # Current value of this sensor
- self.sensor = _sensor
-
- def onChange(self):
- pass
- class SensorManager(JSONRPCServer, HelperPulsar, HelperBTE, HelperIPcamera, HelperSNMPSensors, HelperElectricityMeter, HelperDGS, HelperMPSU, HelperInverPackMeter, HelperCRS, HelperUPKB):
- """ Sensor manager. API for sensors storing and management. """
- JSONRPC_SERVER_ADDRESS = ("127.0.0.1", 4665)
- STATE_CHANGE_TRIGGER_PATH = ["notifications", "notifications", "3db09436-17a8-4b0e-b70c-93ceaf094cff", "trigger"]
- SENSOR_SCHEDULERS = ["DEFAULT", "1W", "CONMAN"]
-
- def __init__(self, _configman, _queue_event, _queue_action):
- self.logger = logging.getLogger(__name__)
- self.sensors = {}
- self.sensor_modules = ModuleLoader(__file__, "/../sensors/", "sensor_*", _recursive = True)
- self.event_modules = ModuleLoader(__file__, "/../events/", "event_*")
- self.configman = _configman
- self.queue_event = _queue_event
- self.queue_action = _queue_action
- self.deviceman = None
- self.sensor_schedulers = {}
- for name in self.SENSOR_SCHEDULERS:
- self.sensor_schedulers[name] = SensorScheduler(name)
-
- HelperBTE.__init__(self, _configman)
- HelperIPcamera.__init__(self, _configman)
- HelperSNMPSensors.__init__(self, _configman)
- HelperElectricityMeter.__init__(self, _configman)
- HelperDGS.__init__(self, _configman)
- HelperMPSU.__init__(self, _configman)
- HelperInverPackMeter.__init__(self, _configman)
- HelperCRS.__init__(self, _configman)
- HelperPulsar.__init__(self, _configman)
- HelperUPKB.__init__(self, _configman)
-
- JSONRPCServer.__init__(self)
-
- def set_device_man(self, _deviceman):
- self.deviceman = _deviceman
- try:
- HelperBTE.set_device_man(_deviceman)
- HelperDGS.set_device_man(_deviceman)
- HelperMPSU.set_device_man(_deviceman)
- HelperPulsar.set_device_man(_deviceman)
- except: self.logger.exception("Unable to set device manager instance")
- #try: self.MPSU_check({"alias":"serial232.0", "name":"/dev/ttyS1"}, 9600)
- #except: self.logger.exception("Unable to set check MPSU")
-
- def start_sensor(self, id):
- """ Start sensor thread, if any """
- try:
- self.logger.debug("Starting sensor module '"+id+"'")
- self.put_value_pair(id, (None, None), default=True)
- self.sensors[id].sensor.restart()
- except: self.logger.exception("Unable to start sensor '"+id+"'")
-
- def start_scheduler(self):
- try:
- for scheduler in self.sensor_schedulers.values():
- scheduler.start()
- except:
- self.logger.exception("start scheduler error")
-
- def register_polling_sensor(self, sensor):
- self.sensor_schedulers[sensor.get_scheduler_name()].register_sensor(sensor)
-
- def unregister_polling_sensor(self, sensor):
- self.sensor_schedulers[sensor.get_scheduler_name()].unregister_sensor(sensor)
-
- def list_sensor_ids(self):
- return self.sensors.keys()
-
- def find_sensor_by_class(self, class_name):
- """ Find sensor id by class name (can be regular expression) """
- SensorHolder.sensors_lock.acquire()
- res = []
- try:
- for id in self.sensors.keys():
- if re.match(class_name, self.sensors[id].sensor.__class__.__name__):
- res.append(id)
- except: self.logger.exception("Unable to find sensor by class '"+str(class_name)+"'")
- SensorHolder.sensors_lock.release()
- return res
-
- def find_sensor_by_group(self, group_name):
- """ Find sensor id by group name (can be regular expression) """
- SensorHolder.sensors_lock.acquire()
- res = []
- try:
- for id in self.sensors.keys():
- if re.match(group_name, self.sensors[id].sensor.group):
- res.append(id)
- except: self.logger.exception("Unable to find sensor by group '"+str(group_name)+"'")
- SensorHolder.sensors_lock.release()
- return res
-
- def get_available_classes(self):
- """ Return dictionary {"class_name":("description", [ids])} """
- res = {}
- self.logger.debug("get_available_classes start")
- SensorHolder.sensors_lock.acquire()
- try:
- sensor_classes = self.sensor_modules.class_by_regexp("Sensor[\w]+")
- for sensor_class in sensor_classes:
- sensor_ids = []
- for id in self.sensors.keys():
- if (not (id in sensor_ids)) and (self.sensors[id].sensor.__class__.__name__ == sensor_class.__name__):
- sensor_ids.append(id)
- res[sensor_class.__name__] = ( [sensor_class.__doc__, sensor_ids] )
- except: res = None
- SensorHolder.sensors_lock.release()
- self.logger.debug("get_available_classes end")
- return res
-
- def get_view_dict(self, id):
- """
- Returns view dictionary of sensor. This dictionary
- contains strings, that can be used in formatting.
- """
- try:
- return self.sensors[id].sensor.get_view_dict()
- except:
- return {}
-
- def get_view_dicts(self, ids):
- """ Returns view dictionaries of sensors. """
- res = {}
- try:
- for id in ids: res[id] = self.get_view_dict(id)
- return res
- except: return {}
-
- def get_view_dict_by_alias(self, alias):
- try: return self.get_sensor_by_alias(alias).get_view_dict()
- except: return {}
-
- def get_view_dicts_by_aliases(self, aliases):
- res = {}
- for alias in aliases:
- try: res[alias] = self.get_sensor_by_alias(alias).get_view_dict()
- except: self.logger.exception("get_sensor_by_alias exception")
- return res
-
- def get_all_sensors_views(self):
- res = {}
- for id in self.sensors.keys():
- res[id] = self.get_view_dict(id)
- return res
-
- def get_value_description(self, id, value):
- """ Returns full form of value description, include state """
- try: return self.sensors[id].sensor.get_value_descr(value)
- except: return {}
-
- def apply_view_dict(self, id, nview):
- """ Set dict nview to sensor (through SensorView class) """
- try: return self.sensors[id].sensor.apply_view_dict(nview)
- except: return False
-
- def apply_view_dict_by_alias(self, alias, nview):
- """ Set dict nview to sensor (through SensorView class) """
- try: return self.get_sensor_by_alias(alias).apply_view_dict(nview)
- except: return False
-
- def put_value_pair(self, id, pair, default = False, priority = None):
- """ Write value pair (value, state) to sensor corresponding files """
- """ default - if no value/state files were created, this pair be written as default """
- value, state = pair
- fRefreshValue, fRefreshSate = False, False
-
-
- SensorHolder.sensors_lock.acquire()
- if not (id in self.sensors.keys()):
- SensorHolder.sensors_lock.release()
- return
-
- try:
- if not default: old_value, old_state = self.get_value_pair(id, False)
- else: old_value, old_state = pair
- except:
- self.logger.exception("Exception while put state/value for sensor '"+str(id)+"'")
- SensorHolder.sensors_lock.release()
- return
-
- try:
- if (old_value != value) or (default): fRefreshValue, self.sensors[id].value = True, value
- if (old_state != state) or (default): fRefreshSate, self.sensors[id].state = True, state
- except:
- self.logger.exception("Exception while put state/value for sensor '"+str(id)+"'")
- SensorHolder.sensors_lock.release()
- return
-
- #fRefreshValue = fRefreshValue or self.sensors[id].sensor.history['any_value']
- fRefreshValue = fRefreshValue or (id in [
- 'DEAD0000-BEAF-DEAD-BEAF-EE0000000005',
- 'DEAD0000-BEAF-DEAD-BEAF-EE0000000006',
- 'DEAD0000-BEAF-DEAD-BEAF-EE00000000A5',
- 'DEAD0000-BEAF-DEAD-BEAF-EE00000000A6'
- ])
- #if id == '00000000-0000-0000-0000-000000000001':
- # self.logger.error("Value change of '"+id+"' detected: "+str(old_value)+" -> "+str(value) + " " + str(fRefreshValue) + " " + str(default))
- if fRefreshValue:
- #self.logger.error("Value change of '"+id+"' detected: "+str(old_value)+" -> "+str(value) + " " + str(fRefreshValue) + " " + str(default))
- if self.logger.level <= logging.DEBUG:
- self.logger.debug("Value change of '"+id+"' detected: "+str(old_value)+" -> "+str(value))
- if not default: self.create_event_custprior(priority, "EventValueChange", self.sensors[id].sensor, old_value, value, state)
-
- if fRefreshSate:
- if self.logger.level <= logging.DEBUG:
- self.logger.debug("State change of '"+id+"' detected: "+str(old_state)+" -> "+str(state))
- if not default: self.create_event_custprior(priority, "EventStateChange", self.sensors[id].sensor, old_state, state, value)
-
- if fRefreshValue or fRefreshSate: self.sensors[id].onChange()
- SensorHolder.sensors_lock.release()
-
- def get_value_pair(self, id, fLock = True):
- """ Read value pair (value, state) for sensor from corresponding files """
- value, state = None, None
- if fLock: SensorHolder.sensors_lock.acquire()
- try:
- if id in self.sensors.keys(): value, state = self.sensors[id].value, self.sensors[id].state
- finally:
- if fLock: SensorHolder.sensors_lock.release()
- return (value, state)
-
- def get_value_pairs(self, ids):
- """ Returns id:value/state pairs for list of ids """
- SensorHolder.sensors_lock.acquire()
- try: res = dict(zip(ids, map(lambda x: self.get_value_pair(x, False), ids)))
- except: res = {}
- SensorHolder.sensors_lock.release()
- return res
-
- def get_value_descrs(self, values):
- """ Returns id:description pairs for list of ids """
- SensorHolder.sensors_lock.acquire()
- try: res = dict(zip(values.keys(), map(lambda x: self.get_value_description(x, values[x]), values.keys())))
- except: res = {}
- SensorHolder.sensors_lock.release()
- return res
-
- def get_value_pair_and_descr(self, ids):
- """ Returns id:value/state/description pairs for list of ids """
- SensorHolder.sensors_lock.acquire()
- try:
- res = dict(zip(ids, map(lambda x: self.get_value_pair(x, False), ids)))
- res = dict(zip(ids, map(lambda x: (res[x][0], res[x][1], self.get_value_description(x, res[x][0])), ids )))
- except:
- self.logger.exception("Exception while get_value_pair_and_descr.")
- res = {}
- SensorHolder.sensors_lock.release()
- return res
-
- def get_sensor_by_id(self, id):
- return self.sensors[id].sensor
-
- def get_sensor_by_alias(self, alias):
- for sensor_holder in self.sensors.values():
- if sensor_holder.sensor.alias == alias:
- return sensor_holder.sensor
- raise Exception("sensor {0} not found".format(alias))
-
- def get_sensor_id_by_alias(self, alias):
- for sensor_holder in self.sensors.values():
- if sensor_holder.sensor.alias == alias:
- return sensor_holder.sensor.id
- raise Exception("sensor {0} not found".format(alias))
-
- def update_snmp_sensor(self, id):
- """ Immidiate update of snmp sensor. Blocking. """
- try: self.get_sensor_by_id(id).immidiate_update()
- except: self.logger.exception("Exception while update_snmp_sensor")
-
- """ ToDo: rename this to event_taker """
- def subscribe(self, _class, _event, _handler):
- """ Register new handler for some sensor """
- self.queue_event.subscribe(_class, _event, _handler)
-
- def action_taker(self, _class, _event, _handler):
- """ Register new handler for some sensor """
- self.queue_action.subscribe(_class, _event, _handler)
-
- def put_message(self, _queue, _class, _priority, *_args):
- """ Create action or event message for some subsystem. _class - string with event class name. """
- """ _args - will be translate to command. if [0] (sender) = None, we set it as self """
- if (len(_args) >= 1):
- if (len(_class) > 2) and (_class[-2:] != "\Z"): _class += "\Z"
- if self.logger.level <= logging.INFO:
- self.logger.info("Try to generate event '"+str(_class)+"' for '"+str(_args[0])+"'...")
- classes = self.event_modules.class_by_regexp(_class)
- if len(classes) == 1:
- if (len(_args) >= 1) and (_args[0] == None): _args = (self,)+_args[1:]
- try: _queue.put(classes[0](*_args))
- except: self.logger.exception("Exception while creating event class '"+_class+"'")
- else: self.logger.error("Loading event "+_class+" - FAIL, no or too many classes ("+str(classes)+")")
- else: self.logger.error("Not enough arguments to create event '"+_class+"' - FAIL")
-
- def create_event(self, _class, *_args):
- self.put_message(self.queue_event, _class, MessageQueue.PRIOR_LOWEST, *_args)
- def create_event_hiprior(self, _class, *_args):
- self.put_message(self.queue_event, _class, MessageQueue.PRIOR_HIGHEST, *_args)
-
- def create_event_custprior(self, _prior, _class, *_args):
- if _prior == None: _prior = MessageQueue.PRIOR_LOWEST
- self.put_message(self.queue_event, _class, _prior, *_args)
-
- def create_action(self, _class, *_args):
- self.put_message(self.queue_action, _class, MessageQueue.PRIOR_LOWEST, *_args)
-
- """ ToDo: maybe move this to some power-helper """
- def is_battery_present(self):
- try: return self.get_sensor_by_alias("BAT").is_battery_present()
- except: return False
-
- """ --- This two routines for compatibility, and for raw Event/Action class put --- """
- def put_action(self, action, priority = None):
- """ Put action element in queue """
- if priority == None: priority = MessageQueue.PRIOR_LOWEST
- self.queue_action.put(action, priority)
- def put_event(self, event, priority = None):
- """ Put event element in queue """
- if priority == None: priority = MessageQueue.PRIOR_LOWEST
- self.queue_event.put(event)
-
- def load_sensor(self, id, descr):
- """ Create sensor from configuration description and put it in self.sensors """
- """ WARNING! By default this function works only with NEW configuration. """
- """ For newly added sensor please use load_new_sensor, to save/deserialize configuration. """
- try:
- self.logger.info("Try to load sensor "+id+"...")
- sensor_classes = self.sensor_modules.class_by_regexp(descr["sensor_class"]+"\Z")
- if len(sensor_classes) == 1:
- try:
- self.logger.debug("Found sensor class '"+str(sensor_classes[0].__name__)+"' for sensor "+id+", creating...")
- self.sensors[id] = SensorHolder(sensor_classes[0](self.configman, self, id))
- return True
- except: self.logger.exception("Exception while creating sensor class '"+str(sensor_classes[0])+"', id "+id)
- else: self.logger.error("Loading sensor "+id+" - FAIL, no such class: "+descr["sensor_class"]+" or too many classes ("+str(sensor_classes)+")")
- except: self.logger.exception("Exception while searching for sensor with description '"+str(descr)+"'")
- return False
- def load_new_sensor(self, id, class_name):
- fake_descr = {"sensor_class":class_name}
- if self.load_sensor(id, fake_descr):
- self.start_sensor(id)
- self.configman.confirm_path(["sensors", id])
- return True
- return False
-
- def confirm_sensors(self):
- self.configman.confirm_path(["sensors"])
-
- def delete_sensor(self, id):
- result = False
- self.sensors[id].sensor.stop()
- SensorHolder.sensors_lock.acquire()
- try:
- self.sensors[id].sensor.delete()
- del self.sensors[id]
- except: self.logger.exception("Exception while delete sensor '"+str(id)+"'")
- finally: SensorHolder.sensors_lock.release()
-
- try:
- """ This must not be under lock """
- sensors = make_copy(self.configman.read_config(["sensors"]))
- del sensors[id]
- self.configman.write_config(["sensors"], sensors)
- result = True
- except: self.logger.exception("Exception while delete sensor '"+str(id)+"'")
- return result
-
- def delete_sensor_device(self, sensor_id):
- result = False
- ids = []
- try:
- device_sensors = [sensor for sensor in self.sensors[sensor_id].sensor.get_device_sensors()]
- self.sensors[sensor_id].sensor.remove_device()
- except:
- self.logger.exception("Exception while delete device '"+str(sensor_id)+"'")
-
- try:
- """ This must not be under lock """
- sensors = make_copy(self.configman.read_config(["sensors"]))
- aliases = []
- for sensor in device_sensors:
- try:
- self.logger.debug("delete sensor '"+str(sensor.id)+"'")
- aliases.append(sensor.alias)
- del sensors[sensor.id]
- del self.sensors[sensor.id]
- except:
- self.logger.exception("Exception while delete sensor '"+str(sensor.id)+"'")
- self.configman.write_config(["sensors"], sensors)
- try:
- self.set_send_inform_flag(aliases, False)
- except:
- self.logger.exception("can't set inform flag")
- result = True
- except: self.logger.exception("Exception while delete device '"+str(sensor_id)+"'")
- return result
-
- def get_controller_state(self):
- sensor = self.get_sensor_by_alias("CNT")
- value = self.get_value_pair(sensor.id)[0]
- return self.get_value_description(sensor.id, value).get("caption", "n/a")
- def get_Sig(self, teploAPI=None):
- def get_sensor_state(alias):
- return self.get_sensor_state_by_alias(alias) == 'almaj'
-
- events_functions = {
- 0: [lambda: get_sensor_state("VIBlis")],
- 1: [lambda: self.get_sensor_value_by_alias("teploUB.pwr") in ("DISCHARGING", "SHUTDOWN")],
- 2: [lambda: get_sensor_state("DI1")],
- 3: [lambda: get_sensor_state("DI2")],
- 4: [lambda: get_sensor_state("DI3")],
- 5: [lambda: get_sensor_state("DI4")],
- 6: [lambda: get_sensor_state("teploUB.extDS1")],
- 7: [lambda: teploAPI is not None and not teploAPI.check_serial_numbers()],
- }
-
- sig = 0
- for bit, func_list in events_functions.items():
- sig |= any(func() for func in func_list) << bit
- return sig
- def get_sensor_description_by_alias(self, alias): #description
- sensor = self.get_sensor_by_alias(alias)
- value = self.get_value_pair(sensor.id)[0]
- return self.get_value_description(sensor.id, value).get("caption", "n/a")
-
- def get_sensor_state_by_alias(self, alias):
- sensor = self.get_sensor_by_alias(alias)
- return self.get_value_pair(sensor.id)[1]
-
- def get_sensor_value_by_alias(self, alias):
- sensor = self.get_sensor_by_alias(alias)
- return self.get_value_pair(sensor.id)[0]
-
- def find_free_alias(self, pattern):
- """ ToDo: merge with same function of event monitor (load_sensors) and move to configman """
- res = set(range(1, 999))
- try:
- self.logger.debug("Search for free aliases...")
- all_sensors = self.configman.read_config(["sensors"])
- for sensor in all_sensors.keys():
- try:
- self.logger.debug("matching sensor ID \'{0}\' alias \'{1}\'".format(sensor, all_sensors[sensor]["alias"]))
- matched = re.match(pattern, all_sensors[sensor]["alias"])
- if matched:
- self.logger.debug("\tAllready have sensor ID '"+sensor+"'")
- num = int(matched.group("num"), 10)
- self.logger.debug("removing {0}".format(num))
- if num in res:
- res.remove(num)
- except:
- self.logger.exception("Exception while scan aliases '"+str(sensor)+"'")
- except:
- self.logger.exception("Exception while read sensors configuration")
- return res # This is bad - on exception will return full range
-
- def set_send_inform_flag(self, aliases, flag):
- try:
- change_state_trigger = self.configman.read_config(self.STATE_CHANGE_TRIGGER_PATH, copy_=True)
- for alias in aliases:
- if len(change_state_trigger) == 0:
- change_state_trigger.append("or")
- trigger = [
- "sensor_state",
- alias
- ]
- if flag:
- if trigger not in change_state_trigger:
- change_state_trigger.append(trigger)
- else:
- try:
- change_state_trigger.remove(trigger)
- except Exception as e: self.logger.warning("No sensor for trigger "+str(trigger))
- self.configman.write_config(self.STATE_CHANGE_TRIGGER_PATH, change_state_trigger)
- except:
- self.logger.exception("set_send_snmp_flag exception")
|