source: topdoc/src/TopDoc/EpicsDatabase.py @ 348

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

progress - refactored expectation of iocBoot directory -- need to refactor EpicsDatabase? like EpicsTemplate?

  • Property svn:eol-style set to native
  • Property svn:executable set to *
  • Property svn:keywords set to Date Revision Author HeadURL Id
File size: 7.5 KB
Line 
1#!/usr/bin/env python
2
3'''
4Created on Feb 10, 2011
5
6@author: Pete
7########### SVN repository information ###################
8# $Date: 2011-03-12 06:26:53 +0000 (Sat, 12 Mar 2011) $
9# $Author: jemian $
10# $Revision: 348 $
11# $URL: topdoc/src/TopDoc/EpicsDatabase.py $
12# $Id: EpicsDatabase.py 348 2011-03-12 06:26:53Z jemian $
13########### SVN repository information ###################
14'''
15
16
17import os
18import pprint
19import utilities
20
21
22class Db():
23    '''
24    Read an EPICS .db database file into memory.
25    The internal representation of the file is in self.pvDict.
26    This is a Python dictionary of dictionaries.  Each outer dictionary
27    item is for a specific instance of an EPICS process variable (PV).
28    The internal dictionary keys are field names and the dictionary values
29    are the field values before macro expansion.
30    Important methods provided are:
31        countPVs()
32        getPVs()
33        getFull()
34        keyName()
35        xrefDict()
36    '''
37    filename = None
38    absolute_filename = None
39    buf = None
40    pvDict = {}
41    cache_xref = None
42    cache_macros = None
43
44    def __init__(self, dbFilename):
45        '''
46        Constructor.  Try to read and interpret the named file.
47        @param dbFilename: string with name of .db file to open
48        '''
49        if os.path.exists(dbFilename):
50            self.filename = dbFilename
51            self.absolute_filename = os.path.abspath(dbFilename)
52            self.buf = utilities.tokenizeTextFile(dbFilename)
53            self._parse()
54
55    def xrefDict(self, macros = {}):
56        '''
57        Make a dictionary that cross-references the 'NAME' field
58        between defined as macro-expanded values.  Both forward
59        and reverse definitions are included.
60        @param macros: dictionary of macro substitutions.
61        @return: cross-reference dictionary
62        '''
63        # make this more efficient for repeated calls by caching the xref
64        if self.cache_macros == macros:
65            return self.cache_xref
66        xref = {}
67        for k, v in self.pvDict.items():
68            name = utilities.replaceMacros( v['NAME'], macros )
69            xref[k] = name
70            xref[name] = k
71        self.cache_macros = macros
72        self.cache_xref = xref
73        return xref
74
75    def keyName(self, pvName, macros = {}):
76        '''
77        Find the dictionary key that expands to pvName.
78        This is easy to get from the cross-reference dictionary.
79        (This actually works either way since it uses the xrefDict() method.)
80        @param macros: dictionary of macro substitutions.
81        @return: name of dictionary key or None if not found
82        '''
83        try:
84            return self.xrefDict(macros)[pvName]
85        except:
86            return None
87
88    def countPVs(self):
89        '''
90        @return: number of PVs that have been defined
91        '''
92        return len(self.pvDict)
93
94    def getPVs(self, macros = {}):
95        '''
96        Get a list of the EPICS PVs defined with optional macro substitutions.
97        The result with no macro substitutions is trivial.
98        @param macros: dictionary of macro substitutions.
99        @return: list PV names or None if no PVs records have been defined
100        '''
101        keys = self.pvDict.keys()
102        result = [utilities.replaceMacros( item, macros ) for item in keys]
103        return result
104
105    def getFull(self, macros = {}):
106        '''
107        Get the full specification of the PVs and fields with optional macro substitutions.
108        The result with no macro substitutions is trivial.
109        @param macros: dictionary of macro substitutions. Keys are expanded, also.
110        @return: dictionary of dictionaries
111        '''
112        result = {}
113        for key, fieldDict in self.pvDict.items():
114            k = utilities.replaceMacros( key, macros )
115            result[k] = {}
116            for field, value in fieldDict.items():
117                v = utilities.replaceMacros( value, macros )
118                result[k][field] = utilities.replaceMacros( v, macros )
119        return result
120
121    def _parse(self):
122        '''
123        Interpret the contents of the .db file
124        into the internal memory structure.
125        '''
126        if self.buf is not None:
127            pvDict = {}
128            #count open parentheses and open braces and rely on state
129            n_parens = 0
130            n_braces = 0
131            newRecordStart = False
132            newFieldStart = False
133            argList = []
134            for tok in self.buf:
135                arg0 = tok['tokStr']
136                if tok['tokName'] in ('COMMENT', 'NEWLINE'):
137                    continue
138                elif arg0 in ('record', 'grecord'):
139                    if n_parens == 0 and n_braces == 0:
140                        newRecordStart = True
141                        fieldDict = {}
142                    else:
143                        print "How did we get here?"
144                elif arg0 == 'field':
145                    if n_parens == 0 and n_braces == 1:
146                        newFieldStart = True
147                    else:
148                        print "How did we get here?"
149                elif tok['tokName'] in ('NAME', 'STRING'):
150                    if ((newRecordStart and n_parens == 1 and n_braces == 0) or 
151                        (newFieldStart and n_parens == 1 and n_braces == 1)):
152                        arg0 = arg0.strip('"')
153                        if newFieldStart:
154                            argList.append( arg0 )
155                        else:
156                            argList.append( arg0.split(" ")[0] )
157                elif arg0 == "(":
158                    n_parens += 1
159                    argList = []
160                elif arg0 == ")":
161                    n_parens -= 1
162                    if newRecordStart and n_parens == 0 and n_braces == 0:
163                        # RTYP is a virtual field provided by the CA server
164                        fieldDict['RTYP'] = argList[0]
165                        # PV name
166                        fieldDict['NAME'] = argList[1]
167                    elif newFieldStart and n_parens == 0 and n_braces == 1:
168                        # fields of "pv"
169                        newFieldStart = False
170                        fieldDict[argList[0]] = argList[1]
171                        pass
172                elif arg0 == "{":
173                    n_braces += 1
174                    argList = []
175                elif arg0 == "}":
176                    n_braces -= 1
177                    if n_braces == 0:
178                        newFieldStart = False
179                        pvName = fieldDict['NAME']
180                        if pvName in pvDict:
181                            # alert that an assumption is not valid
182                            print "PROBLEM: multiple %s declarations in %s" % (pvName, self.filename)
183                        else:
184                            pvDict[pvName] = fieldDict
185        self.pvDict = pvDict
186
187
188if __name__ == '__main__':
189    dbFilename = "../../IOCs/12id/12ida1App/Db/SB_DCM.db"
190    dbFilename = "C:\\Users\\Pete\\Apps\\CSS_SoftIOC\\demo\\Ctrl_PID.vdb"
191    db = Db(dbFilename)
192    # P=12ida1:,MTH1=m9,MTH2=m11,MZ2=m14
193    macros = {'P': '12ida1:', 'MTH1': 'm9', 'MTH2': 'm11', 'MZ2': 'm14'}
194    print "Unexpanded list of PVs:"
195    for item in sorted(db.getPVs()):
196        print item
197    print "Expanded list of PVs:"
198    for item in sorted(db.getPVs({'P': '12ida1:'})):
199        print item
200    print 'filename:', db.filename
201    print 'absolute filename on this system:', db.absolute_filename
202    print 'There are %d PV records defined' % db.countPVs()
203    print "Fully expanded list of PVs and fields:"
204    pprint.pprint( db.getFull(macros) )
205    print "%s expanded to %s" % (db.keyName('12ida1:DCM:LM2', macros), '12ida1:DCM:LM2')
206    print "Cross-reference dictionary:"
207    pprint.pprint( db.xrefDict(macros) )
Note: See TracBrowser for help on using the repository browser.