1 | #!/usr/bin/env python |
---|
2 | |
---|
3 | ''' |
---|
4 | Created 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 | |
---|
17 | import os |
---|
18 | import pprint |
---|
19 | import utilities |
---|
20 | |
---|
21 | |
---|
22 | class 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 | |
---|
188 | if __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) ) |
---|