diff options
Diffstat (limited to 'lib/taurus/core/tango/tangodatabase.py')
-rw-r--r-- | lib/taurus/core/tango/tangodatabase.py | 380 |
1 files changed, 380 insertions, 0 deletions
diff --git a/lib/taurus/core/tango/tangodatabase.py b/lib/taurus/core/tango/tangodatabase.py new file mode 100644 index 00000000..f6c501b7 --- /dev/null +++ b/lib/taurus/core/tango/tangodatabase.py @@ -0,0 +1,380 @@ +#!/usr/bin/env python + +############################################################################# +## +## This file is part of Taurus +## +## http://taurus-scada.org +## +## Copyright 2011 CELLS / ALBA Synchrotron, Bellaterra, Spain +## +## Taurus is free software: you can redistribute it and/or modify +## it under the terms of the GNU Lesser General Public License as published by +## the Free Software Foundation, either version 3 of the License, or +## (at your option) any later version. +## +## Taurus 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 Lesser General Public License for more details. +## +## You should have received a copy of the GNU Lesser General Public License +## along with Taurus. If not, see <http://www.gnu.org/licenses/>. +## +############################################################################# + +"""This module contains all taurus tango database""" + +__all__ = ["TangoDevInfo", "TangoServInfo", "TangoDatabaseCache", + "TangoDatabase" ] + +__docformat__ = "restructuredtext" + +import os + +import PyTango +import PyTango.utils + +from taurus import Factory +from taurus.core.taurusbasetypes import TaurusSWDevHealth +from taurus.core.taurusdatabase import TaurusDatabaseCache, TaurusDevInfo, \ + TaurusAttrInfo, TaurusServInfo, TaurusDevClassInfo, TaurusDevTree, \ + TaurusServerTree +from taurus.core.util.containers import CaselessDict +from taurus.core.taurusdatabase import TaurusDatabase + +InvalidAlias = "nada" + + +class TangoDevInfo(TaurusDevInfo): + + def __init__(self, container, name=None, full_name=None, alias=None, server=None, klass=None, exported=False, host=None): + super(TangoDevInfo, self).__init__(container, name=name, full_name=full_name, alias=alias, server=server, klass=klass, exported=exported, host=host) + self._alivePending = False + + def attributes(self): + if self._attributes is None or len(self._attributes) == 0: + self.refreshAttributes() + return self._attributes + + def getHWObj(self): + db = self.container().db + name = self.name() + full_name = db.getFullName() + "/" + name + dev = None + try: + dev = db.factory().getDevice(full_name).getHWObj() + except: + pass + return dev + + def alive(self): + if self._alive is None: + if self._alivePending: + return False + self._alivePending = True + try: + dev = self.getHWObj() + state = dev.state() + self._alive = True + except: + self._alive = False + self._alivePending = False + return self._alive + + def health(self): + """Overwrite health so it doesn't call 'alive()' since it can take + a long time for devices that are declared as exported but are in fact + not running (crached, network error, power cut, etc)""" + if not self._health is None: + return self._health + exported = self.exported() + if exported: + self._health = TaurusSWDevHealth.Exported + else: + self._health = TaurusSWDevHealth.NotExported + return self._health + + def refreshAttributes(self): + attrs = [] + try: + dev = self.getHWObj() + if dev is None: + raise PyTango.DevFailed() # @todo: check if this is the right exception to throw + attr_info_list = dev.attribute_list_query_ex() + for attr_info in attr_info_list: + full_name = "%s/%s" % (self.fullName(), attr_info.name) + attr_obj = TaurusAttrInfo(self.container(), + name=attr_info.name.lower(), full_name=full_name.lower(), + device=self, info=attr_info) + attrs.append(attr_obj) + attrs = sorted(attrs, key=lambda attr : attr.name()) + except PyTango.DevFailed as df: + if self.health() == TaurusSWDevHealth.Exported: + self._health = TaurusSWDevHealth.ExportedNotAlive + self.setAttributes(attrs) + + +class TangoServInfo(TaurusServInfo): + + def __init__(self, container, name=None, full_name=None): + super(TangoServInfo, self).__init__(container, name=name, full_name=full_name) + self._alivePending = False + + def alive(self): + if self._alive is None: + if self._alivePending: + return False + try: + self._alivePending = True + alive = True + for d in self.devices().values(): + alive = d.alive() + if not alive: + break + self._alive = alive + except Exception,e: + print "except",e + self._alive = False + self._alivePending = False + return self._alive + + +class TangoDatabaseCache(TaurusDatabaseCache): + + def refresh(self): + db = self.db + + query = "SELECT name, alias, exported, host, server, class FROM device" + + r = db.command_inout("DbMySqlSelect", query) + row_nb, column_nb = r[0][-2], r[0][-1] + results, data = r[0][:-2], r[1] + assert row_nb == len(data) / column_nb + + CD = CaselessDict + #CD = dict + dev_dict, serv_dict, klass_dict, alias_dict = CD(), {}, {}, CD() + + for i in xrange(0, len(data), column_nb): + name, alias, exported, host, server, klass = data[i:i+column_nb] + if name.count("/") != 2: continue # invalid/corrupted entry: just ignore it + if server.count("/") != 1: continue # invalid/corrupted entry: just ignore it + if not len(alias): alias = None + + serv_dict[server] = si = serv_dict.get(server, + TangoServInfo(self, name=server, + full_name=server)) + + klass_dict[klass] = dc = klass_dict.get(klass, + TaurusDevClassInfo(self, + name=klass, + full_name=klass)) + + full_name = "tango://%s/%s" % (db.getFullName(), name) + dev_dict[name] = di = TangoDevInfo(self, name=name, full_name=full_name, + alias=alias, server=si, klass=dc, + exported=exported, host=host) + + si.addDevice(di) + dc.addDevice(di) + if alias is not None: + alias_dict[alias] = di + + self._devices = dev_dict + self._device_tree = TaurusDevTree(dev_dict) + self._server_tree = TaurusServerTree(serv_dict) + self._servers = serv_dict + self._klasses = klass_dict + self._aliases = alias_dict + + def refreshAttributes(self, device): + attrs = [] + try: + db = self.db + name = device.name() + full_name = db.getFullName() + "/" + name + taurus_dev = db.factory().getExistingDevice(full_name) + if taurus_dev is None: + dev = PyTango.DeviceProxy(full_name) + else: + dev = taurus_dev.getHWObj() + attr_info_list = dev.attribute_list_query_ex() + for attr_info in attr_info_list: + full_attr_name = "%s/%s" % (full_name, attr_info.name) + attr_obj = TaurusAttrInfo(self, name=attr_info.name, + full_name=full_attr_name, + device=device, info=attr_info) + attrs.append(attr_obj) + attrs = sorted(attrs, key=lambda attr : attr.name().lower()) + except PyTango.DevFailed as df: + pass + device.setAttributes(attrs) + + +def get_home(): + """ + Find user's home directory if possible. Otherwise raise error. + + :return: user's home directory + :rtype: str + + New in PyTango 7.2.0 + """ + path='' + try: + path=os.path.expanduser("~") + except: + pass + if not os.path.isdir(path): + for evar in ('HOME', 'USERPROFILE', 'TMP'): + try: + path = os.environ[evar] + if os.path.isdir(path): + break + except: pass + if path: + return path + else: + raise RuntimeError('please define environment variable $HOME') + +def get_env_var(env_var_name): + """ + Returns the value for the given environment name + A backup method for old Tango/PyTango versions which don't implement + :meth:`PyTango.ApiUtil.get_env_var` + + Search order: + + * a real environ var + * HOME/.tangorc + * /etc/tangorc + + :param env_var_name: the environment variable name + :type env_var_name: str + :return: the value for the given environment name + :rtype: str + """ + + if env_var_name in os.environ: + return os.environ[env_var_name] + + fname = os.path.join(get_home(), '.tangorc') + if not os.path.exists(fname): + if os.name == 'posix': + fname = "/etc/tangorc" + if not os.path.exists(fname): + return None + + for line in file(fname): + strippedline = line.split('#',1)[0].strip() + + if not strippedline: + #empty line + continue + + tup = strippedline.split('=',1) + if len(tup) !=2: + # illegal line! + continue + + key, val = map(str.strip, tup) + if key == env_var_name: + return val + + +class TangoDatabase(TaurusDatabase): + + def __init__(self,host=None,port=None,parent=None): + pars = () + if host is None or port is None: + try: + host, port = TangoDatabase.get_default_tango_host().rsplit(':', 1) + pars = host, port + except Exception, e: + print "Error getting env TANGO_HOST:", str(e) + else: + pars = host, port + self.dbObj = PyTango.Database(*pars) + self._dbProxy = None + self._dbCache = None + + complete_name = "%s:%s" % (host, port) + self.call__init__(TaurusDatabase, complete_name, parent) + + try: + self.get_class_for_device(self.dev_name()) + except: + # Ok, old tango database. + self.get_class_for_device = self.__get_class_for_device + + @staticmethod + def get_default_tango_host(): + if hasattr(PyTango.ApiUtil, "get_env_var"): + f = PyTango.ApiUtil.get_env_var + else: + f = get_env_var + return f("TANGO_HOST") + + def __get_class_for_device(self, dev_name): + """Backup method when connecting to tango 5 database device server""" + # Ok, old tango database. + serv_name = self.command_inout("DbGetDeviceInfo",dev_name)[1][3] + devs = self.get_device_class_list(serv_name) + dev_name_lower = dev_name.lower() + for i in xrange(len(devs)/2): + idx = i*2 + if devs[idx].lower() == dev_name_lower: + return devs[idx+1] + return None + + def get_device_attribute_list(self, dev_name, wildcard): + return self.command_inout("DbGetDeviceAttributeList", (dev_name, wildcard)) + + # Export the PyTango.Database interface into this object. + # This way we can call for example get_attribute_property on an object of this class + def __getattr__(self, name): + if not self.dbObj is None: + return getattr(self.dbObj,name) + return None + + #-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~- + # TaurusModel implementation + #-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~- + + # helper class property that stores a reference to the corresponding factory + _factory = None + + @classmethod + def factory(cls): + if cls._factory is None: + cls._factory = Factory(scheme='tango') + return cls._factory + + def getValueObj(self,cache=True): + return self.dbObj + + #-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~- + # Mandatory methods to overwrite from TaurusDatabase + #-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~- + + def cache(self): + if self._dbCache is None: + self._dbCache = TangoDatabaseCache(self) + return self._dbCache + + def getElementAlias(self, full_name): + try: + alias = self.getValueObj().get_alias(full_name) + if alias and alias.lower() == InvalidAlias: + alias = None + except: + alias = None + return alias + + def getElementFullName(self, alias): + try: + return self.getValueObj().get_device_alias(alias) + except: + pass + return None |