source: epics2xml/epics2xml.py @ 83

Last change on this file since 83 was 83, checked in by jemian, 14 years ago

Refs #2: progress ...

  • Property svn:eol-style set to native
  • Property svn:executable set to *
  • Property svn:keywords set to Rev Date Author Id Url
File size: 9.5 KB
Line 
1#!/usr/bin/env python
2
3#*************************************************************************
4# Copyright (c) 2009 The University of Chicago, as Operator of Argonne
5#     National Laboratory.
6# Copyright (c) 2009 The Regents of the University of California, as
7#     Operator of Los Alamos National Laboratory.
8# This file is distributed subject to a Software License Agreement found
9# in the file LICENSE that is included with this distribution.
10#*************************************************************************
11
12'''@note: watch EPICS process variables and write them to an XML file
13
14@version:
15########### SVN repository information ###################
16# $Date: 2009-11-06 18:30:04 +0000 (Fri, 06 Nov 2009) $
17# $Author: jemian $
18# $Revision: 83 $
19# $URL$
20# $Id: epics2xml.py 83 2009-11-06 18:30:04Z jemian $
21########### SVN repository information ###################
22
23@note: for help with xml.dom, see http://docs.python.org/library/xml.dom.html
24'''
25
26
27from xml.dom import minidom
28import datetime
29#import copy
30#import pvConnect
31
32
33class Epics2Xml:
34    '''watch PVs and write to XMl'''
35
36    def __init__(self, configFile=None):
37        '''prepare the settings file
38            @param configFile: [string] name of XML file with settings'''
39        self.rootElementConfig = 'epics2xml'
40        self.rootElementPvdata = 'pvdata'
41        self.Clear()
42        self.SetConfigFile(configFile)
43
44    def GetDb(self):
45        '''@return: database'''
46        return self.db
47
48    def GetConfigFile(self):
49        '''@return: name of XML configuration file'''
50        return self.configFile
51
52    def SetConfigFile(self, thefile):
53        '''set the name of XML configuration file
54            @param thefile: [string] name of XML file with the configuration'''
55        self.configFile = thefile
56
57    def Clear(self):
58        '''reset the internal data representations (db, pv) to empty'''
59        self.db = {}
60        self.pv = {}
61
62    def ReadConfigFile(self):
63        '''read the configuration from an XML file into an internal dictionary (self.db)
64
65            @note: this method uses xml.dom.minidom (built into all Pythons)
66            @see: http://docs.python.org/library/xml.dom.minidom.html
67        '''
68        try:
69            doc = minidom.parse(self.configFile) # parse an XML file by name
70            assert doc.documentElement.tagName == self.rootElementConfig
71        except IOError:
72            return 'Could not read the XML file: ' + self.configFile
73        except AssertionError:
74            return 'XML root element is not ' + self.rootElementConfig
75        #... read all attributes from the root element
76        docElem = doc.documentElement
77        self.Clear()
78        version = self._get_attribute(docElem, "version", "not-specified")
79        try:
80            # only handle v1.0 resource configuration files
81            assert version == u'1.0'
82        except AssertionError:
83            doc.unlink()
84            return 'Cannot handle file version:', version
85        # file verified now
86        self.db['title'] = self._getFirstChildData(docElem, "title", "default title")
87        tfNode = docElem.getElementsByTagName("targetFile")[0]
88        tf_db = {}
89        tf_db['name'] = self._get_attribute(tfNode, "name", "").strip()
90        tf_db['refreshMinimum'] = self._getFirstChildData(tfNode, "refreshMinimum", "60")
91        tf_db['stylesheet'] = self._getFirstChildData(tfNode, "stylesheet", "basic-table.xsl")
92        # refreshMinimum
93        self.db['targetFile'] = tf_db
94        pvlist_db = []
95        for elementNode in docElem.getElementsByTagName("pv"):
96            pv_db = {}
97            pv_db['name'] = self._get_attribute(elementNode, "name", "").strip()
98            pv_db['rate'] = self._get_attribute(elementNode, "rate", "monitor").strip()
99            pv_db['description'] = self._getFirstChildData(elementNode, "description", "")
100            pvlist_db.append(pv_db)
101        self.db['pvList'] = pvlist_db
102
103    def _getFirstChildData(self, parent, tag, default):
104        '''get the full content of the node or return the default
105            @param parent: XML Node object
106            @param tag: [string] name of element to find
107            @param default: [string] default value to return'''
108        str = default
109        # need some error checking here
110        nodeList = parent.getElementsByTagName(tag)
111        node = nodeList[0]
112        child = node.firstChild
113        data = child.data
114        str = data.strip()
115        return str
116
117    def _get_attribute(self, node, key, default):
118        '''get a specific attribute or return the default
119            @param node: XML Node object
120            @param key: [string] name of attribute to find
121            @param default: [string] default value to return'''
122        value = default
123        if node.attributes.has_key(key):
124            value = node.attributes[key].value
125        return value
126
127    def SaveXmlFile(self):
128        '''save the internal data (self.__repr__()) to the target XML file'''
129        out = open(self.db['targetFile']['name'], 'w')
130        out.write(repr(self))
131        out.close()
132        return 'Saved settings to ' + self.configFile
133
134    def _SetAttr(self, node, attribute, value):
135        '''add attributes that are not empty (but do not strip the whitespace)
136            @param node: XML Node object
137            @param attribute: [string] name of attribute
138            @param value: [string] value of attribute'''
139        if len(value) > 0:
140            node.setAttribute(attribute, value)
141
142    def _makeTextNode(self, doc, tag, value):
143        '''create a text node for the XML file
144            @param doc: [xml.dom.minidom documentElement object]
145            @param tag: [string] element name
146            @param value: [string] element text'''
147        node = doc.createElement(tag)
148        text = doc.createTextNode(value)
149        node.appendChild(text)
150        return node
151
152    def _getDateTime(self):
153        '''return current date and time as a tuple
154            @return: yyyy-mm-dd hh:mm:ss
155        '''
156        t = datetime.datetime.now()
157        yyyymmdd = t.strftime("%Y-%m-%d")
158        hhmmss = t.strftime("%H:%M:%S")
159        return yyyymmdd, hhmmss
160
161    def getPvKey(self, name, key, default):
162        '''get the internal database representation of the PV key
163            or the default
164            @param name: EPICS PV name
165            @param key: value | date | time
166            @param default: use if key not defined'''
167        if self.pv.has_key(name):
168            pv = self.pv[name]
169            if pv.has_key(key):
170                content = pv[key]
171            else:
172                content = default
173        else:
174            content = default
175        return content
176
177    def setPvKey(self, name, key, value):
178        '''set the internal database representation of the PV key
179            @param name: EPICS PV name
180            @param key: value | date | time
181            @param value: new content from this EPICS PV
182        '''
183        if not self.pv.has_key(name):
184            self.pv[name] = {}
185        pv = self.pv[name]
186        pv[key] = value
187
188    def setPv(self, name, value):
189        '''save this PV now
190            @param name: EPICS PV name
191            @param value: new content from this EPICS PV
192        '''
193        yyyymmdd, hhmmss = self._getDateTime()
194        self.setPvKey(name, 'value', value)
195        self.setPvKey(name, 'date', yyyymmdd)
196        self.setPvKey(name, 'time', hhmmss)
197
198    def __repr__(self):
199        '''default representation of this structure is XML
200            @return: XML representation of internal database (db)
201            @note: What about a default stylesheet?
202        '''
203        # Create the minidom document
204        doc = minidom.Document()
205
206        # Use a stylesheet
207        target = "xml-stylesheet"
208        instructions = 'type="text/xsl" href="%s" ' % (self.db['targetFile']['stylesheet'])
209        #
210        xsltNode = doc.createProcessingInstruction(target, instructions)
211        doc.appendChild(xsltNode)
212
213        # Create the root element
214        root = doc.createElement(self.rootElementPvdata)
215        self._SetAttr(root, "version", "1.0")
216        yyyymmdd, hhmmss = self._getDateTime()
217        self._SetAttr(root, "date", yyyymmdd)
218        self._SetAttr(root, "time", hhmmss)
219        doc.appendChild(root)
220        titleNode = self._makeTextNode(doc, "title", self.db['title'])
221        root.appendChild(titleNode)
222        for pv in self.db['pvList']:
223            pvNode = doc.createElement('pv')
224            name = pv['name']
225            pv_value = self.getPvKey(name, "value", "")
226            pv_date = self.getPvKey(name, "date", "")
227            pv_time = self.getPvKey(name, "time", "")
228            self._SetAttr(pvNode, "name", pv['name'])
229            self._SetAttr(pvNode, "rate", pv['rate'])
230            self._SetAttr(pvNode, "date", pv_date)
231            self._SetAttr(pvNode, "time", pv_time)
232            pvNode.appendChild(self._makeTextNode(doc, "description", pv['description']))
233            pvNode.appendChild(self._makeTextNode(doc, "value", pv_value))
234            root.appendChild(pvNode)
235        return doc.toprettyxml(indent="  ")
236
237    def receiver(self, epics_args, user_args):
238        '''Example response to an EPICS monitor on the channel
239           @param value: str(epics_args['pv_value'])'''
240        value = epics_args['pv_value']
241        print 'receiver', 'updated value:', str(value)
242
243
244if __name__ == '__main__':
245    cf = Epics2Xml("config.xml")
246    cf.ReadConfigFile()
247    # connect with EPICS here and start waiting for events
248    # How to save periodically?
249    # How to throttle the output file update rate?
250   
251    #----------------------------
252    # load some artificial data to test
253    cf.setPv("S:SRcurrent:AI", '99.9853')
254    cf.setPv("APS:BarometricPressure:MBR", '992.083')
255    print cf.__repr__()
256    #----------------------------
Note: See TracBrowser for help on using the repository browser.