# This file is part of nexdatas - Tango Server for NeXus data writer
#
# Copyright (C) 2012-2018 DESY, Jan Kotanski <jkotan@mail.desy.de>
#
# nexdatas 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.
#
# nexdatas 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 nexdatas. If not, see <http://www.gnu.org/licenses/>.
#
""" Command-line tool for creating to the nexdatas configuration server """
import copy
import os.path
import json
import sys
from operator import itemgetter
import lxml.etree as etree
import xml.etree.ElementTree as et
from lxml.etree import XMLParser
from nxstools.nxsdevicetools import (
storeDataSource, getDataSourceComponents, storeComponent,
moduleAttributes, motorModules,
generateDeviceNames, getServerTangoHost,
openServer, findClassName,
xmlPackageHandler)
from nxstools.nxsxml import (XMLFile, NDSource, NGroup, NField, NLink,
NAttr, NDimensions)
from nxstools.pyeval.secop import secop_cmd
#: (:obj:`bool`) True if PyTango available
PYTANGO = False
try:
try:
import tango
except Exception:
import PyTango as tango
PYTANGO = True
except Exception:
pass
if sys.version_info > (3,):
basestring = str
unicode = str
npTn = {"double": "NX_FLOAT64",
"int": "NX_INT64",
"string": "NX_CHAR",
"bool": "NX_BOOLEAN"}
mnTme = {
"temperature": "temperature",
"temperature_regulation": "temperature",
"magneticfield": "magnetic_field",
"electricfield": "electric_field",
"pressure": "pressure",
"flowrate": "flow",
# not defined in NeXus yet
# "humidity": "humidity",
# "viscosity": "viscosity",
# "concentration": "concentration",
# "rotation_z": "rotation_z",
# not defined in secop yet
"ph": "pH",
"conductivity": "conductivity",
"resistance": "resistance",
"voltage": "voltage",
"surfacepressure": "surface_pressure",
"stress": "stress",
"strain": "strain",
"shear": "shear",
}
[docs]class CPExistsException(Exception):
""" Component already exists exception
"""
[docs]class DSExistsException(Exception):
""" DataSource already exists exception
"""
def _tostr(text):
""" converts text to str type
:param text: text
:type text: :obj:`bytes` or :obj:`unicode`
:returns: text in str type
:rtype: :obj:`str`
"""
if hasattr(text, "tostring"):
text = text.tostring()
if isinstance(text, str):
return text
elif sys.version_info > (3,):
return str(text, encoding="utf8")
else:
return str(text)
def _toxml(node):
""" provides xml content of the whole node
:param node: DOM node
:type node: :class:`xml.dom.Node`
:returns: xml content string
:rtype: :obj:`str`
"""
if sys.version_info > (3,):
xml = _tostr(et.tostring(node, encoding='unicode', method='xml'))
else:
xml = _tostr(et.tostring(node, encoding='utf8', method='xml'))
if xml.startswith("<?xml version='1.0' encoding='utf8'?>"):
xml = str(xml[38:])
return xml
def _simpletoxml(node):
""" provides xml content of the whole node
:param node: DOM node
:type node: :class:`xml.dom.Node`
:returns: xml content string
:rtype: :obj:`str`
"""
if sys.version_info > (3,):
return _tostr(et.tostring(node, encoding='unicode', method='xml'))
else:
return _tostr(et.tostring(node, encoding='utf8', method='xml'))
[docs]class Device(object):
""" device from online.xml
"""
__slots__ = [
'name', 'dtype', 'module', 'tdevice', 'sdevice',
'hostname', 'sardananame', 'sardanahostname',
'host', 'port', 'shost', 'sport', 'thost', 'tport',
'group', 'attribute']
def __init__(self):
#: (:obj:`str`) device name
self.name = None
#: (:obj:`str`) device type
self.dtype = None
#: (:obj:`str`) device module
self.module = None
#: (:obj:`str`) tango device name
self.tdevice = None
#: (:obj:`str`) sardana device name
self.sdevice = None
#: (:obj:`str`) host name with port
self.hostname = None
#: (:obj:`str`) sardana name with port
self.sardananame = None
#: (:obj:`str`) sardana host name
self.sardanahostname = None
#: (:obj:`str`) host without port
self.host = None
#: (:obj:`str`) port
self.port = None
#: (:obj:`str`) tango host without port
self.thost = None
#: (:obj:`str`) tango port
self.tport = None
#: (:obj:`str`) sardana host without port
self.shost = None
#: (:obj:`str`) sardana tango port
self.sport = None
#: (:obj:`str`) datasource tango group
self.group = None
#: (:obj:`str`) attribute name
self.attribute = None
[docs] def compare(self, dv):
dct = {}
tocompare = [
"name", "dtype", "module", "tdevice", "hostname",
"sardananame", "sardanahostname"]
for at in tocompare:
v1 = getattr(self, at)
v2 = getattr(dv, at)
if v1 != v2:
dct[at] = (str(v1) if v1 else v1, str(v2) if v2 else v2)
return dct
[docs] def tolower(self):
""" converts `name`, `module`, `tdevice`, `hostname` into lower case
"""
if self.name:
self.name = self.name.lower()
if self.module:
self.module = self.module.lower()
if self.tdevice:
self.tdevice = self.tdevice.lower()
if self.hostname:
self.hostname = self.hostname.lower()
[docs] def splitHostPort(self):
""" spilts host name from port
"""
if self.hostname:
self.host = self.hostname.split(":")[0]
self.port = self.hostname.split(":")[1] \
if len(self.hostname.split(":")) > 1 else None
self.thost = self.host
self.tport = self.port
else:
self.host = None
self.port = None
raise Exception("hostname not defined")
[docs] def findDevice(self, tangohost):
""" sets sardana device name and sardana host/port of online.xml device
:param tangohost: tango host
:type tangohost: :obj:`str`
"""
mhost = self.sardanahostname or tangohost
self.sdevice = None
self.shost = None
self.sport = None
if PYTANGO:
try:
dp = tango.DeviceProxy(str("%s/%s" % (mhost, self.name)))
mdevice = str(dp.name())
# self.hostname = mhost
self.shost = mhost.split(":")[0]
if len(mhost.split(":")) > 1:
self.sport = mhost.split(":")[1]
self.sdevice = mdevice
except Exception:
pass
[docs] def findAttribute(self, tangohost, clientlike=False):
""" sets attribute and datasource group of online.xml device
:param tangohost: tango host
:type tangohost: :obj:`str`
:param clientlike: tango motors to be client like
:type clientlike: :obj:`bool`
"""
mhost = self.sardanahostname or tangohost
self.group = None
self.attribute = None
spdevice = self.tdevice.split("/")
if mhost and len(spdevice) > 3:
self.attribute = spdevice[3]
self.tdevice = "/".join(spdevice[0:3])
self.hostname = mhost
if self.module in motorModules or self.dtype == 'stepping_motor':
if self.attribute is None:
self.attribute = 'Position'
if clientlike:
self.group = '__CLIENT__'
elif PYTANGO and self.module in moduleAttributes:
try:
try:
dp = tango.DeviceProxy(
str("%s/%s" % (mhost, self.sardananame)))
except Exception:
dp = tango.DeviceProxy(str("%s/%s" % (mhost, self.name)))
mdevice = str(dp.name())
sarattr = moduleAttributes[self.module][0]
if not sarattr or \
sarattr not in dp.get_attribute_list():
raise Exception("Missing attribute: Value")
self.hostname = mhost
self.host = mhost.split(":")[0]
self.shost = self.host
if len(mhost.split(":")) > 1:
self.port = mhost.split(":")[1]
self.sport = self.port
if mdevice:
self.sdevice = mdevice
self.attribute = sarattr
self.group = '__CLIENT__'
except Exception:
if moduleAttributes[self.module][1]:
if self.attribute is None:
self.attribute = moduleAttributes[self.module][1]
self.group = '__CLIENT__'
[docs] def setSardanaName(self, tolower):
""" sets sardana name
:param tolower: If True name in lowercase
:type tolower: :obj:`bool`
"""
self.name = self.sardananame or self.name
if tolower:
self.name = self.name.lower()
[docs]class Creator(object):
""" configuration server adapter
"""
def __init__(self, options, args, printouts=True):
""" constructor
:param options: command options
:type options: :class:`optparse.Values`
:param args: command arguments
:type args: :obj:`list` < :obj:`str` >
:param printouts: if printout is enable
:type printouts: :obj:`bool`
"""
#: (:class:`optparse.Values`) creator options
self.options = options
#: (:obj:`list` < :obj:`str` >) creator arguments
self.args = args
#: (:obj:`bool`) if printout is enable
self._printouts = printouts
@classmethod
def _areComponentsAvailable(cls, names, server, lower=False):
""" checks if the components are available
:param names: component names
:type names: :obj:`list` < :obj:`str` >
:param server: server name
:type server: :obj:`str`
:param lower: checks lower case name
:type lower: :obj:`bool`
:returns: a subset of available components
:rtype: :obj:`list` < :obj:`str` >
"""
try:
proxy = openServer(server)
proxy.Open()
acps = proxy.availableComponents()
except Exception:
raise Exception("Cannot connect to %s" % server)
cps = []
for name in names:
if name in acps or (lower and name.lower() in acps):
cps.append(name)
return cps
@classmethod
def _componentFilesExist(cls, names, fileprefix, directory):
""" checks if the components files exist
:param names: component names
:type names: :obj:`list` < :obj:`str` >
:param fileprefix: file name prefix
:type fileprefix: :obj:`str`
:param directory: file directory
:type directory: :obj:`str`
:param lower: checks lower case name
:type lower: :obj:`bool`
:returns: a subset of available components
:rtype: :obj:`list` < :obj:`str` >
"""
cps = []
for name in names:
fname = "%s/%s%s.xml" % (directory, fileprefix, name)
if os.path.exists(fname):
cps.append(fname)
return cps
@classmethod
def _areDataSourcesAvailable(cls, names, server, lower=False):
""" checks if the datasources are available
:param names: datasource names
:type names: :obj:`list` < :obj:`str` >
:param server: server name
:type server: :obj:`str`
:param lower: checks lower case name
:type lower: :obj:`bool`
:returns: a subset of available datasources
:rtype: :obj:`list` < :obj:`str` >
"""
try:
proxy = openServer(server)
proxy.Open()
adss = proxy.availableDataSources()
except Exception:
raise Exception("Cannot connect to %s" % server)
dss = []
for name in names:
if name in adss or (lower and name.lower() in adss):
dss.append(name)
return dss
@classmethod
def _dataSourceFilesExist(cls, names, fileprefix, directory):
""" checks if the datasources files exist
:param names: datasource names
:type names: :obj:`list` < :obj:`str` >
:param fileprefix: file name prefix
:type fileprefix: :obj:`str`
:param directory: file directory
:type directory: :obj:`str`
:param lower: checks lower case name
:type lower: :obj:`bool`
:returns: a subset of available datasources
:rtype: :obj:`list` < :obj:`str` >
"""
dss = []
for name in names:
fname = "%s/%s%s.ds.xml" % (directory, fileprefix, name)
if os.path.exists(fname):
dss.append(fname)
return dss
@classmethod
def _createTangoDataSource(
cls, name, directory, fileprefix, server, device,
elementname, host, port="10000", group=None, elementtype=None):
""" creates TANGO datasource file
:param name: device name
:type name: :obj:`str`
:param directory: output file directory
:type directory: :obj:`str`
:param fileprefix: file name prefix
:type fileprefix: :obj:`str`
:param server: server name
:type server: :obj:`str`
:param device: device name
:type device: :obj:`str`
:param elementname: element name, e.g. attribute name
:type elementname: :obj:`str`
:param host: tango host name
:type host: :obj:`str`
:param port: tango port
:type port: :obj:`str`
:parma group: datasource tango group
:type group: :obj:`str`
:parma elementtype: element type, i.e. attribute, property or command
:type elementtype: :obj:`str`
:returns: xml string
:rtype: :obj:`str`
"""
df = XMLFile("%s/%s%s.ds.xml" % (directory, fileprefix, name))
sr = NDSource(df)
sr.initTango(name, device, elementtype or "attribute",
elementname, host, port, group=group)
xml = df.prettyPrint()
if server:
storeDataSource(name, xml, server)
elif directory is not None and fileprefix is not None:
df.dump()
return xml
@classmethod
def _createClientDataSource(
cls, name, directory, fileprefix, server, dsname=None):
""" creates CLIENT datasource file
:param name: device name
:type name: :obj:`str`
:param directory: output file directory
:type directory: :obj:`str`
:param fileprefix: file name prefix
:type fileprefix: :obj:`str`
:param server: server name
:type server: :obj:`str`
:param dsname: datasource name
:type dsname: :obj:`str`
:returns: xml string
:rtype: :obj:`str`
"""
dname = name if not dsname else dsname
df = XMLFile("%s/%s%s.ds.xml" % (directory, fileprefix, dname))
print("%s/%s%s.ds.xml" % (directory, fileprefix, dname))
sr = NDSource(df)
sr.initClient(dname, name)
xml = df.prettyPrint()
if server:
storeDataSource(dname, xml, server)
elif directory is not None and fileprefix is not None:
df.dump()
return xml
@classmethod
def __patheval(cls, nexuspath):
""" splits nexus path into list
:param nexuspath: nexus path
:type nexuspath: :obj:`str`
:returns: nexus path in lists of (name, NXtype)
:rtype: :obj:`list` < (:obj:`str`, :obj:`str`) >
"""
pathlist = []
spath = nexuspath.split("/")
if spath:
for sp in spath[:-1]:
nlist = sp.split(":")
if len(nlist) == 2:
if len(nlist[0]) == 0 and \
len(nlist[1]) > 2 and nlist[1].startswith("NX"):
pathlist.append((nlist[1][2:], nlist[1]))
else:
pathlist.append((nlist[0], nlist[1]))
elif len(nlist) == 1 and nlist[0]:
if len(nlist[0]) > 2 and nlist[0].startswith("NX"):
pathlist.append((nlist[0][2:], nlist[0]))
else:
pathlist.append((nlist[0], "NX" + nlist[0]))
pathlist.append((spath[-1], None))
return pathlist
@classmethod
def __createTree(cls, df, nexuspath, name, nexusType,
strategy, units, link, chunk, canfail=None,
depends=None):
""" create nexus node tree
:param df: definition parent node
:type df: :class:'nxstools.nxsxml.XMLFile'
:param nexuspath: nexus path
:type nexuspath: :obj:`str`
:param name: name
:type name: :obj:`str`
:param nexusType: nexus type
:type nexusType: :obj:`str`
:param strategy: strategy mode
:type startegy: :obj:`str`
:param units: field units
:type units: :obj:`str`
:param links: if create link
:type links: :obj:`bool`
:param chunk: chunk size, e.g. `SCALAR`, `SPECTRUM` or `IMAGE`
:type chunk: :obj:`str`
:param canfail: can fail strategy flag
:type canfail: :obj:`bool`
:param depends: a list of component dependencies separated by ','
:type depends: :obj:`str`
"""
pathlist = cls.__patheval(nexuspath)
entry = None
parent = df
for path in pathlist[:-1]:
child = NGroup(parent, path[0], path[1])
if parent == df:
entry = child
parent = child
if pathlist:
fname = pathlist[-1][0] or name
field = NField(parent, fname, nexusType)
field.setStrategy(strategy, canfail=canfail)
if units.strip():
field.setUnits(units.strip())
field.setText("$datasources.%s" % name)
if chunk != 'SCALAR':
if chunk == 'SPECTRUM':
NDimensions(field, "1")
elif chunk == 'IMAGE':
NDimensions(field, "2")
if link and entry:
npath = (nexuspath + name) \
if nexuspath[-1] == '/' else nexuspath
data = NGroup(entry, "data", "NXdata")
if link > 1:
NLink(data, name, npath)
else:
NLink(data, fname, npath)
if depends:
deps = [dp for dp in depends.split(",") if dp]
if deps:
df.setDependencies(deps, entry)
@classmethod
def _createComponent(cls, name, directory, fileprefix, nexuspath,
strategy, nexusType, units, links, server, chunk,
dsname, canfail=None, depends=None):
""" creates component file
:param name: component name
:type name: :obj:`str`
:param directory: output file directory
:type directory: :obj:`str`
:param fileprefix: file name prefix
:type fileprefix: :obj:`str`
:param nexuspath: nexus path
:type nexuspath: :obj:`str`
:param strategy: field strategy
:type startegy: :obj:`str`
:param nexusType: nexus Type of the field
:type nexusType: :obj:`str`
:param units: field units
:type units: :obj:`str`
:param link: nxdata link
:type links: :obj:`bool`
:param server: configuration server
:type server: :obj:`str`
:returns: component xml
:rtype: :obj:`str`
:param dsname: datasource name
:type dsname: :obj:`str`
:param canfail: can fail strategy flag
:type canfail: :obj:`bool`
:param depends: a list of component dependencies separated by ','
:type depends: :obj:`str`
"""
defpath = "/$var.entryname#'scan'$var.serialno:NXentry/instrument" \
+ "/collection/%s" % (dsname or name)
df = XMLFile("%s/%s%s.xml" % (directory, fileprefix, name))
cls.__createTree(df, nexuspath or defpath, dsname or name, nexusType,
strategy, units, links, chunk, canfail, depends)
xml = df.prettyPrint()
if server:
storeComponent(name, xml, server)
elif directory is not None and fileprefix is not None:
df.dump()
return xml
@classmethod
def _getText(cls, node):
""" provides xml content of the node
:param node: DOM node
:type node: :class:`lxml.etree.Element`
:returns: xml content string
:rtype: :obj:`str`
"""
if node is None:
return
xml = _toxml(node)
start = xml.find('>')
end = xml.rfind('<')
if start == -1 or end < start:
return ""
return xml[start + 1:end].replace("<", "<").replace(">", "<"). \
replace(""", "\"").replace("&", "&")
@classmethod
def _getChildText(cls, parent, childname):
""" provides text of child named by childname
:param parent: parent node
:type parent: :class:`lxml.etree.Element`
:param childname: child name
:type childname: :opj:`str`
:returns: text string
:rtype: :obj:`str`
"""
children = parent.findall(childname)
return cls._getText(children[0]) if len(children) else None
def _getModuleName(self, device):
""" provides module name
:param device: device name
:type device: :obj:`str`
:returns: module name
:rtype: :obj:`str`
"""
if device.module.lower() in \
self.xmlpackage.moduleMultiAttributes.keys():
return device.module.lower()
elif len(device.tdevice.split('/')) == 3:
try:
classname = findClassName(device.hostname, device.tdevice)
if classname.lower() \
in self.xmlpackage.moduleMultiAttributes.keys():
return classname.lower()
except Exception:
pass
if device.module.lower() == 'module_tango' \
and len(device.tdevice.split('/')) == 3 \
and device.tdevice.split('/')[1] \
in self.xmlpackage.moduleMultiAttributes.keys():
return device.tdevice.split('/')[1].lower()
[docs]class WrongParameterError(Exception):
""" wrong parameter exception
"""
pass
[docs]class ComponentCreator(Creator):
""" component creator
"""
[docs] def create(self):
""" creates a component xml and stores it in DB or filesytem
"""
aargs = []
if self.options.device.strip():
try:
first = int(self.options.first)
except Exception:
raise WrongParameterError(
"ComponentCreator Invalid --first parameter\n")
try:
last = int(self.options.last)
except Exception:
raise WrongParameterError(
"ComponentCreator Invalid --last parameter\n")
aargs = generateDeviceNames(self.options.device, first, last,
self.options.minimal)
if self.options.datasource:
dsargs = generateDeviceNames(
self.options.datasource, first, last,
self.options.minimal) or None
else:
dsargs = None
else:
dsargs = None
self.args += aargs
if not len(self.args):
raise WrongParameterError("")
if dsargs is None and self.options.datasource:
dsargs = [self.options.datasource]
if dsargs is not None and len(self.args) != len(dsargs):
raise WrongParameterError(
"component names cannot be match into datasource namse")
if self.options.database:
if not self.options.overwrite:
existing = self._areComponentsAvailable(
self.args, self.options.server)
if existing:
raise CPExistsException(
"Components '%s' already exist." % existing)
elif not self.options.overwrite:
existing = self._componentFilesExist(
self.args, self.options.file, self.options.directory)
if existing:
raise CPExistsException(
"Component files '%s' already exist." % existing)
for i, name in enumerate(self.args):
dsname = dsargs[i] if dsargs is not None else None
if not self.options.database:
if self._printouts:
print("CREATING: %s%s.xml" % (self.options.file, name))
else:
if self._printouts:
print("STORING: %s" % (name))
self._createComponent(
name, self.options.directory,
self.options.file,
self.options.nexuspath,
self.options.strategy,
self.options.type,
self.options.units,
int(self.options.fieldlinks) + 2 * int(
self.options.sourcelinks),
self.options.server if self.options.database else None,
self.options.chunk, dsname,
self.options.canfail,
self.options.depends
)
[docs]class TangoDSCreator(Creator):
""" tango datasource creator
"""
[docs] def create(self):
""" creates a tango datasource xml and stores it in DB or filesytem
"""
dvargs = []
dsargs = []
if self.options.device.strip():
try:
last = int(self.options.last)
try:
first = int(self.options.first)
except Exception:
first = 1
dvargs = generateDeviceNames(
self.options.device, first, last)
dsargs = generateDeviceNames(
self.options.datasource, first, last)
except Exception:
dvargs = [str(self.options.device)]
dsargs = [str(self.options.datasource)]
if not dvargs or not len(dvargs):
raise WrongParameterError("")
if self.options.database:
if not self.options.overwrite:
existing = self._areDataSourcesAvailable(
dsargs, self.options.server)
if existing:
raise DSExistsException(
"DataSources '%s' already exist." % existing)
elif not self.options.overwrite:
existing = self._dataSourceFilesExist(
dsargs, self.options.file, self.options.directory)
if existing:
raise DSExistsException(
"DataSource files '%s' already exist." % existing)
for i in range(len(dvargs)):
if not self.options.database:
print("CREATING %s: %s%s.ds.xml" % (
dvargs[i], self.options.file, dsargs[i]))
else:
print("STORING %s: %s" % (dvargs[i], dsargs[i]))
self._createTangoDataSource(
dsargs[i], self.options.directory, self.options.file,
self.options.server if self.options.database else None,
dvargs[i],
self.options.attribute,
self.options.host,
self.options.port,
self.options.group or None,
self.options.elementtype or "attribute"
)
[docs]class ClientDSCreator(Creator):
""" client datasource creator
"""
[docs] def create(self):
""" creates a client datasource xml and stores it in DB or filesytem
"""
dsargs = None
aargs = []
if self.options.device.strip():
try:
first = int(self.options.first)
except Exception:
raise WrongParameterError(
"ClientDSCreator: Invalid --first parameter\n")
try:
last = int(self.options.last)
except Exception:
raise WrongParameterError(
"ClientDSCreator: Invalid --last parameter\n")
aargs = generateDeviceNames(self.options.device, first, last,
self.options.minimal)
if self.options.dsource:
dsaargs = generateDeviceNames(
self.options.dsource, first, last)
dsargs = list(self.args) + dsaargs
self.args += aargs
if not dsargs:
dsargs = self.args
if not len(self.args):
raise WrongParameterError("")
if self.options.database:
if not self.options.overwrite:
existing = self._areDataSourcesAvailable(
dsargs, self.options.server)
if existing:
raise DSExistsException(
"DataSources '%s' already exist." % existing)
elif not self.options.overwrite:
existing = self._dataSourceFilesExist(
dsargs, self.options.file, self.options.directory)
if existing:
raise DSExistsException(
"DataSource files '%s' already exist." % existing)
for i in range(len(self.args)):
if not self.options.database:
print("CREATING: %s%s.ds.xml" % (
self.options.file, dsargs[i]))
else:
print("STORING: %s" % (dsargs[i]))
self._createClientDataSource(
self.args[i], self.options.directory,
self.options.file,
self.options.server if self.options.database else None,
dsargs[i])
[docs]class DeviceDSCreator(Creator):
""" device datasource creator
"""
[docs] def create(self):
""" creates a tango datasources xml of given device
and stores it in DB or filesytem
"""
if self.options.database:
if not self.options.overwrite:
dsargs = [
"%s%s" % (self.options.datasource.lower(), at.lower())
for at in self.args
]
existing = self._areDataSourcesAvailable(
dsargs, self.options.server)
if existing:
raise DSExistsException(
"DataSources '%s' already exist." % existing)
elif not self.options.overwrite:
dsargs = [
"%s%s" % (self.options.datasource.lower(), at.lower())
for at in self.args
]
existing = self._dataSourceFilesExist(
dsargs, self.options.file, self.options.directory)
if existing:
raise DSExistsException(
"DataSource files '%s' already exist." % existing)
for at in self.args:
dsname = "%s%s" % (self.options.datasource.lower(), at.lower())
if not self.options.database:
if self._printouts:
print("CREATING %s/%s: %s%s.ds.xml" % (
self.options.device, at, self.options.file, dsname))
else:
if self._printouts:
print("STORING %s/%s: %s" % (
self.options.device, at, dsname))
self._createTangoDataSource(
dsname, self.options.directory, self.options.file,
self.options.server if self.options.database else None,
self.options.device, at, self.options.host,
self.options.port,
self.options.datasource
if not self.options.nogroup else None)
[docs]class PoolDSCreator(Creator):
""" datasource creator of all sardana pool acquisition channels
"""
def __init__(self, options, args, printouts=True):
""" constructor
:param options: command options
:type options: :class:`optparse.Values`
:param args: command arguments
:type args: :obj:`list` <:obj:`str` >
:param printouts: if printout is enable
:type printouts: :obj:`bool`
"""
Creator.__init__(self, options, args, printouts)
#: (:obj:`dict` <:obj:`str`, :obj:`str` >) datasource xml dictionary
self.datasources = {}
def _printAction(self, dv, dscps=None):
""" prints out information about the performed action
:param dv: online device object
:type dv: :class:`Device`
:param dscps: datasource components
:type dscps: :obj:`dict` <:obj:`str`, :obj:`list` < :obj:`str` > >
"""
if self._printouts:
if hasattr(self.options, "directory") and \
self.options.directory:
print("CREATING %s: %s/%s%s.ds.xml" % (
dv.tdevice, self.options.directory,
self.options.file, dv.name))
elif self.options.database:
print("CREATING %s %s/%s/%s %s" % (
str(dv.name) + ":" + " " * (34 - len(dv.name or "")),
dv.hostname,
dv.tdevice,
str(dv.attribute) + " " * (
70 - len(dv.tdevice or "") - len(dv.attribute or "")
- len(dv.hostname or "")),
",".join(dscps[dv.name])
if (dscps and dv.name in dscps and dscps[dv.name])
else ""))
else:
print("TEST %s %s/%s/%s %s" % (
str(dv.name) + ":" + " " * (34 - len(dv.name or "")),
dv.hostname,
dv.tdevice,
str(dv.attribute) + " " * (
70 - len(dv.tdevice or "") - len(dv.attribute or "")
- len(dv.hostname or "")),
",".join(dscps[dv.name])
if (dscps and dv.name in dscps and dscps[dv.name])
else ""))
[docs] def create(self):
""" creates datasources of all online.xml simple devices
"""
self.createXMLs()
server = self.options.server
if not hasattr(self.options, "directory") or \
not self.options.directory:
if self.options.database:
for dsname, dsxml in self.datasources.items():
storeDataSource(dsname, dsxml, server)
else:
for dsname, dsxml in self.datasources.items():
with open("%s/%s%s.ds.xml" % (
self.options.directory,
self.options.file, dsname), "w") as myfile:
myfile.write(dsxml)
def __createDevice(self, name, source, clientlike=True):
""" create Device from name source and chtype
:param name: alias device name
:type name: :obj:`str`
:param source: device source string
:type source: :obj:`str`
:param clientlike: device to be client like
:type clientlike: :obj:`bool`
:returns: device object
:rtype: :obj:`Device`
"""
dv = Device()
if name and source:
if source.startswith("tango://"):
source = source[8:]
slst = source.split('/')
if not slst[0] or ":" not in slst[0] or len(slst) < 2:
return dv
hplst = slst[0].split(":")
if len(hplst) != 2:
return dv
dv.host, dv.port = hplst[0], hplst[1]
dv.hostname = slst[0]
dv.attribute = slst[-1]
dv.name = name
dv.tdevice = "/".join(slst[1:-1])
if clientlike:
dv.group = "__CLIENT__"
return dv
[docs] def createXMLs(self):
""" creates datasource xmls of all online.xml simple devices
"""
self.datasources = {}
plname = self.options.pool
motors = ['Motor', 'PseudoMotor']
expchnls = ['CTExpChannel', 'ZeroDExpChannel',
'OneDExpChannel', 'TwoDExpChannel',
'PseudoCounter']
if 'ALL' in self.args or not self.args:
args = list(motors)
args.extend(expchnls)
else:
args = list(self.args)
try:
plproxy = openServer(plname)
except Exception:
raise Exception("Cannot connect to %s" % plname)
try:
chlist = plproxy.ExpChannelList
except Exception:
chlist = []
try:
motorlist = plproxy.MotorList
except Exception:
motorlist = []
for ellist in [chlist, motorlist]:
for els in ellist:
elprop = json.loads(els) or {}
if 'name' in elprop.keys() \
and 'source' in elprop.keys() \
and 'type' in elprop.keys():
if elprop['name'] in args or elprop['type'] in args:
dv = self.__createDevice(
elprop['name'], elprop['source'],
self.options.clientlike)
if self.options.lower:
dv.tolower()
if dv.tdevice and dv.attribute and dv.host and dv.port:
self._printAction(dv)
xml = self._createTangoDataSource(
dv.name, None, None, None,
dv.tdevice, dv.attribute, dv.host,
dv.port, dv.group)
self.datasources[dv.name] = xml
[docs]class OnlineDSCreator(Creator):
""" datasource creator of all online.xml simple devices
"""
def __init__(self, options, args, printouts=True):
""" constructor
:param options: command options
:type options: :class:`optparse.Values`
:param args: command arguments
:type args: :obj:`list` <:obj:`str` >
:param printouts: if printout is enable
:type printouts: :obj:`bool`
"""
Creator.__init__(self, options, args, printouts)
#: (:obj:`dict` <:obj:`str`, :obj:`str` >) datasource xml dictionary
self.datasources = {}
if options.xmlpackage:
xmlPackageHandler.loadXMLTemplates(options.xmlpackage)
else:
xmlPackageHandler.loadXMLTemplates('nxstools.xmltemplates')
#: (:obj:`str`) xml template component package path
self.xmltemplatepath = xmlPackageHandler.packagepath
#: (:obj:`str`) xml template component package
self.xmlpackage = xmlPackageHandler.package
def _printAction(self, dv, dscps=None):
""" prints out information about the performed action
:param dv: online device object
:type dv: :class:`Device`
:param dscps: datasource components
:type dscps: :obj:`dict` <:obj:`str`, :obj:`list` < :obj:`str` > >
"""
if self._printouts:
if hasattr(self.options, "directory") and \
self.options.directory:
print("CREATING %s: %s/%s%s.ds.xml" % (
dv.tdevice, self.options.directory,
self.options.file, dv.name))
elif self.options.database:
print("CREATING %s %s/%s/%s %s" % (
str(dv.name) + ":" + " " * (34 - len(dv.name or "")),
dv.hostname,
dv.tdevice,
str(dv.attribute) + " " * (
70 - len(dv.tdevice or "") - len(dv.attribute or "")
- len(dv.hostname or "")),
",".join(dscps[dv.name])
if (dscps and dv.name in dscps and dscps[dv.name])
else ""))
else:
print("TEST %s %s %s %s/%s/%s %s" % (
str(dv.name) + ":" + " " * (34 - len(dv.name or "")),
str(dv.dtype) + ":" + " " * (20 - len(dv.dtype or "")),
str(dv.module) + ":" + " " * (24 - len(dv.module or "")),
dv.hostname,
dv.tdevice,
str(dv.attribute) + " " * (
70 - len(dv.tdevice or "") - len(dv.attribute or "")
- len(dv.hostname or "")),
",".join(dscps[dv.name])
if (dscps and dv.name in dscps and dscps[dv.name])
else ""))
[docs] def create(self):
""" creates datasources of all online.xml simple devices
"""
self.createXMLs()
server = self.options.server
if not hasattr(self.options, "directory") or \
not self.options.directory:
if self.options.database:
for dsname, dsxml in self.datasources.items():
storeDataSource(dsname, dsxml, server)
else:
for dsname, dsxml in self.datasources.items():
with open("%s/%s%s.ds.xml" % (
self.options.directory,
self.options.file, dsname), "w") as myfile:
myfile.write(dsxml)
[docs] def createXMLs(self):
""" creates datasource xmls of all online.xml simple devices
"""
self.datasources = {}
tangohost = getServerTangoHost(
self.options.external or self.options.server)
hw = etree.parse(self.args[0],
parser=XMLParser(collect_ids=False)).getroot()
if hw.tag != 'hw':
hw = hw.find('hw')
dscps = {}
if self.options.server and self._printouts and \
(
not hasattr(self.options, "directory") or
not self.options.directory
):
try:
dscps = getDataSourceComponents(
self.options.server, self.options.verbose)
except Exception:
dscps = {}
for device in hw:
if device.tag == 'device':
dv = Device()
dv.name = self._getChildText(device, "name")
dv.dtype = self._getChildText(device, "type")
dv.module = self._getChildText(device, "module")
dv.tdevice = self._getChildText(device, "device")
dv.hostname = self._getChildText(device, "hostname")
dv.sardananame = self._getChildText(device, "sardananame")
dv.sardanahostname = self._getChildText(
device, "sardanahostname")
if self.options.lower:
dv.tolower()
try:
dv.splitHostPort()
except Exception:
if self._printouts:
print("ERROR %s: host for module %s of %s "
"type not defined"
% (dv.name, dv.module, dv.dtype))
continue
dv.findAttribute(tangohost, self.options.clientlike)
created = False
if dv.attribute:
dv.setSardanaName(self.options.lower)
mdv = copy.copy(dv)
mdv.tdevice = dv.sdevice or dv.tdevice
self._printAction(mdv, dscps)
xml = self._createTangoDataSource(
mdv.name, None, None, None,
mdv.tdevice, mdv.attribute, mdv.host,
mdv.port, mdv.group)
self.datasources[mdv.name] = xml
created = True
module = self._getModuleName(dv)
smodule = "%s@pool" % module.lower() if module else None
if module and module.lower() in \
self.xmlpackage.moduleMultiAttributes.keys():
multattr = self.xmlpackage.moduleMultiAttributes[
module.lower()]
for at in multattr:
dsname = "%s_%s" % (dv.name.lower(), at.lower())
xml = self._createTangoDataSource(
dsname, None, None, None,
dv.tdevice, at, dv.thost, dv.tport,
"%s_" % (dv.name))
self.datasources[dsname] = xml
mdv = copy.copy(dv)
mdv.name = dsname
mdv.hostname = "%s:%s" % (dv.thost, dv.tport)
mdv.attribute = at
self._printAction(mdv, dscps)
created = True
if smodule in \
self.xmlpackage.moduleMultiAttributes.keys():
smultattr = self.xmlpackage.moduleMultiAttributes[
smodule]
if smultattr and not dv.sdevice:
if self._printouts:
print(
"SKIPPING %s: Device cannot be found" %
dv.name)
else:
for at in smultattr:
dsname = "%s_%s" % (
dv.name, at.lower())
xml = self._createTangoDataSource(
dsname, None, None, None,
dv.sdevice, at, dv.shost, dv.sport,
"%s_" % (dv.name))
# "__CLIENT__")
self.datasources[dsname] = xml
mdv = copy.copy(dv)
mdv.name = dsname
mdv.tdevice = dv.sdevice
mdv.hostname = "%s:%s" % (dv.shost, dv.sport)
mdv.attribute = at
self._printAction(mdv, dscps)
created = True
if not created:
if self._printouts:
print(
"SKIPPING %s: module '%s' of '%s' "
"type not defined"
% (dv.name, dv.module, dv.dtype))
[docs]class CPCreator(Creator):
""" component creator of all online.xml complex devices
"""
def __init__(self, options, args, printouts=True):
""" constructor
:param options: command options
:type options: :class:`optparse.Values`
:param args: command arguments
:type args: :obj:`list` <:obj:`str` >
:param printouts: if printout is enable
:type printouts: :obj:`bool`
"""
Creator.__init__(self, options, args, printouts)
#: (:obj:`dict` <:obj:`str`, :obj:`str` >) datasource xml dictionary
self.datasources = {}
#: (:obj:`dict` <:obj:`str`, :obj:`str` >) component xml dictionary
self.components = {}
#: component xml dictionary
if options.xmlpackage:
xmlPackageHandler.loadXMLTemplates(options.xmlpackage)
else:
xmlPackageHandler.loadXMLTemplates('nxstools.xmltemplates')
#: (:obj:`str`) xml template component package path
self.xmltemplatepath = xmlPackageHandler.packagepath
#: (:obj:`str`) xml template component package
self.xmlpackage = xmlPackageHandler.package
[docs] def create(self):
""" creates components of all online.xml complex devices
"""
cpname = self.options.component
if hasattr(self.options, "database") and \
self.options.database:
server = self.options.server
if not self.options.overwrite:
if self._areComponentsAvailable(
[cpname], server, self.options.lower):
raise CPExistsException(
"Component '%s' already exists." % cpname)
elif not self.options.overwrite:
existing = self._componentFilesExist(
[cpname], self.options.file, self.options.directory)
if existing:
raise CPExistsException(
"Component files '%s' already exist." % existing)
self.createXMLs()
if not self.datasources and not self.components:
raise CPExistsException(
"Warning: Component %s cannot be created" % cpname)
server = self.options.server
if hasattr(self.options, "database") and \
self.options.database:
for dsname, dsxml in self.datasources.items():
storeDataSource(dsname, dsxml, server)
for cpname, cpxml in self.components.items():
mand = False
if hasattr(self.options, "mandatory") and \
self.options.mandatory:
mand = True
storeComponent(cpname, cpxml, server, mand)
else:
for dsname, dsxml in self.datasources.items():
with open("%s/%s%s.ds.xml" % (
self.options.directory,
self.options.file, dsname), "w") as myfile:
myfile.write(dsxml)
for cpname, cpxml in self.components.items():
with open("%s/%s%s.xml" % (
self.options.directory,
self.options.file, cpname), "w") as myfile:
myfile.write(cpxml)
@classmethod
def _replaceName(cls, filename, cpname, module=None):
""" replaces name prefix of xml templates files
:param filename: template filename
:type filename: :obj:`str`
:param cpname: output prefix
:type cpname: :obj:`str`
:param module: module name
:type module: :obj:`str`
:returns: output filename
:rtype: :obj:`str`
"""
if filename.endswith(".ds.xml"):
filename = filename[:-7]
elif filename.endswith(".xml"):
filename = filename[:-4]
sname = filename.split("_")
if not module or module == sname[0]:
sname[0] = cpname
return "_".join(sname)
[docs] def createXMLs(self):
""" creates component xmls of all online.xml complex devices
abstract method
"""
pass
[docs]class CompareOnlineDS(object):
""" comparing tool for online.xml files
"""
def __init__(self, options, args, printouts=True):
""" constructor
:param options: command options
:type options: :class:`optparse.Values`
:param args: command arguments
:type args: :obj:`list` < :obj:`str` >
:param printouts: if printout is enable
:type printouts: :obj:`bool`
"""
#: (:class:`optparse.Values`) creator options
self.options = options
#: (:obj:`list` < :obj:`str` >) creator arguments
self.args = args
#: (:obj:`bool`) if printout is enable
self._printouts = printouts
@classmethod
def _getText(cls, node):
""" provides xml content of the node
:param node: DOM node
:type node: :class:`lxml.etree.Element`
:returns: xml content string
:rtype: :obj:`str`
"""
if node is None:
return
xml = _toxml(node)
start = xml.find('>')
end = xml.rfind('<')
if start == -1 or end < start:
return ""
return xml[start + 1:end].replace("<", "<").replace(">", "<"). \
replace(""", "\"").replace("&", "&")
@classmethod
def _getChildText(cls, parent, childname):
""" provides text of child named by childname
:param parent: parent node
:type parent: :class:`lxml.etree.Element`
:param childname: child name
:type childname: :opj:`str`
:returns: text string
:rtype: :obj:`str`
"""
children = parent.findall(childname)
return cls._getText(children[0]) if len(children) else None
def _load(self, fname):
""" loads device data from online.xml file
:param fname: filename
:type fname: :obj:`str`
:returns: dictionary with devices of the given name
:rtype: :obj:`dict` <:obj:`str`, :obj:`list` <:class:`Device`>>
"""
dct = {}
hw = etree.parse(fname,
parser=XMLParser(collect_ids=False)).getroot()
if hw.tag != 'hw':
hw = hw.find('hw')
for device in hw:
if device.tag == 'device':
name = self._getChildText(device, "name")
if self.options.lower:
name = name.lower()
dv = Device()
dv.name = name
dv.dtype = self._getChildText(device, "type")
dv.module = self._getChildText(device, "module")
dv.tdevice = self._getChildText(device, "device")
dv.hostname = self._getChildText(device, "hostname")
dv.sardananame = self._getChildText(device, "sardananame")
dv.sardanahostname = self._getChildText(
device, "sardanahostname")
sname = dv.sardananame or name
if sname not in dct.keys():
dct[sname] = []
dct[sname].append(dv)
return dct
[docs] def compare(self):
if self._printouts:
print("Comparing: %s\n" % " ".join(self.args))
dct1 = self._load(self.args[0])
dct2 = self._load(self.args[1])
common = sorted(set(dct1.keys()) & set(dct2.keys()))
d1md2 = sorted(set(dct1.keys()) - set(dct2.keys()))
d2md1 = sorted(set(dct2.keys()) - set(dct1.keys()))
addd1 = dict((str(k),
[(str(dv.name) if dv.name else dv.name)
for dv in dct1[k]])
for k in d1md2)
addd2 = dict((str(k),
[(str(dv.name) if dv.name else dv.name)
for dv in dct2[k]])
for k in d2md1)
diff = {}
for name in common:
ndiff = {}
l1 = [True] * len(dct1[name])
l2 = [True] * len(dct2[name])
for i1, dv1 in enumerate(dct1[name]):
for i2, dv2 in enumerate(dct2[name]):
if l1[i1] and l2[i2]:
res = dv1.compare(dv2)
if not res:
l1[i1] = False
l2[i2] = False
break
else:
ndiff["%s:%s" % (i1, i2)] = res
if True in l1 and True not in l2:
addd1[str(name)] = []
for i1, dv in enumerate(dct1[name]):
if l1[i1]:
addd1[str(name)].append(
(str(dv.name) if dv.name else dv.name))
elif True not in l1 and True in l2:
addd2[str(name)] = []
for i2, dv in enumerate(dct2[name]):
if l2[i2]:
addd2[str(name)].append(
(str(dv.name) if dv.name else dv.name))
if True in l1 or True in l2:
diff[str(name)] = []
for i1, dv1 in enumerate(dct1[name]):
for i2, dv2 in enumerate(dct2[name]):
if l1[i1] and l2[i2]:
diff[str(name)].append(ndiff["%s:%s" % (i1, i2)])
if self._printouts:
import pprint
print("Additional devices in '%s' {alias: [name]} :\n"
% self.args[0])
pprint.pprint(addd1)
print("\nAdditional devices in '%s' {alias: [name]} :\n"
% self.args[1])
pprint.pprint(addd2)
print("\nDiffrences in the common part:\n")
pprint.pprint(diff)
[docs]class OnlineCPCreator(CPCreator):
""" component creator of online components
"""
def __init__(self, options, args, printouts=True):
""" constructor
:param options: command options
:type options: :class:`optparse.Values`
:param args: command arguments
:type args: :obj:`list` < :obj:`str` >
:param printouts: if printout is enable
:type printouts: :obj:`bool`
"""
CPCreator.__init__(self, options, args, printouts)
def _printAction(self, dv, dscps=None):
""" prints out information about the performed action
:param dv: online device object
:type dv: :class:`Device`
:param dscps: datasource components
:type dscps: :obj:`dict` <:obj:`str`, :obj:`list` < :obj:`str` > >
"""
if self._printouts:
if hasattr(self.options, "database") and \
self.options.database:
print("CREATING %s %s/%s %s" % (
str(dv.name) + ":" + " " * (34 - len(dv.name)),
dv.hostname,
str(dv.tdevice) + " " * (
60 - len(dv.tdevice) - len(dv.hostname)),
",".join(dscps[dv.name])
if (dscps and dv.name in dscps and dscps[dv.name])
else ""))
else:
print("CREATING %s: %s/%s%s.ds.xml" % (
dv.tdevice, self.options.directory, self.options.file,
dv.name))
[docs] def listcomponents(self):
""" provides a list of components with xml templates
:returns: list of components with xml templates
:rtype: :obj:`list` <:obj:`str` >
"""
hw = etree.parse(
self.args[0],
parser=XMLParser(collect_ids=False)).getroot()
if hw.tag != 'hw':
hw = hw.find('hw')
cpnames = set()
for device in hw:
if device.tag == 'device':
dvname = self._getChildText(device, "name")
sardananame = self._getChildText(device, "sardananame")
name = sardananame or dvname
if self.options.lower:
name = name.lower()
dv = Device()
dv.name = name
dv.dtype = self._getChildText(device, "type")
dv.module = self._getChildText(device, "module")
dv.tdevice = self._getChildText(device, "device")
dv.hostname = self._getChildText(device, "hostname")
dv.sardananame = self._getChildText(device, "sardananame")
dv.sardanahostname = self._getChildText(
device, "sardanahostname")
module = self._getModuleName(dv)
if module:
if module.lower() in self.xmlpackage.moduleTemplateFiles:
cpnames.add(dv.name)
return cpnames
[docs] def listcomponenttypes(self):
""" provides a list of standard component types
:returns: list of standard component types
:rtype: :obj:`list` <:obj:`str`>
"""
return list(sorted(self.xmlpackage.moduleTemplateFiles.keys()))
[docs] def createXMLs(self):
""" creates component xmls of all online.xml complex devices
"""
self.datasources = {}
self.components = {}
if self.options.component and self.options.cptype and \
self.options.device:
hw = [None]
else:
hw = etree.parse(
self.args[0],
parser=XMLParser(collect_ids=False)).getroot()
if hw.tag != 'hw':
hw = hw.find('hw')
cpname = self.options.component
tangohost = getServerTangoHost(
self.options.external or self.options.server)
for device in hw:
if device is None or device.tag == 'device':
if device is None:
name = cpname
else:
dvname = self._getChildText(device, "name")
sardananame = self._getChildText(
device, "sardananame")
name = sardananame or dvname
if self.options.lower:
name = name.lower()
cpname = cpname.lower()
if name == cpname:
dv = Device()
dv.name = name
dv.dtype = self.options.cptype or \
self._getChildText(device, "type")
dv.module = self.options.cptype or \
self._getChildText(device, "module")
dv.tdevice = self.options.device or \
self._getChildText(device, "device")
hostname = None
if self.options.host:
host = self.options.host
port = self.options.port \
if self.options.port else "10000"
hostname = "%s:%s" % (host, port)
if hostname is None and device is None:
hostname = tangohost
if hostname is not None:
dv.hostname = hostname
if hostname is None and device is not None:
dv.hostname = self._getChildText(device, "hostname")
if device is not None:
dv.sardananame = \
self._getChildText(device, "sardananame")
dv.sardanahostname = self._getChildText(
device, "sardanahostname")
dv.findDevice(tangohost)
try:
dv.splitHostPort()
except Exception:
if self._printouts:
print("ERROR %s: host for module %s of %s "
"type not defined"
% (dv.name, dv.module, dv.dtype))
continue
if self.options.cptype:
module = self.options.cptype
else:
module = self._getModuleName(dv)
if module:
if module.lower() in \
self.xmlpackage.moduleMultiAttributes.keys():
multattr = self.xmlpackage.moduleMultiAttributes[
module.lower()]
for at in multattr:
dsname = "%s_%s" % (
dv.name, at.lower())
xml = self._createTangoDataSource(
dsname, None, None, None,
dv.tdevice, at, dv.host, dv.port,
"%s_" % (dv.name.lower()))
self.datasources[dsname] = xml
mdv = copy.copy(dv)
mdv.name = dsname
mdv.attribute = at
self._printAction(mdv)
smodule = "%s@pool" % module.lower()
if smodule in \
self.xmlpackage.moduleMultiAttributes.keys():
smultattr = self.xmlpackage.moduleMultiAttributes[
smodule]
if smultattr and not dv.sdevice:
raise Exception(
"Device %s cannot be found" % dv.name)
for at in smultattr:
dsname = "%s_%s" % (
dv.name,
at.lower())
xml = self._createTangoDataSource(
dsname, None, None, None,
dv.sdevice, at, dv.shost, dv.sport,
"%s_" % (dv.name))
# "__CLIENT__")
self.datasources[dsname] = xml
mdv = copy.copy(dv)
mdv.name = dsname
mdv.tdevice = dv.sdevice
mdv.hostname = "%s:%s" % (dv.shost, dv.sport)
mdv.attribute = at
self._printAction(mdv)
if module.lower() \
in self.xmlpackage.moduleTemplateFiles:
xmlfiles = self.xmlpackage.moduleTemplateFiles[
module.lower()]
for xmlfile in xmlfiles:
newname = self._replaceName(xmlfile, cpname)
with open(
'%s/%s' % (
self.xmltemplatepath, xmlfile), "r"
) as content_file:
xmlcontent = content_file.read()
xml = xmlcontent.replace("$(name)", cpname)\
.replace("$(device)", dv.tdevice)\
.replace("$(__entryname__)",
(self.options.entryname
or "scan"))\
.replace("$(__insname__)",
(self.options.insname
or "instrument"))\
.replace("$(hostname)", dv.hostname)
mdv = copy.copy(dv)
mdv.name = newname
self._printAction(mdv)
if xmlfile.endswith(".ds.xml"):
self.datasources[newname] = xml
else:
self.components[newname] = xml
[docs]class SECoPCPCreator(CPCreator):
""" component creator of secop components
"""
def __init__(self, options, args, printouts=True):
""" constructor
:param options: command options
:type options: :class:`optparse.Values`
:param args: command arguments
:type args: :obj:`list` < :obj:`str` >
:param printouts: if printout is enable
:type printouts: :obj:`bool`
"""
CPCreator.__init__(self, options, args, printouts)
def _printAction(self, name, ds=False):
""" prints out information about the performed action
:param name: component name
:type name: :obj:`str`
"""
if self._printouts:
if hasattr(self.options, "database") and \
self.options.database:
print("CREATING '%s' of secop on '%s'" % (
name,
self.options.server))
else:
ext = ".ds" if ds else ""
print("CREATING '%s' of secop in '%s/%s%s%s.xml'" % (
name,
self.options.directory,
self.options.file,
name, ext))
[docs] def listmodules(self):
""" provides a list of modules for the secop node
:returns: list of modules for the secop node
:rtype: :obj:`list` <:obj:`str` >
"""
names = []
conf = {}
if self.options.json:
with open(self.options.json) as fl:
conf = json.load(fl)
else:
conf = secop_cmd(
"describe", self.options.host, int(self.options.port)) or {}
modules = conf.get("modules", {})
if modules:
names = [mname for mname in modules.keys()]
# print(json.dumps(conf))
return names
def __createSECoPTree(self, df, name, conf, samplename=None,
sampleenvname=None,
modulenames=None, canfail=None,
environments=None,
meanings=None, first=None, transattrs=None,
dynamiclinks=False, samplenxdata=False):
""" create nexus node tree
:param df: definition parent node
:type df: :class:'nxstools.nxsxml.XMLFile'
:param name: node name
:type name: :obj:`str`
:param conf: secop configuration
:type conf: :obj:`dict`
:param samplename: sample name
:type samplename: :obj:`str`
:param sampleenvname: sample environment name
:type sampleenvname: :obj:`str`
:param modulenames: module names
:typae modulenames: :obj:`list` <:obj:`str`>
:param canfail: can fail strategy flag
:type canfail: :obj:`bool`
:param environments: environments to link separated by comman
:type environments: :obj:`str`
:param meanings: physical quantity meaning to link separated by comman
:type meanings: :obj:`str`
:param first: first targets to link separated by comman
:type first: :obj:`str`
:param transattrs: JSON dictionary with transformation attributes
:type transattrs: :obj:`str`
:param dynamiclinks: dynamic links flag
:type dynamiclinks: :obj:`bool`
:param samplenxdata: sample nxdata
:type samplenxdata: :obj:`bool`
"""
ename = "$var.entryname#'$(__entryname__)'$var.serialno".replace(
"$(__entryname__)", (self.options.entryname or "scan"))
entry = NGroup(df, ename, "NXentry")
samplename = samplename or "sample"
sampleenvname = sampleenvname or "sample_environment"
sample = NGroup(entry, samplename, "NXsample")
field = NField(sample, 'type', 'NX_CHAR')
field.setText("sample")
field.setStrategy('INIT')
sampleenv = NGroup(entry, sampleenvname, "NXsample")
field = NField(sampleenv, 'type', 'NX_CHAR')
field.setText("sample environment")
field.setStrategy('INIT')
modules = conf.get("modules", {})
senv = None
seenv = None
for mname, mconf in modules.items():
if mname and (not senv or not seenv):
if not modulenames or mname in modulenames:
if not senv and "meaning" in mconf.keys():
senv = NGroup(sample, name or "environment",
"NXenvironment")
if not seenv and "meaning" not in mconf.keys():
seenv = NGroup(sampleenv, name or "environment",
"NXenvironment")
envs = [senv, seenv]
for env in envs:
if env:
if 'equipment_id' in conf.keys():
field = NField(env, 'name', 'NX_CHAR')
field.setText("%s" % str(conf['equipment_id']))
field.setStrategy('INIT')
if name or 'equipment_id' in conf.keys():
field = NField(env, 'short_name', 'NX_CHAR')
if name:
field.setText("%s" % str(name))
elif 'equipment_id' in conf.keys():
field.setText("%s" % str(conf['equipment_id']))
field.setStrategy('INIT')
if 'firmware' in conf.keys() or 'version' in conf.keys():
field = NField(env, 'type', 'NX_CHAR')
txt = ""
if 'firmware' in conf.keys():
txt = str(conf['firmware'])
if 'version' in conf.keys():
if txt:
txt = "%s (%s)" % (txt, str(conf['version']))
else:
txt = "(%s)" % (str(conf['version']))
field.setText(txt)
field.setStrategy('INIT')
if 'description' in conf.keys():
field = NField(env, 'description', 'NX_CHAR')
field.setText("%s" % str(conf['description']))
field.setStrategy('INIT')
targets = (first or "").split(",")
lmeanings = (meanings or "").split(",")
lenvironments = (environments or "").split(",")
try:
trattrs = json.loads(transattrs or "{}")
except Exception:
trattrs = {}
links = {}
for mname, mconf in modules.items():
if mname:
if not modulenames or mname in modulenames:
lk = self.__createSECoPSensor(
senv, seenv, mname, mconf, name, canfail, samplename,
sampleenvname, trattrs)
if lk and isinstance(lk, dict):
links.update(lk)
ename = \
"$var.entryname#'$(__entryname__)'" \
"$var.serialno".replace(
"$(__entryname__)",
(self.options.entryname or "scan"))
created = []
created_trans = []
trans = None
for tg in targets:
try:
stg = "/".join(tg.split("/")[-3:])
except Exception:
stg = None
if tg in links.keys() or stg and stg in links.keys():
mn = links[tg][0]
NLink(sample, mn, tg)
created.append(mn)
llinks = sorted([(tg, mns[0], mns[1]) for tg, mns in links.items()],
key=itemgetter(2), reverse=True)
if dynamiclinks or samplenxdata:
self.createSECoPLinkDS(
ename, samplename, sampleenvname,
meanings or "", environments or "")
if dynamiclinks:
ae = sample.addAttr(
'secop_env_links', "NX_CHAR",
"$datasources.sample_env_links")
ae.setStrategy("FINAL")
al = sample.addAttr(
'secop_log_links', "NX_CHAR",
"$datasources.sample_log_links")
al.setStrategy("FINAL")
if samplenxdata:
al = sample.addAttr(
'sample_nxdata', "NX_CHAR",
"$datasources.sample_nxdata")
al.setStrategy("FINAL")
al = sampleenv.addAttr(
'sampleenv_nxdata', "NX_CHAR",
"$datasources.sampleenv_nxdata")
al.setStrategy("FINAL")
for target, mn, semn in llinks:
starget = target.split("/")
if not dynamiclinks:
if mn in lenvironments and "%s_env" % mn not in created:
env = NGroup(
sample, "%s_env" % mn, "NXenvironment")
NLink(env, starget[-2], "/".join(starget[:-1]))
NLink(env, "description",
"/".join(starget[:-2]) + "/description")
NLink(env, "name",
"/".join(starget[:-2]) + "/name")
NLink(env, "short_name",
"/".join(starget[:-2]) + "/short_name")
NLink(env, "type", "/".join(starget[:-2]) + "/type")
created.append("%s_env" % mn)
if mn in lmeanings and mn not in created:
NLink(sample, mn, target)
created.append(mn)
if mn in trattrs.keys():
nm = "%s_%s" % (starget[-3], starget[-2])
if nm not in created_trans:
if trans is None:
trans = NGroup(
sample, "transformations", "NXtransformations")
NLink(trans, nm, target + "/value")
created_trans.append(nm)
def __createSECoPSensor(self, senv, seenv, name, conf, nodename,
canfail=None, samplename="sample",
sampleenvname="sample_environment",
trattrs=None):
""" create nexus node tree
:param senv: definition parent node
:type senv: :class:'nxstools.nxsxml.XMLFile'
:param seenv: definition parent node
:type seenv: :class:'nxstools.nxsxml.XMLFile'
:param name: sensor name
:type name: :obj:`str`
:param conf: secop configuration
:type conf: :obj:`dict`
:param nodename: node name
:type nodename: :obj:`str`
:param canfail: can fail strategy flag
:type canfail: :obj:`bool`
:param samplename: sample group name i.e. sample
:type samplename: :obj:`str`
:param sampleenvname: sample environment group name
:type sampleenvname: :obj:`str`
:param trattrs: dictionary with transformation attributes
:type trattrs: :obj:`dict` <:obj:`str`,:obj:`dict` <:obj:`str`,`and`>>
:returns: links targets and meaning names
:rtype: :obj:`dict`< :obj:`str`, (:obj:`str`, :obj:`str`) >
"""
if 'meaning' in conf.keys():
meaning = conf['meaning']
env = senv
basename = samplename
else:
meaning = None
env = seenv
basename = sampleenvname
links = {}
trattrs = trattrs or {}
mgr = NGroup(env, name, "NXsensor")
if 'description' in conf.keys():
field = NField(mgr, 'name', 'NX_CHAR')
field.setText("%s" % str(name))
field.setStrategy('INIT')
semeaning = None
if 'meaning' in conf.keys():
meaning = conf['meaning']
semeaning = meaning
importance = None
if isinstance(meaning, list):
if len(meaning) > 1:
try:
importance = int(meaning[1])
except Exception:
importance = None
if len(meaning) > 0:
meaning = meaning[0]
if meaning in mnTme.keys():
meaning = mnTme[meaning]
field = NField(mgr, 'measurement', 'NX_CHAR')
field.setText(meaning)
field.setStrategy('INIT')
if importance is not None:
mimp = NAttr(field, "secop_importance", "NX_INT32")
mimp.setText(str(importance))
if 'implementation' in conf.keys():
field = NField(mgr, 'model', 'NX_CHAR')
field.setText("%s" % str(conf['implementation']))
field.setStrategy('INIT')
if 'description' in conf.keys():
field = NField(mgr, 'description', 'NX_CHAR')
field.setText("%s" % str(conf['description']))
field.setStrategy('INIT')
params = conf.get("accessibles", {})
if params:
par = NGroup(mgr, "parameters", "NXcollection")
for pname, pconf in params.items():
if pname:
if pname == "value":
di = pconf.get("datainfo")
if di:
dtype = di.get("type")
nxtype = npTn.get(dtype, "NX_CHAR")
units = di.get("unit")
minval = di.get("min")
maxval = di.get("max")
log = NGroup(mgr, "value_log", "NXlog")
field = NField(log, 'value', nxtype)
if meaning:
ename = \
"$var.entryname#'$(__entryname__)'" \
"$var.serialno".replace(
"$(__entryname__)",
(self.options.entryname or "scan"))
target = "/%s/%s/%s/%s/value_log" % \
(ename, basename, nodename, name)
links[target] = (meaning, semeaning)
dsname = "%s_%s" % (nodename, name)
timedsname = "%s_%s_time" % (nodename, name)
if self.options.lower:
dsname = dsname.lower()
timedsname = timedsname.lower()
self.createSECoPDS(dsname,
"read %s:%s" % (name, pname),
dsname, "[0]")
field.setText("$datasources.%s" % dsname)
if units:
field.setUnits(units)
if meaning and meaning in trattrs.keys():
try:
attrs = dict(trattrs[meaning])
except Exception:
attrs = {}
for nm, vl in attrs.items():
if isinstance(vl, list):
if vl and isinstance(vl[0], str):
vct = NAttr(field, nm, "NX_CHAR")
vct.setText("\n ".join(vl))
else:
vct = NAttr(field, nm, "NX_FLOAT64")
vct.setText(
" ".join([str(st) for st in vl]))
d = NDimensions(vct, "1")
d.dim("1", str(len(vl)))
vct.setStrategy("INIT")
else:
field.addTagAttr(nm, vl)
strategy = self.options.strategy
field.setStrategy(strategy)
field = NField(log, 'time', "NX_FLOAT64")
field.setText(
"$datasources.%s" % timedsname)
at = field.addAttr(
'start', "NX_DATE_TIME",
"$datasources.client_start_time")
at.setStrategy("INIT")
field.setUnits("s")
field.setStrategy(strategy)
if minval:
field = NField(log, 'minimal_value', nxtype)
field.setStrategy('INIT')
field.setText(str(minval))
if units:
field.setUnits(units)
if maxval:
field = NField(log, 'maximal_value', nxtype)
field.setStrategy('INIT')
field.setText(str(maxval))
if units:
field.setUnits(units)
elif pname == "status":
self.__createSECoPParam(
par, pname, pconf, nodename, name, canfail,
"[0,0]", "int")
else:
self.__createSECoPParam(
par, pname, pconf, nodename, name, canfail)
if pname == "target":
ename = \
"$var.entryname#'$(__entryname__)'" \
"$var.serialno".replace(
"$(__entryname__)",
(self.options.entryname or "scan"))
NLink(mgr, "value",
"/%s/%s/%s/%s/parameters/target/value" %
(ename, basename, nodename, name))
return links
def __createSECoPParam(self, par, name, conf, nodename, modname,
canfail=None, access=None, accesstype=None):
""" create nexus node tree
:param env: definition parent node
:type env: :class:'nxstools.nxsxml.XMLFile'
:param name: parameter name
:type name: :obj:`str`
:param conf: secop configuration
:type conf: :obj:`dict`
:param nodename: node name
:type nodename: :obj:`str`
:param modname: sensor name
:type modname: :obj:`str`
:param canfail: can fail strategy flag
:type canfail: :obj:`bool`
"""
di = conf.get("datainfo")
if di:
dtype = di.get("type")
if dtype == "command":
return
if dtype not in npTn.keys() and not access:
return
nxtype = npTn.get(dtype, "NX_CHAR")
if accesstype in npTn.keys():
nxtype = npTn.get(accesstype)
units = di.get("unit")
minval = di.get("min")
maxval = di.get("max")
access = access or "[0]"
log = NGroup(par, name, "NXlog")
field = NField(log, "value", nxtype)
dsname = "%s_%s_%s" % (nodename, modname, name)
timedsname = "%s_%s_%s_time" % (nodename, modname, name)
if self.options.lower:
dsname = dsname.lower()
timedsname = timedsname.lower()
field.setText("$datasources.%s" % (dsname))
pstrategy = self.options.paramstrategy
self.createSECoPDS(dsname,
"read %s:%s" % (modname, name),
dsname, access)
field.setStrategy(pstrategy)
if units:
field.setUnits(units)
field = NField(log, 'time', "NX_FLOAT64")
field.setText(
"$datasources.%s" % timedsname)
at = field.addAttr(
'start', "NX_DATE_TIME", "$datasources.client_start_time")
at.setStrategy("INIT")
field.setUnits("s")
field.setStrategy(pstrategy)
if minval:
field = NField(log, 'minimal_value', nxtype)
field.setStrategy('INIT')
field.setText(str(minval))
if units:
field.setUnits(units)
if maxval:
field = NField(log, 'maximal_value', nxtype)
field.setStrategy('INIT')
field.setText(str(maxval))
if units:
field.setUnits(units)
[docs] def createXMLs(self):
""" creates component xmls of all online.xml complex devices
"""
self.datasources = {}
self.components = {}
cpnames = self.args
conf = {}
if self.options.json:
with open(self.options.json) as fl:
conf = json.load(fl)
else:
conf = secop_cmd(
"describe", self.options.host, int(self.options.port))
if isinstance(conf, dict):
# dump = \
# json.dumps(conf, sort_keys=True, indent=4)
# print("%s" % dump)
cpname = self.options.component
if 'description' in conf.keys() and not cpname:
eid = str(conf['equipment_id']).split(".")
if eid and eid[0]:
cpname = eid[0]
else:
des = str(conf['description']).split("\n")
if des and des[0]:
cpname = des[0]
cpname = cpname.replace("[", "").\
replace("]", "_").replace(",", "_")
if not cpname:
cpname = "secop"
fname = "%s%s.xml" % (self.options.file, cpname)
if self.options.lower:
fname = fname.lower()
cpname = cpname.lower()
df = XMLFile("%s/%s" % (self.options.directory, fname))
self.__createSECoPTree(df, cpname, conf, self.options.samplename,
self.options.sampleenvname,
cpnames, self.options.canfail,
self.options.environments,
self.options.meanings,
self.options.first,
self.options.transattrs,
self.options.dynamiclinks,
self.options.samplenxdata)
self._printAction(cpname)
self.components[cpname] = df.prettyPrint()
[docs] def createSECoPDS(self, dsname, message, group=None, access=None,
host=None, port=None, timeout=None):
""" create SECoP datasource
:param dsname: datasource name
:type dsname: :obj:`str`
:param message: secop command
:type message: :obj:`str`
:param group: secop group name
:type group: :obj:`str`
:param access: secop attribute access list
:type access: :obj:`str`
:param host: secop host name
:type host: :obj:`str`
:param port: secop port name
:type port: :obj:`str` or :obj:`int`
:param port: minimum timeout
:type port: :obj:`str` or :obj:`float`
"""
if access is not None and group is not None:
module = 'groupsecop'
elif group is not None:
module = 'secop'
else:
module = 'singlesecop'
if module in self.xmlpackage.standardComponentTemplateFiles:
xmlfiles = self.xmlpackage.standardComponentTemplateFiles[module]
else:
if os.path.isfile("%s/%s.xml" % (self.xmltemplatepath, module)):
xmlfiles = ["%s.xml" % module]
params = {}
host = self.options.host if host is None else host
port = self.options.port if port is None else port
timeout = str(self.options.timeout) \
if timeout is None else str(timeout)
if host is not None:
params["host"] = host
if port is not None:
params["port"] = str(port)
if message:
params["message"] = message
if timeout:
params["timeout"] = timeout
if access:
params["access"] = access
if group:
params["group"] = group
if self.options.lower:
dsname = dsname.lower()
for xmlfile in xmlfiles:
# print(xmlfile)
newname = self._replaceName(xmlfile, dsname, module)
with open(
'%s/%s' % (
self.xmltemplatepath, xmlfile), "r"
) as content_file:
xmlcontent = content_file.read()
xml = xmlcontent.replace("$(name)", dsname)
missing = []
for var, desc in self.xmlpackage.standardComponentVariables[
module].items():
if var in params.keys():
xml = xml.replace("$(%s)" % var, params[var])
elif desc["default"] is not None:
xml = xml.replace("$(%s)" % var, desc["default"])
else:
missing.append(var)
if missing:
if sys.version_info > (3,):
root = et.fromstring(
bytes(xml, "UTF-8"),
parser=XMLParser(collect_ids=False))
else:
root = et.fromstring(
xml,
parser=XMLParser(collect_ids=False))
nodes = root.findall(".//attribute")
nodes.extend(root.findall(".//field"))
nodes.extend(root.findall(".//link"))
grnodes = root.findall(".//group")
for node in nodes:
text = self.__getText(node)
for ms in missing:
label = "$(%s)" % ms
if label in text:
parent = node.getparent()
parent.remove(node)
break
for node in grnodes:
text = node.attrib["name"]
if text and "$(" in text:
for ms in missing:
label = "$(%s)" % ms
if label in text:
parent = node.getparent()
parent.remove(node)
break
xml = _simpletoxml(root)
if self._printouts:
print("MISSING %s" % missing)
errors = []
for var in missing:
if "s.$(%s)" % var in xml:
errors.append(var)
if errors:
print(
"WARNING: %s cannot be created without %s"
% (var, errors))
continue
for var in missing:
xml = xml.replace("$(%s)" % var, "")
lines = xml.split('\n')
xml = '\n'.join([x for x in lines if len(x.strip())])
if xmlfile.endswith(".ds.xml"):
self._printAction(newname, True)
self.datasources[newname] = xml
else:
self._printAction(newname)
self.components[newname] = xml
[docs] def createSECoPLinkDS(self, entryname, samplename, sampleenvname,
meanings, environments):
""" create SECoP datasource
:param entryname: secop entry name
:type entryname: :obj:`str`
:param samplename: secop sample name
:type samplename: :obj:`str`
:param sampleenvname: secop sample name
:type sampleenvname: :obj:`str`
:param meanings: secop meanings list
:type meanings: :obj:`str`
:param environments: secop environments list
:type environments: :obj:`str`
"""
module = 'secoplinks'
dsname = "secop"
if module in self.xmlpackage.standardComponentTemplateFiles:
xmlfiles = self.xmlpackage.standardComponentTemplateFiles[module]
else:
if os.path.isfile("%s/%s.xml" % (self.xmltemplatepath, module)):
xmlfiles = ["%s.xml" % module]
params = {}
params["entryname"] = entryname
params["samplename"] = samplename
params["sampleenvname"] = sampleenvname
params["meanings"] = meanings
params["environments"] = environments
if self.options.lower:
dsname = dsname.lower()
for xmlfile in xmlfiles:
# print(xmlfile)
newname = self._replaceName(xmlfile, dsname, module)
with open(
'%s/%s' % (
self.xmltemplatepath, xmlfile), "r"
) as content_file:
xmlcontent = content_file.read()
xml = xmlcontent.replace("$(name)", dsname).replace(
"$(__entryname__)",
(self.options.entryname or "scan")).replace(
"$(__insname__)",
(self.options.insname
or "instrument"))
missing = []
for var, desc in self.xmlpackage.standardComponentVariables[
module].items():
if var in params.keys():
xml = xml.replace("$(%s)" % var, params[var])
elif desc["default"] is not None:
xml = xml.replace("$(%s)" % var, desc["default"])
else:
missing.append(var)
if missing:
if sys.version_info > (3,):
root = et.fromstring(
bytes(xml, "UTF-8"),
parser=XMLParser(collect_ids=False))
else:
root = et.fromstring(
xml,
parser=XMLParser(collect_ids=False))
nodes = root.findall(".//attribute")
nodes.extend(root.findall(".//field"))
nodes.extend(root.findall(".//link"))
grnodes = root.findall(".//group")
for node in nodes:
text = self.__getText(node)
for ms in missing:
label = "$(%s)" % ms
if label in text:
parent = node.getparent()
parent.remove(node)
break
for node in grnodes:
text = node.attrib["name"]
if text and "$(" in text:
for ms in missing:
label = "$(%s)" % ms
if label in text:
parent = node.getparent()
parent.remove(node)
break
xml = _simpletoxml(root)
if self._printouts:
print("MISSING %s" % missing)
errors = []
for var in missing:
if "s.$(%s)" % var in xml:
errors.append(var)
if errors:
print(
"WARNING: %s cannot be created without %s"
% (var, errors))
continue
for var in missing:
xml = xml.replace("$(%s)" % var, "")
lines = xml.split('\n')
xml = '\n'.join([x for x in lines if len(x.strip())])
if xmlfile.endswith(".ds.xml"):
self._printAction(newname, True)
self.datasources[newname] = xml
else:
self._printAction(newname)
self.components[newname] = xml
[docs] def create(self):
""" creates components of all online.xml complex devices
"""
cpnames = self.args
for cpname in cpnames:
if hasattr(self.options, "database") and \
self.options.database:
server = self.options.server
if not self.options.overwrite:
if self._areComponentsAvailable(
[cpname], server, self.options.lower):
raise CPExistsException(
"Component '%s' already exists." % cpname)
elif not self.options.overwrite:
existing = self._componentFilesExist(
[cpname], self.options.file, self.options.directory)
if existing:
raise CPExistsException(
"Component files '%s' already exist." % existing)
self.createXMLs()
if not self.datasources and not self.components:
raise CPExistsException(
"Warning: Components %s cannot be created" % cpnames)
server = self.options.server
if hasattr(self.options, "database") and \
self.options.database:
for dsname, dsxml in self.datasources.items():
storeDataSource(dsname, dsxml, server)
for cpname, cpxml in self.components.items():
mand = False
if hasattr(self.options, "mandatory") and \
self.options.mandatory:
mand = True
storeComponent(cpname, cpxml, server, mand)
else:
for dsname, dsxml in self.datasources.items():
with open("%s/%s%s.ds.xml" % (
self.options.directory,
self.options.file, dsname), "w") as myfile:
myfile.write(dsxml)
for cpname, cpxml in self.components.items():
# print(cpname)
with open("%s/%s%s.xml" % (
self.options.directory,
self.options.file, cpname), "w") as myfile:
# print("%s/%s%s.xml" % (
# self.options.directory,
# self.options.file, cpname))
myfile.write(cpxml)
@classmethod
def _replaceName(cls, filename, cpname, module=None):
""" replaces name prefix of xml templates files
:param filename: template filename
:type filename: :obj:`str`
:param cpname: output prefix
:type cpname: :obj:`str`
:param module: module name
:type module: :obj:`str`
:returns: output filename
:rtype: :obj:`str`
"""
if filename.endswith(".ds.xml"):
filename = filename[:-7]
elif filename.endswith(".xml"):
filename = filename[:-4]
sname = filename.split("_")
if not module or module == sname[0]:
sname[0] = cpname
return "_".join(sname)
[docs]class StandardCPCreator(CPCreator):
""" component creator of standard templates
"""
def __init__(self, options, args, printouts=True):
""" constructor
:param options: command options
:type options: :class:`optparse.Values`
:param args: command arguments
:type args: :obj:`list` < :obj:`str` >
:param printouts: if printout is enable
:type printouts: :obj:`bool`
"""
CPCreator.__init__(self, options, args, printouts)
self.__params = {}
self.__specialparams = {}
[docs] def listcomponenttypes(self):
""" provides a list of standard component types
:returns: list of standard component types
:rtype: :obj:`list` <:obj:`str`>
"""
return list(sorted(self.xmlpackage.standardComponentVariables.keys()))
[docs] def listcomponentvariables(self):
""" provides a list of standard component types
:returns: list of standard component types
:rtype: :obj:`list` <:obj:`str`>
"""
if self.options.cptype not \
in self.xmlpackage.standardComponentVariables.keys():
raise Exception(
"Component type %s not in %s" %
(self.options.cptype,
list(self.xmlpackage.standardComponentVariables.keys())))
return self.xmlpackage.standardComponentVariables[
self.options.cptype]
def __setspecialparams(self):
""" sets special parameters,
i.e. __tangohost__, __tangoport__ and __configdevice__
"""
server = self.options.external or self.options.server
host, port = getServerTangoHost(server).split(":")
self.__specialparams['__tangohost__'] = host
self.__specialparams['__tangoport__'] = port
if server:
proxy = openServer(server)
self.__specialparams['__configdevice__'] = proxy.name()
else:
self.__specialparams['__configdevice__'] = None
[docs] def createXMLs(self):
""" creates component xmls of all online.xml complex devices
"""
self.datasources = {}
self.components = {}
self.__setspecialparams()
if self.args:
self.__params = dict(zip(self.args[::2], self.args[1::2]))
else:
self.__params = {}
cpname = self.options.component
module = self.options.cptype
if self.options.lower:
cpname = cpname.lower()
module = module.lower()
if module not in self.xmlpackage.standardComponentVariables.keys():
raise Exception(
"Component type %s not in %s" %
(module,
list(self.xmlpackage.standardComponentVariables.keys()))
)
xmlfiles = []
if module in self.xmlpackage.standardComponentTemplateFiles:
xmlfiles = self.xmlpackage.standardComponentTemplateFiles[module]
else:
if os.path.isfile("%s/%s.xml" % (self.xmltemplatepath, module)):
xmlfiles = ["%s.xml" % module]
for xmlfile in xmlfiles:
# print(xmlfile)
newname = self._replaceName(xmlfile, cpname, module)
with open(
'%s/%s' % (
self.xmltemplatepath, xmlfile), "r"
) as content_file:
xmlcontent = content_file.read()
xml = xmlcontent.replace("$(name)", cpname).replace(
"$(__entryname__)",
(self.options.entryname or "scan")).replace(
"$(__insname__)",
(self.options.insname
or "instrument"))
missing = []
for var, desc in self.xmlpackage.standardComponentVariables[
module].items():
if var in self.__params.keys():
xml = xml.replace("$(%s)" % var, self.__params[var])
elif var in self.__specialparams.keys():
if self.__specialparams[var] is not None:
xml = xml.replace("$(%s)" % var,
self.__specialparams[var])
else:
raise Exception(
"Parameter: %s cannot be found" % var)
elif desc["default"] is not None:
xml = xml.replace("$(%s)" % var, desc["default"])
else:
missing.append(var)
if missing:
if sys.version_info > (3,):
root = et.fromstring(
bytes(xml, "UTF-8"),
parser=XMLParser(collect_ids=False))
else:
root = et.fromstring(
xml,
parser=XMLParser(collect_ids=False))
nodes = root.findall(".//attribute")
nodes.extend(root.findall(".//field"))
nodes.extend(root.findall(".//link"))
grnodes = root.findall(".//group")
for node in nodes:
text = self.__getText(node)
for ms in missing:
label = "$(%s)" % ms
if label in text:
parent = node.getparent()
parent.remove(node)
break
for node in grnodes:
text = node.attrib["name"]
if text and "$(" in text:
for ms in missing:
label = "$(%s)" % ms
if label in text:
parent = node.getparent()
parent.remove(node)
break
xml = _simpletoxml(root)
if self._printouts:
print("MISSING %s" % missing)
errors = []
for var in missing:
if "s.$(%s)" % var in xml:
errors.append(var)
if errors:
print(
"WARNING: %s cannot be created without %s"
% (var, errors))
continue
for var in missing:
xml = xml.replace("$(%s)" % var, "")
lines = xml.split('\n')
xml = '\n'.join([x for x in lines if len(x.strip())])
if xmlfile.endswith(".ds.xml"):
self._printAction(newname)
self.datasources[newname] = xml
else:
self._printAction(newname)
self.components[newname] = xml
def _printAction(self, name):
""" prints out information about the performed action
:param name: component name
:type name: :obj:`str`
"""
if self._printouts:
if hasattr(self.options, "database") and \
self.options.database:
print("CREATING '%s' of '%s' on '%s' with %s" % (
name,
self.options.cptype,
self.options.server,
self.__params))
else:
print("CREATING '%s' of '%s' in '%s/%s%s.xml' with %s" % (
name,
self.options.cptype,
self.options.directory,
self.options.file,
name,
self.__params))
@classmethod
def __getText(cls, node):
""" collects text from text child nodes
:param node: parent node
:type node: :obj:`xml.etree.ElementTree.Element`
"""
if node is not None:
tnodes = ([node.text] if node.text else []) \
+ [child.tail for child in node if child.tail]
return unicode("".join(tnodes)).strip()
return ""
if __name__ == "__main__":
pass