source: epics2xml/epics2www.py @ 76

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

initial commit

  • Property svn:eol-style set to native
  • Property svn:executable set to *
  • Property svn:keywords set to Rev Date Author Id Url
File size: 19.8 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 15:49:21 +0000 (Fri, 06 Nov 2009) $
17# $Author: jemian $
18# $Revision: 75 $
19# $URL$
20# $Id: epics2www.py 75 2009-11-06 15:49:21Z 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
29import copy
30
31
32class Epics2Www:
33    '''watch PVs and write to XMl'''
34
35    def __init__(self, settingsFile=None):
36        '''prepare the settings file
37            @param settingsFile: [string] name of XML file with settings'''
38        self.rootElement = 'epics2www'
39        self.Clear()
40        self.SetSettingsFile(settingsFile)
41
42    def GetDb(self):
43        '''@return: database'''
44        return self.db
45
46    def GetSettingsFile(self):
47        '''@return: name of XML settings file'''
48        return self.settingsFile
49
50    def SetSettingsFile(self, thefile):
51        '''set the name of XML settings file
52            @param thefile: [string] name of XML file with settings'''
53        self.settingsFile = thefile
54
55    def Clear(self):
56        '''reset the internal data representation (db) to empty'''
57        self.db = {}
58
59    def NewPair(self, title=''):
60        ''' create space in the database (db) for a new pair
61            and sets defaults for fields
62
63            @param title: [string] the title of the XY_pair set (default="")
64            @return: the index number'''
65        if self.CountPairs() == -1:
66            self.db[u"pairs"] = []
67        pairdb = {}
68        pairdb[u"@name"] = title
69        pairdb[u"@selected"] = False
70        self.db[u'pairs'].append(pairdb)
71        return len(self.db[u'pairs'])-1
72
73    def GetPairTitle(self, pairnum):
74        '''return the name of the XY_pair
75            @param pairnum: [int] index number of the XY_pair'''
76        return self.db[u"pairs"][pairnum][u"@name"]
77
78    def SetPairTitle(self, pairnum, title):
79        '''set the name of the XY_pair
80            @param pairnum: [int] index number of the XY_pair'
81            @param title: [string] name of the XY_pair'''
82        self.db[u"pairs"][pairnum][u"@name"] = title
83
84    def SelectPair(self, pairnum):
85        '''set the "selected" attribute of the pair
86            @param pairnum: [int] index number of the XY_pair'''
87        for pair in self.db[u"pairs"]:   # first, deselect all pairs
88            pair[u"@selected"] = False
89        self.db[u"pairs"][pairnum][u"@selected"] = True
90
91    def GetSelectedPair(self):
92        '''@return: index number of the "selected" pair (-1 if none selected)'''
93        selected = -1
94        try:
95            pairs = self.db[u"pairs"]
96            for pairnum in range(len(pairs)):
97                if pairs[pairnum][u"@selected"]:
98                    selected = pairnum
99                    break
100        except:
101            pass
102        return selected
103
104    def CountPairs(self):
105        '''@return: number of pairs'''
106        try:
107            return len(self.db[u"pairs"])
108        except:
109            return -1
110
111    def NewEpicsConfig(self, pairnum):
112        '''Create internal space for a new EPICS configuration
113            @param pairnum: [int] index number of the XY_pair'''
114        pairdb = self.db[u"pairs"][pairnum]
115        pairdb[u"epics"] = {}
116        epicsdb = pairdb[u"epics"]
117        for axis in ['x', 'y']:
118            epicsdb[axis] = {}
119            axisdb = epicsdb[axis]
120            for field in wxmtxy_axis.field_list:
121                axisdb[field] = ""
122            axisdb[u"isMotorRec"] = False
123
124    def GetEpicsConfig(self, pairnum):
125        '''Get a deep copy Python dictionary of the current EPICS PV config.
126            @param pairnum: [int] index number of the XY_pair
127            @return: the current EPICS configuration'''
128        return copy.deepcopy(self.db[u"pairs"][pairnum][u"epics"])
129
130    def SetEpicsConfig(self, pairnum, config):
131        '''set the current EPICS configuration
132            @param pairnum: [int] index number of the XY_pair
133            @param config: Python dictionary of EPICS PV configuration'''
134        pairdb = self.db[u"pairs"][pairnum]
135        pairdb[u"epics"] = copy.deepcopy(config)
136
137    def SetEpicsField(self, pairnum, axis, field, value):
138        '''Define the EPICS config for a specific field
139            @param pairnum: [int] index number of the XY_pair'
140            @param axis: [string] "x" or "y"'
141            @param field: [string] member of wxmtxy_axis.field_list'
142            @param value: [string] value of this field'''
143        try:
144            axisdb = self.db[u"pairs"][pairnum][u"epics"][axis]
145            axisdb[field] = value
146        except:
147            print "Could not assign EPICS field", pairnum, axis, field, value
148
149
150    def NewTab(self, pairnum, title=''):
151        ''' create space in the database (db) pair for a new tab
152            and sets defaults for fields
153
154            @param pairnum: [int] index number of the XY_pair
155            @param title: the title of the pair set (default="")
156            @return the index number'''
157        pairdb = self.db[u"pairs"][pairnum]
158        if not pairdb.has_key(u"tabs"):
159            pairdb[u"tabs"] = []
160        tabdb = {}
161        tabdb[u"@name"] = title
162        tabdb[u"@selected"] = False
163        pairdb[u"tabs"].append(tabdb)
164        return len(pairdb[u"tabs"])-1
165
166    def GetTabTitle(self, pairnum, tabnum):
167        '''return the name of the tab
168            @param pairnum: [int] index number of the XY_pair
169            @param tabnum: [int] index number of the Tab object'''
170        return self.db[u"pairs"][pairnum][u"tabs"][tabnum][u"@name"]
171
172    def SetTabTitle(self, pairnum, tabnum, title):
173        '''set the name attribute of the tab
174            @param pairnum: [int] index number of the XY_pair
175            @param tabnum: [int] index number of the Tab object
176            @param title: [string] title the Tab object'''
177        self.db[u"pairs"][pairnum][u"tabs"][tabnum]["@name"] = title
178
179    def SelectTab(self, pairnum, tabnum):
180        '''set the selected attribute of the pair
181            @param pairnum: [int] index number of the XY_pair
182            @param tabnum: [int] index number of the Tab object'''
183        pairdb = self.db[u"pairs"][pairnum]
184        for tab in pairdb[u"tabs"]:   # first, deselect all tabs
185            tab[u"@selected"] = False
186        pairdb[u"tabs"][tabnum][u"@selected"] = True
187
188    def GetSelectedTab(self, pairnum):
189        '''return the index number of the selected tab
190            @param pairnum: [int] index number of the XY_pair'''
191        selected = -1
192        try:
193            tabs = self.db[u"pairs"][pairnum][u"tabs"]
194            for tabnum in range(len(tabs)):
195                if tabs[tabnum][u"@selected"]:
196                    selected = tabnum
197                    break
198        except:
199            pass
200        return selected
201
202    def CountTabs(self, pairnum):
203        '''return the number of tabs
204            @param pairnum: [int] index number of the XY_pair'''
205        try:
206            return len(self.db[u"pairs"][pairnum][u"tabs"])
207        except:
208            return -1
209
210    def NewRow(self, pairnum, tabnum, title=''):
211        ''' create space in the database (db) pair for a new tab
212            and sets defaults for fields
213
214            @param pairnum: [int] index number of the XY_pair
215            @param tabnum: [int] index number of the Tab object
216            @param title: the title of the Tab object (default="")
217            @return the index number'''
218        tabdb = self.db[u"pairs"][pairnum][u"tabs"][tabnum]
219        if not tabdb.has_key(u"rows"):
220            tabdb[u"rows"] = []
221        rowdb = {}
222        rowdb[u"@name"] = title
223        rowdb[u"@selected"] = False
224        rowdb[u"@x"] = ""
225        rowdb[u"@y"] = ""
226        tabdb[u"rows"].append(rowdb)
227        return len(tabdb[u"rows"])-1
228
229    def GetRowTitle(self, pairnum, tabnum, rownum):
230        '''return the name of the row
231            @param pairnum: [int] index number of the XY_pair
232            @param tabnum: [int] index number of the Tab object
233            @param rownum: [int] index number of the Row object'''
234        tabdb = self.db[u"pairs"][pairnum][u"tabs"][tabnum]
235        return tabdb[u"rows"][rownum][u"@name"]
236
237    def SetRowTitle(self, pairnum, tabnum, rownum, title):
238        '''set the name attribute of the row
239            @param pairnum: [int] index number of the XY_pair
240            @param tabnum: [int] index number of the Tab object
241            @param rownum: [int] index number of the Row object
242            @param title: [string] title the Tab object'''
243        tabdb = self.db[u"pairs"][pairnum][u"tabs"][tabnum]
244        tabdb[u"rows"][rownum][u"@name"] = title
245
246    def GetRowXY(self, pairnum, tabnum, rownum):
247        '''return the name of the row
248            @param pairnum: [int] index number of the XY_pair
249            @param tabnum: [int] index number of the Tab object
250            @param rownum: [int] index number of the Row object'''
251        tabdb = self.db[u"pairs"][pairnum][u"tabs"][tabnum]
252        x = tabdb[u"rows"][rownum][u"@x"]
253        y = tabdb[u"rows"][rownum][u"@y"]
254        return x, y
255
256    def SetRowXY(self, pairnum, tabnum, rownum, x, y):
257        '''set the name attribute of the row
258            @param pairnum: [int] index number of the XY_pair
259            @param tabnum: [int] index number of the Tab object
260            @param x: [float] X axis position
261            @param y: [float] Y axis position'''
262        tabdb = self.db[u"pairs"][pairnum][u"tabs"][tabnum]
263        tabdb[u"rows"][rownum][u"@x"] = x
264        tabdb[u"rows"][rownum][u"@y"] = y
265
266    def CountRows(self, pairnum, tabnum):
267        '''return the number of rows
268            @param pairnum: [int] index number of the XY_pair
269            @param tabnum: [int] index number of the Tab object'''
270        try:
271            return len(self.db[u"pairs"][pairnum][u"tabs"][tabnum][u"rows"])
272        except:
273            return -1
274
275    def GetSelectedRow(self, pairnum, tabnum):
276        '''return the index number of the selected row
277            @param pairnum: [int] index number of the XY_pair
278            @param tabnum: [int] index number of the Tab object'''
279        selected = -1
280        try:
281            rows = self.db[u"pairs"][pairnum][u"tabs"][tabnum][u"rows"]
282            for rownum in range(len(rows)):
283                if rows[rownum][u"@selected"]:
284                    selected = rownum
285                    break
286        except:
287            pass
288        return selected
289
290    def SelectRow(self, pairnum, tabnum, rownum):
291        '''set the selected attribute of the pair
292            @param pairnum: [int] index number of the XY_pair
293            @param tabnum: [int] index number of the Tab object
294            @param rownum: [int] index number of the Row object'''
295        tabdb = self.db[u"pairs"][pairnum][u"tabs"][tabnum]
296        for row in tabdb[u"rows"]:   # first, deselect all rows
297            row[u"@selected"] = False
298        tabdb[u"rows"][rownum][u"@selected"] = True
299
300    def ReadXmlFile(self):
301        '''read the settings from a file into an internal dictionary (self.db)
302
303            @note: this method uses xml.dom.minidom (built into all Pythons)
304            @see: http://docs.python.org/library/xml.dom.minidom.html
305        '''
306        try:
307            doc = minidom.parse(self.settingsFile) # parse an XML file by name
308            assert doc.documentElement.tagName == self.rootElement
309        except IOError:
310            return 'Could not read the XML file: ' + self.settingsFile
311        except AssertionError:
312            return 'XML root element is not ' + self.rootElement
313        #... read all attributes from the root element
314        docElem = doc.documentElement
315        self.Clear()
316        version = self._get_attribute(docElem, "version", "not-specified")
317        try:
318            # only handle v1.0 resource configuration files
319            assert version == u'1.0'
320        except AssertionError:
321            doc.unlink()
322            return 'Cannot handle file version:', version
323        # file verified now
324        for pairNode in docElem.getElementsByTagName("XYpair"):
325            title = self._get_attribute(pairNode, "name", "")
326            selected = self._get_attribute(pairNode, "selected", "")
327            pairnum = self.NewPair(title)
328            if selected.lower() == "true":
329                self.SelectPair(pairnum)
330            self.NewEpicsConfig(pairnum)
331            for EpicsNode in pairNode.getElementsByTagName("EPICS_configuration"):
332                for axisNode in EpicsNode.getElementsByTagName("axis"):
333                    axis = self._get_attribute(axisNode, "name", "")
334                    #isMotorRec
335                    for flagNode in axisNode.getElementsByTagName("flag"):
336                        text = self._get_attribute(flagNode, "isMotorRec", "False")
337                        self.SetEpicsField(pairnum, axis, "isMotorRec", (text == "True"))
338                    for fieldNode in axisNode.getElementsByTagName("field"):
339                        name = self._get_attribute(fieldNode, "name", "")
340                        pv = self._get_attribute(fieldNode, "pv", "")
341                        if (len(name)>0) and (len(pv)>0):
342                            #print pairnum, axis, name, pv
343                            self.SetEpicsField(pairnum, axis, name, pv)
344            for tabNode in pairNode.getElementsByTagName("tab"):
345                title = self._get_attribute(tabNode, "name", "")
346                selected = self._get_attribute(tabNode, "selected", "")
347                tabnum = self.NewTab(pairnum, title)
348                if selected.lower() == "true":
349                    self.SelectTab(pairnum, tabnum)
350                # EPICS settings here
351                for rowNode in tabNode.getElementsByTagName("row"):
352                    title = self._get_attribute(rowNode, "name", "")
353                    selected = self._get_attribute(rowNode, "selected", "")
354                    x = self._get_attribute(rowNode, "x", "")
355                    y = self._get_attribute(rowNode, "y", "")
356                    rownum = self.NewRow(pairnum, tabnum, title)
357                    self.SetRowXY(pairnum, tabnum, rownum, x, y)
358        doc.unlink()  # ensures XML document is disposed cleanly
359        return None
360
361    def _get_attribute(self, node, key, default):
362        '''get a specific attribute or return the default
363            @param node: XML Node object
364            @param key: [string] name of attribute to find
365            @param default: [string] default value to return'''
366        value = default
367        if node.attributes.has_key(key):
368            value = node.attributes[key].value
369        return value
370
371    def SaveXmlFile(self):
372        '''save the internal dictionary (self.db) to an XML file
373           @note: What about using/saving a default stylesheet?
374           @see: http://www.boddie.org.uk/python/XML_intro.html
375        '''
376        out = open(self.settingsFile, 'w')
377        out.write(repr(self))
378        out.close()
379        #
380        # What about a default stylesheet?
381        #
382       
383        return 'Saved settings to ' + self.settingsFile
384
385    def _SetAttr(self, node, attribute, value):
386        '''add attributes that are not empty (but do not strip the whitespace)
387            @param node: XML Node object
388            @param attribute: [string] name of attribute
389            @param value: [string] value of attribute'''
390        if len(value) > 0:
391            node.setAttribute(attribute, value)
392
393    def _makeTextNode(self, doc, tag, value):
394        '''create a text node for the XML file
395            @param doc: [xml.dom.minidom documentElement object]
396            @param tag: [string] element name
397            @param value: [string] element text'''
398        node = doc.createElement(tag)
399        text = doc.createTextNode(value)
400        node.appendChild(text)
401        return node
402
403    def __repr__(self):
404        '''default representation of this structure is XML
405            @return: XML representation of internal database (db)
406            @note: What about a default stylesheet?
407        '''
408        t = datetime.datetime.now()
409        yyyymmdd = t.strftime("%Y-%m-%d")
410        hhmmss = t.strftime("%H:%M:%S")
411
412        # Create the minidom document
413        doc = minidom.Document()
414
415        # Create the root element
416        root = doc.createElement(self.rootElement)
417        self._SetAttr(root, "version", "1.0")
418        self._SetAttr(root, "date", yyyymmdd)
419        self._SetAttr(root, "time", hhmmss)
420        doc.appendChild(root)
421        selectedpairnum = self.GetSelectedPair()
422        for pairnum in range(self.CountPairs()):
423            pairnode = doc.createElement("XYpair")
424            self._SetAttr(pairnode, "name", 
425                   self.GetPairTitle(pairnum))
426            if selectedpairnum == pairnum:
427                self._SetAttr(pairnode, "selected", "True")
428            if self.db[u"pairs"][pairnum].has_key(u"epics"):
429                epicsnode = doc.createElement("EPICS_configuration")
430                epicsdb = self.db[u"pairs"][pairnum][u"epics"]
431                for axis in epicsdb:
432                    axisnode = doc.createElement("axis")
433                    self._SetAttr(axisnode, "name", axis)
434                    field = "isMotorRec"
435                    node = doc.createElement("flag")
436                    self._SetAttr(node, field, str(epicsdb[axis][field]))
437                    axisnode.appendChild(node)
438                    for field in wxmtxy_axis.field_list:
439                        if len(epicsdb[axis][field])>0:
440                            node = doc.createElement("field")
441                            self._SetAttr(node, "name", field)
442                            self._SetAttr(node, "pv", str(epicsdb[axis][field]))
443                            axisnode.appendChild(node)
444                    epicsnode.appendChild(axisnode)
445                pairnode.appendChild(epicsnode)
446            selectedtabnum = self.GetSelectedTab(pairnum)
447            for tabnum in range(self.CountTabs(pairnum)):
448                tabnode = doc.createElement("tab")
449                self._SetAttr(tabnode, "name", self.GetTabTitle(pairnum, tabnum))
450                if selectedtabnum == tabnum:
451                    self._SetAttr(tabnode, "selected", "True")
452                selectedrownum = self.GetSelectedRow(pairnum, tabnum)
453                for rownum in range(self.CountRows(pairnum, tabnum)):
454                    rownode = doc.createElement("row")
455                    label = self.GetRowTitle(pairnum, tabnum, rownum)
456                    x, y = self.GetRowXY(pairnum, tabnum, rownum)
457                    self._SetAttr(rownode, "name", label)
458                    self._SetAttr(rownode, "x", x)
459                    self._SetAttr(rownode, "y", y)
460                    if selectedrownum == rownum:
461                        self._SetAttr(rownode, "selected", "True")
462                    tabnode.appendChild(rownode)
463                pairnode.appendChild(tabnode)
464            root.appendChild(pairnode)
465        return doc.toprettyxml(indent="  ")
466
467
468if __name__ == '__main__':
469    rc = Settings("examples/test-settings.xml")
470    rc.ReadXmlFile()
471    rc.SetSettingsFile('output-test.xml')
472    rc.SaveXmlFile()
473
474    pj = Settings()
475    pairnum = pj.NewPair("my test pair")
476    tabnum = pj.NewTab(pairnum, title='my test tab')
477    tabnum = pj.NewTab(pairnum, title='another')
478    pj.SelectTab(pairnum, tabnum)
479    tabnum = pj.NewTab(pairnum, title='another')
480    tabnum = pj.NewTab(pairnum, title='another')
481    pairnum = pj.NewPair("another")
482    pj.SelectPair(pairnum)
483    tabnum = pj.NewTab(pairnum, title='another')
484    rownum = pj.NewRow(pairnum, tabnum, "beam center")
485    pj.SelectRow(pairnum, tabnum, rownum)
486    tabnum = pj.NewTab(pairnum, title='another')
487    pairnum = pj.NewPair("another")
488    print str(pj)
Note: See TracBrowser for help on using the repository browser.