#!/usr/bin/env python
# -*- coding: utf-8 -*-
# #########################################################################
# Copyright (c) 2016, UChicago Argonne, LLC. All rights reserved. #
# #
# Copyright 2016. UChicago Argonne, LLC. This software was produced #
# under U.S. Government contract DE-AC02-06CH11357 for Argonne National #
# Laboratory (ANL), which is operated by UChicago Argonne, LLC for the #
# U.S. Department of Energy. The U.S. Government has rights to use, #
# reproduce, and distribute this software. NEITHER THE GOVERNMENT NOR #
# UChicago Argonne, LLC MAKES ANY WARRANTY, EXPRESS OR IMPLIED, OR #
# ASSUMES ANY LIABILITY FOR THE USE OF THIS SOFTWARE. If software is #
# modified to produce derivative works, such modified software should #
# be clearly marked, so as not to confuse it with the version available #
# from ANL. #
# #
# Additionally, redistribution and use in source and binary forms, with #
# or without modification, are permitted provided that the following #
# conditions are met: #
# #
# * Redistributions of source code must retain the above copyright #
# notice, this list of conditions and the following disclaimer. #
# #
# * Redistributions in binary form must reproduce the above copyright #
# notice, this list of conditions and the following disclaimer in #
# the documentation and/or other materials provided with the #
# distribution. #
# #
# * Neither the name of UChicago Argonne, LLC, Argonne National #
# Laboratory, ANL, the U.S. Government, nor the names of its #
# contributors may be used to endorse or promote products derived #
# from this software without specific prior written permission. #
# #
# THIS SOFTWARE IS PROVIDED BY UChicago Argonne, LLC AND CONTRIBUTORS #
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT #
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS #
# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL UChicago #
# Argonne, LLC OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, #
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, #
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; #
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER #
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT #
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN #
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE #
# POSSIBILITY OF SUCH DAMAGE. #
# #########################################################################
"""
Please make sure the installation :ref:`pre-requisite-reference-label` are met.
This module verifies that each of the PVs listed in the configuration file
exist and their values are set within the predefined range.
The results will be reported in a file (printed on screen for now).
An error will be reported back to UI via PV.
"""
import os
import sys
import json
import h5py
import dquality.common.utilities as utils
from dquality.common.utilities import lt, le, eq, ge, gt
__author__ = "Barbara Frosik"
__copyright__ = "Copyright (c) 2016, UChicago Argonne, LLC."
__docformat__ = 'restructuredtext en'
__all__ = ['init',
'verify',
'verify_list',
'find_value']
function_mapper = {'less_than': lt, 'less_or_equal': le,
'equal': eq, 'greater_or_equal': ge, 'greater_than': gt}
[docs]def init(config):
"""
This function initializes global variables. It gets values from the configuration file, evaluates and processes
the values. If mandatory file or directory is missing, the script logs an error and exits.
Parameters
----------
config : str
configuration file name, including path
Returns
-------
logger : Logger
logger instance
dep : dictionary
a dictionary containing dependency values read from the configured 'dependencies' file
"""
conf = utils.get_config(config)
logger = utils.get_logger(__name__, conf)
dependencies = utils.get_file(conf, 'dependencies', logger)
if dependencies is None:
sys.exit(-1)
with open(dependencies) as file:
dep = json.loads(file.read())
return logger, dep
class TagValue:
value = 0
def __init__(self, tag):
self.tag = tag
def set_value(self, value):
self.value = value
def get_value(self):
return self.value
[docs]def find_value(tag, dset):
"""
This function takes tag parameter and a corresponding dataset from
the hd5 file. The tag can be a simple hd5 member tag or extended tag.
This function assumes that the extended tag is a string containing
hd5 tag, a word indicating the tag category (i.e. "*dim*" meaning
dimension), and a parameter (i.e. numeric index). In the case of a
simple hd5 tag the function returns a value of this member.
In the second case the function returns decoded value, in the
case of "dim" category, it returns the indexed dimension.
Parameters
----------
tag : str
a simple hd5 tag or extended
dset : dataset
hd5 dataset corresonding to the tag parameter
Returns
-------
value of decoded tag
"""
tag_def = tag.split()
if len(tag_def) == 1:
return dset.value
else:
if tag_def[1] == 'dim':
axis = tag_def[2]
return dset.shape[int(axis)]
[docs]def verify_list(file, list, relation, logger):
"""
This function takes an hd5 file, a list of tags (can be extended)
and a relation between the list members. First the method creates
a tags dictionary from the list of tags. The key is a simple hd5
tag, and the value is a newly created TagValue instance that
contains extended tag (or simple tag if simple tag was in the
list), and a placeholder for the value. The first tag from the
list is retained as an anchor.
The function travers through all tags in the given file. If the
tag name is found in the tags dictionary, the ```find_value```
method is called to retrieve a defined valueu referenced by the
tag. The value is then added to the TagValue instance for this tag.
When all tags are processed the function iterates over the tags
dictionary to find if the relation between anchor value and other
values can be verified. If any relation is not true, a report is
printed for this tag, and the function will return ```False```.
Otherwise the function returns ```True```.
Parameters
----------
file : file
an hd5 file to be verified
list : list
list of extended or simple hd5 tags
relation : str
a string specifying the relation between tags in the list
logger : Logger
a Logger instance
Returns
-------
boolean
"""
# create dictionary of tag : tag with parameters for fast lookup
anchor_tag = TagValue(list[0])
tags = {}
for long_tag in list:
tags[long_tag.split()[0]] = TagValue(long_tag)
def func(name, dset):
if len(list) != 0 and isinstance(dset, h5py.Dataset):
tag = dset.name
try:
full_tag = tags[tag].tag
list.remove(full_tag)
value = find_value(full_tag, dset)
tags[tag].set_value(value)
if full_tag == anchor_tag.tag:
anchor_tag.set_value(value)
del tags[tag]
except KeyError:
pass
file_h5 = h5py.File(file, 'r')
file_h5.visititems(func)
res = True
for tag in tags:
if not function_mapper[relation](
tags.get(tag).get_value(),
anchor_tag.get_value()):
logger.warning('the ' + tag + ' value is ' +
str(tags.get(tag).get_value()) +
' but should be ' + relation +
' ' + str(anchor_tag.get_value()))
res = False
return res
[docs]def verify(conf, file):
"""
This function reads the json "*dependencies*" file from the
:download:`dqconfig.ini <../../../config/default/dqconfig.ini>` file.
This file contains dictionary with keys of relations between tags.
The value is a list of lists. The relation applies to the tags in
inner list respectively. For example if the relation is "*equal*",
all tags in each inner list must be equal,The outer list hold the
lists that apply the relation. A first element in a inner list is
an "*anchor*" element, so all elements are compared to it. This
is important for the "*less_than*" type of relation, when the
order of parameters is important.
The allowed keys are:
- "*less_than*" - the PV value must be less than attribute value
- "*less_or_equal*" - the PV value must be less than or equal attribute value
- "*equal*" - the PV value must be equal to attribute value
- "*greater_or_equal*" - the PV value must be greater than or equal attribute value
- "*greater_than*" - the PV value must be greater than attribute value
The tag in a "*dependencies*" file can be an hd5 tag, or can have
appended keyword "*dim*" and an numeric value indicating axis. If
the tag is simple hd5 tag, the verifier compares the value of this tag.
If it has the "*dim*" keyword appended, the verifier compares a
specified dimension.
Any vakue that does not agree with the configured relation is
reported (printed for now). The function returns True if no error
was found and False otherwise.
Parameters
----------
conf : str
configuration file name, including path
file : str
File Name to verify including path
Returns
-------
boolean
"""
logger, dependencies = init(conf)
if not os.path.isfile(file):
logger.error(
'parameter error: file ' +
file + ' does not exist')
sys.exit(-1)
res = True
i = 0
for relation in dependencies:
batch = dependencies[relation]
for tag_list in batch:
if not verify_list(file, tag_list, relation, logger):
res = False
return res