step.py: Fix pv name parser
Annotate for file lib/python/bii_scripts/step.py
2015-07-09 Goetz.Pfeiff 1 # -*- coding: utf-8 -*-
09:34:31 ' 2
2022-01-24 Goetz 3 # Copyright 2022 Helmholtz-Zentrum Berlin für Materialien und Energie GmbH
2015-07-09 Goetz.Pfeiff 4 # <https://www.helmholtz-berlin.de>
09:34:31 ' 5 #
' 6 # Author: Bernhard Kuner <bernhard.kuner@helmholtz-berlin.de>
' 7 #
' 8 # This program is free software: you can redistribute it and/or modify it under
' 9 # the terms of the GNU General Public License as published by the Free Software
' 10 # Foundation, either version 3 of the License, or (at your option) any later
' 11 # version.
' 12 #
' 13 # This program is distributed in the hope that it will be useful, but WITHOUT
' 14 # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
' 15 # FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
' 16 # details.
' 17 #
' 18 # You should have received a copy of the GNU General Public License along with
' 19 # this program. If not, see <http://www.gnu.org/licenses/>.
' 20
2012-10-10 Bernhard.Kun 21 import re
2021-01-06 Bernhard.Kun 22 from epics import PV
2012-10-10 Bernhard.Kun 23 import time
12:36:04 ' 24 import sys
' 25 import string
' 26 import signal
2020-04-22 Goetz 27 from bii_scripts import pfunc
2012-10-10 Bernhard.Kun 28 from types import FunctionType
12:36:04 ' 29 from optparse import OptionParser
' 30 import threading
' 31 import atexit
' 32 import os
' 33 import pprint, StringIO
2021-01-06 Bernhard.Kun 34
2012-10-10 Bernhard.Kun 35 """
2021-01-06 Bernhard.Kun 36 step.py - Configurable Measure Loops
2012-10-10 Bernhard.Kun 37 ****************************************
12:36:04 ' 38
' 39 """
' 40
2020-04-22 Goetz 41 assert sys.version_info[0]==2
13:50:00 ' 42
2012-10-10 Bernhard.Kun 43 my_version = "1.1"
12:36:04 ' 44
' 45 def warnSh(warnStr):
' 46 print warnStr
' 47
' 48 # Global warn function, should be set to msg-window by GUI
' 49 warnfuncArr = [warnSh]
' 50
' 51 def setWarnFunc(func):
2021-01-06 Bernhard.Kun 52 pass
13:42:59 ' 53 # warnfuncArr[0] = func
2012-10-10 Bernhard.Kun 54
12:36:04 ' 55 def warnFunc(msg):
' 56 warnfuncArr[0](msg)
' 57
' 58 def Dumper(x):
' 59 s = StringIO.StringIO()
' 60 pprint.pprint(x, s)
' 61 return s.getvalue()
' 62
' 63 class SaveRestore(object):
' 64 """
' 65 Save and Restore original values of all caput() commands.
2017-02-02 Bernhard.Kun 66 """
2012-10-10 Bernhard.Kun 67 caputStore = {}
12:36:04 ' 68 def __init__(self) :
2017-02-02 Bernhard.Kun 69 pass
15:29:59 ' 70
2012-10-10 Bernhard.Kun 71 @staticmethod
12:36:04 ' 72 def isEmpty():
2017-02-02 Bernhard.Kun 73 print SaveRestore.caputStore.keys()
15:29:59 ' 74 if SaveRestore.caputStore: return False
' 75 return True
2012-10-10 Bernhard.Kun 76 @staticmethod
12:36:04 ' 77 def save(pv):
2017-02-02 Bernhard.Kun 78 """
15:29:59 ' 79 """
' 80 # print "SaveRestore check:", pv
' 81 if SaveRestore.caputStore.has_key(pv):
' 82 return
' 83 val = caget(pv)
' 84 # print "\tadd:", pv,val
' 85 SaveRestore.caputStore[pv] = val
2012-10-10 Bernhard.Kun 86
12:36:04 ' 87 @staticmethod
' 88 def restore(pvName):
2017-02-02 Bernhard.Kun 89 val = SaveRestore.caputStore[pvName]
15:29:59 ' 90 # print "SaveRestore restore:", pvName,val, chSet
2021-01-06 Bernhard.Kun 91 caput(val,pvName)
2017-02-02 Bernhard.Kun 92 del SaveRestore.caputStore[pvName]
2012-10-10 Bernhard.Kun 93 @staticmethod
12:36:04 ' 94 def restoreAll(verbose=None):
2017-02-02 Bernhard.Kun 95 if verbose is not None: print "SaveRestore.restoreAll",SaveRestore.caputStore
15:29:59 ' 96 for pv in sorted(SaveRestore.caputStore.keys()):
' 97 SaveRestore.restore(pv)
2012-10-10 Bernhard.Kun 98
12:36:04 ' 99 # def get(self,x):
2017-02-02 Bernhard.Kun 100 # return "%-25s %s" % (x,SaveRestore.caputStore[x])
2012-10-10 Bernhard.Kun 101 # def getStore(self):
2017-02-02 Bernhard.Kun 102 # """
15:29:59 ' 103 # get list of all stored pvs: PV VALUE to be used for printing or listBox
' 104 # """
' 105 # ret = map(get(x),sorted(SaveRestore.caputStore.keys()))
2012-10-10 Bernhard.Kun 106 # def writeFile(self,filename):
2017-02-02 Bernhard.Kun 107 # try:
15:29:59 ' 108 # os.rename(filename,filename+".OLD")
' 109 # f = open("step.sto",'w')
' 110 # for key in sorted(SaveRestore.caputStore.keys()):
' 111 # f.write(get(key)+"\n")
' 112 # f.close()
' 113 # os.unlink(filename+".OLD")
' 114 # except IOError:
' 115 # return False
' 116 # return True
2012-10-10 Bernhard.Kun 117 #
12:36:04 ' 118 # def readFile(self,filename):
2017-02-02 Bernhard.Kun 119 # """
15:29:59 ' 120 # Read a store from File and set the static readStore variable.
2012-10-10 Bernhard.Kun 121 #
2017-02-02 Bernhard.Kun 122 # Raise: ValueError if there is a line that don't match the PV-Value reg. exp.
2012-10-10 Bernhard.Kun 123 #
2017-02-02 Bernhard.Kun 124 # Return: True or False if there is a IOEror
15:29:59 ' 125 # """
' 126 # re_parseLine = re.compile('^\s*([\w\d_-]+)\s*([\.\w\d_-]+)')
' 127 # readStore = {}
' 128 # try:
' 129 # f = open("step.sto",'r')
' 130 # for line in f:
' 131 # p = re_parseLine.match(line)
' 132 # if p is None: raise ValueError
' 133 # pv = p.group(0)
' 134 # val= p.group(1)
' 135 # readStore[pv] = val
' 136 # f.close()
' 137 # except IOError:
' 138 # return None
' 139 # SaveRestore.caputStore = readStore
' 140 # return True
2012-10-10 Bernhard.Kun 141
12:36:04 ' 142 ################################################################################
' 143 #
' 144 # Base functions for channel access (module ca)
' 145 # ============================================
' 146 #
2021-01-06 Bernhard.Kun 147 pvCa = {}
13:42:59 ' 148 """ Create and connect a PV variable if not allready exist.
' 149 Return: the PV or false if the PV can't be connected
' 150 """
' 151 def getCa(pvName):
' 152 if pvName in pvCa:
' 153 return pvCa[pvName]
' 154 else:
' 155 p = PV(pvName)
' 156 connected = p.wait_for_connection(timeout=3.0)
' 157
' 158 if connected == True:
' 159 pvCa[pvName] = p
' 160 return p
' 161 else:
' 162 return False
' 163
2012-10-10 Bernhard.Kun 164 def caExist(chName) :
2021-01-06 Bernhard.Kun 165 """ Create and connect a PV variable if not allready exist.
2012-10-10 Bernhard.Kun 166 Return: True if chName is found, otherwise False
12:36:04 ' 167 """
2021-01-06 Bernhard.Kun 168 if getCa(chName) == False:
2017-02-02 Bernhard.Kun 169 return False
2021-01-06 Bernhard.Kun 170 return True
2012-10-10 Bernhard.Kun 171
2021-01-06 Bernhard.Kun 172 def caput(val,pvName) :
13:42:59 ' 173 """ Create and connect a PV variable if not allready exist.
' 174 Put val to pvName.
2012-10-10 Bernhard.Kun 175 """
2021-01-06 Bernhard.Kun 176 pv = getCa(pvName)
13:42:59 ' 177 stat = pv.put(val,wait=True)
' 178
' 179 if stat == None:
' 180 warnFunc( "caput ERROR: '"+pvName)
2017-02-02 Bernhard.Kun 181 return False
2012-10-10 Bernhard.Kun 182 else :
2017-02-02 Bernhard.Kun 183 return True
2012-10-10 Bernhard.Kun 184
2021-01-06 Bernhard.Kun 185 def caget(pvName):
13:42:59 ' 186 """ Create and connect a PV variable if not allready exist.
' 187 Get val from pvName.
2012-10-10 Bernhard.Kun 188 """
2021-01-06 Bernhard.Kun 189 pv = getCa(pvName)
13:42:59 ' 190 val = pv.get()
' 191 if val == None:
' 192 warnFunc( "caget ERROR: '"+pv)
2012-10-10 Bernhard.Kun 193 return val
12:36:04 ' 194
' 195 cadiffPVs = {}
' 196 def cadiff(pv,dbrTtype=-1):
2021-01-06 Bernhard.Kun 197 """ Create and connect a PV variable if not allready exist.
2012-10-10 Bernhard.Kun 198 Return the difference value of consecutive calls of a PV OR
12:36:04 ' 199 None for the first call or ca-error. This works for multiple PVs! see also caput
' 200 """
2021-01-06 Bernhard.Kun 201 pv = getCa(pvName)
13:42:59 ' 202 now = pv.get()
' 203 if now == None:
' 204 warnFunc( "cadiff ERROR: '"+pv)
2017-02-02 Bernhard.Kun 205 return None
2012-10-10 Bernhard.Kun 206 else :
2017-02-02 Bernhard.Kun 207 if cadiffPVs.has_key(pv) :
15:29:59 ' 208 before = cadiffPVs[pv]
' 209 diff = now - before
' 210 else :
' 211 diff = None # first call - just define the before value
' 212 cadiffPVs[pv] = now
' 213 return diff
2012-10-10 Bernhard.Kun 214
12:36:04 ' 215 class monPV :
' 216 """
' 217 Create a monitored PV,
' 218
' 219 - setup the PV, or throw any ca.caError
' 220 - access the updated value
' 221 - set/read event flag
2017-02-02 Bernhard.Kun 222 """
2012-10-10 Bernhard.Kun 223 def __init__(self, pv,Type=-1):
2021-01-06 Bernhard.Kun 224 self.pvName = pv
13:42:59 ' 225 self.val = None
' 226 self.event = False
' 227 def onChanges(pvname=None, value=None, char_value=None, **kw):
' 228 self.val = value
2017-02-02 Bernhard.Kun 229 self.event = True
2021-01-06 Bernhard.Kun 230 mypv = getCa(pv)
13:42:59 ' 231 if mypv == False:
' 232 warnFunc("Error: Can't init PV: "+pv)
' 233 else:
' 234 mypv.add_callback(onChanges)
' 235
2012-10-10 Bernhard.Kun 236 def get(self) :
2017-02-02 Bernhard.Kun 237 """
15:29:59 ' 238 - o.get() Return: PV.VAL
' 239 """
' 240 if self.val != None :
2021-01-06 Bernhard.Kun 241 return self.val
2017-02-02 Bernhard.Kun 242 else :
15:29:59 ' 243 return None
2012-10-10 Bernhard.Kun 244 def testEvent(self) :
2017-02-02 Bernhard.Kun 245 """
15:29:59 ' 246 o.testEvent() Return: True if any monitor occured since last call of testEvent() or False if not
' 247 """
2021-01-06 Bernhard.Kun 248 if self.event == True :
2017-02-02 Bernhard.Kun 249 self.event = False
15:29:59 ' 250 return True
' 251 else :
' 252 return False
2012-10-10 Bernhard.Kun 253
12:36:04 ' 254 ################################################################################
' 255 #
' 256 # The classes that provide functions to set PVs in the measure loop
' 257 # ============================================
' 258 #
' 259 class anyLoopPV(object):
' 260 """
' 261 Base class for loop PVs
' 262 """
' 263 def __init__(self, name):
' 264 """
2017-02-02 Bernhard.Kun 265 Base init two variables:
15:29:59 ' 266
' 267 - self.setVal: the last set value (init to None)
' 268 - self.pvName: the PV name, but no channel access initialisation of it!
' 269
' 270 If there exists a global saveRestObj (class saveRestore),store the value
' 271 of this PV before it is set by the program.
' 272 """
' 273 # print "anyLoopPV.__init__(",name,")"
' 274 if name == "":
2012-10-10 Bernhard.Kun 275 raise exceptions.ValueError, name
2017-02-02 Bernhard.Kun 276 self.setVal = None
2012-10-10 Bernhard.Kun 277 self.pvName = name
2017-02-02 Bernhard.Kun 278 sr = None
15:29:59 ' 279 try:
' 280 sr = saveRestObj
' 281 except NameError:
' 282 # print "NameError saveRestObj"
' 283 return
' 284 else:
' 285 # print "\tsr.save(",name,")"
' 286 sr.save(name)
2012-10-10 Bernhard.Kun 287 def __str__(self) :
2017-02-02 Bernhard.Kun 288 return str(self.toDict())
2012-10-10 Bernhard.Kun 289 def toDict(self):
2017-02-02 Bernhard.Kun 290 """
15:29:59 ' 291 Dictionary representation of the object
' 292 """
' 293 return {'TYPE':'anyLoopPV_DUMMY','PV':self.pvName}
2012-10-10 Bernhard.Kun 294 def get(self) :
2017-02-02 Bernhard.Kun 295 """
15:29:59 ' 296 Get the active set value
' 297 """
' 298 return self.setVal
2012-10-10 Bernhard.Kun 299 def set(self, val) :
2017-02-02 Bernhard.Kun 300 """
15:29:59 ' 301 Set the PV
' 302 """
' 303 self.setVal = val
' 304 # print "anyLoopPV.set(",self.pvName,",",self.setVal,")"
2012-10-10 Bernhard.Kun 305 class setPV(anyLoopPV) :
12:36:04 ' 306 """
' 307 Establish a PV and provide for a set(val) function.
' 308 See also Ducktypes (motorPV)
' 309 """
' 310 def __init__(self, name):
2017-02-02 Bernhard.Kun 311 """
15:29:59 ' 312 Create a motor object and setup all PVs, monitors to operate this motor.
' 313 """
' 314 anyLoopPV.__init__(self,name)
2012-10-10 Bernhard.Kun 315
12:36:04 ' 316 def toDict(self):
2017-02-02 Bernhard.Kun 317 return {'TYPE':'PV','PV':self.pvName}
2012-10-10 Bernhard.Kun 318 def set(self, val) :
2017-02-02 Bernhard.Kun 319 self.setVal = val
2021-01-06 Bernhard.Kun 320 caput(val,self.pvName)
2012-10-10 Bernhard.Kun 321
12:36:04 ' 322 class motorPV(anyLoopPV) :
' 323 """
' 324 Is Ducktype of class setPV to define access for a stepper motor via motorRecord
' 325 """
' 326 def __init__(self, motor):
2017-02-02 Bernhard.Kun 327 """
15:29:59 ' 328 Create a motor object and setup all PVs, monitors to operate this motor.
' 329 """
' 330 anyLoopPV.__init__(self,motor)
' 331
' 332 chSet = None
2021-05-07 Bernhard.Kun 333 # chSetCmd = None
2017-02-02 Bernhard.Kun 334 # Setup motor PVs
2012-10-10 Bernhard.Kun 335 self.chSet = ca.channel(self.pvName)
2017-02-02 Bernhard.Kun 336 self.chSet.wait_conn(dt=0.1,wait=10)
2021-05-07 Bernhard.Kun 337 # self.chSetCmd = ca.channel(self.pvName + ":cmdHome")
08:48:51 ' 338 # self.chSetCmd.wait_conn(dt=0.1,wait=10)
2017-02-02 Bernhard.Kun 339 self.lvio = monPV(self.pvName + ".LVIO")
15:29:59 ' 340 self.dmov = monPV(self.pvName + ".DMOV")
' 341 self.rbv = monPV(self.pvName + ".RBV")
' 342 self.lls = monPV(self.pvName + ".LLS")
' 343 self.hls = monPV(self.pvName + ".HLS")
2021-05-07 Bernhard.Kun 344 # self.stShow = monPV(self.pvName + ":stShow",Type=1)
2012-10-10 Bernhard.Kun 345
12:36:04 ' 346 def toDict(self):
2017-02-02 Bernhard.Kun 347 return {'TYPE':'MOTOR','PV':self.pvName}
2012-10-10 Bernhard.Kun 348 def set(self, pos, Wait=True) :
2017-02-02 Bernhard.Kun 349 """
15:29:59 ' 350 Move this motor to the position and wait if has reached it. Break for
' 351 limit violations (motor.LVIO)
' 352
' 353 Wait==True: optional parameter True means wait until move is done, False means fire and forget.
' 354 """
' 355 # try :
' 356 self.chSet.put(pos)
' 357 self.setVal = pos
' 358 self.dmov.testEvent() # clear event flag
' 359 self.chSet.flush()
' 360 if Wait == True:
' 361 while self.dmov.testEvent() == False:
' 362 if self.lvio.get() == 1:
' 363 raise ValueError, "lvio"
' 364 while self.dmov.get() == 0:
' 365 if self.lvio.get() == 1:
' 366 raise ValueError, "lvio"
' 367 # except ca.caError,e:
' 368 # warnFunc( "motorPV Error: '"+self.chSet.name+": "+e.__doc__)
' 369 # except ValueError:
' 370 # warnFunc( "motorPV Error: '"+self.pvName+"LVIO ERROR - move run into soft limit for pos="+str(pos))
2012-10-10 Bernhard.Kun 371
2021-05-07 Bernhard.Kun 372 # def home(self) :
08:48:51 ' 373 # """
' 374 # Call sequencer moho home routine and wait until done or timeout
' 375 # """
' 376 # try :
' 377 # chSetCmd.put(1)
' 378 # chSetCmd.flush()
' 379 # t1 = time.time()
' 380 # while self.stShow.get() != 6 : # 6=DONE
' 381 # time.sleep(0.3)
' 382 # if self.stShow.get() == 7: # 7=ABBORT
' 383 # warnFunc("motorPV Error: '"+self.pvName+"moho ABBORT, stShow="+self.stShow)
' 384 # break
' 385 # except ca.caError,e:
' 386 # warnFunc( "motorPV Error: '"+self.pvName+": "+e.__doc__)
2012-10-10 Bernhard.Kun 387
12:36:04 ' 388 class funcPV(anyLoopPV):
' 389 """
' 390 Create a set PV object. calculate the set value from the loop-set value (variable 'x')
' 391 by a free defined function. e.g. sqare by func = "x*x"
' 392 """
' 393 def __init__(self, name,func):
2017-02-02 Bernhard.Kun 394 anyLoopPV.__init__(self,name)
15:29:59 ' 395 self.func = func
' 396 if func is not None: self.setFunc(func)
2012-10-10 Bernhard.Kun 397 def toDict(self):
2017-02-02 Bernhard.Kun 398 return {'TYPE':'FUNC','PV':self.pvName,'FUNC':self.func}
2012-10-10 Bernhard.Kun 399 def setFunc(self, f) :
2017-02-02 Bernhard.Kun 400 self.func = f
2012-10-10 Bernhard.Kun 401 def set(self, x) :
2017-02-02 Bernhard.Kun 402 try:
15:29:59 ' 403 fVal = eval(self.func)
' 404 except SyntaxError, e:
' 405 raise SyntaxError(" "+self.pvName+" has illegal Function: \n'"+self.func+"'")
' 406
' 407 self.setVal = fVal
2021-01-06 Bernhard.Kun 408 caput(fVal,self.pvName)
2012-10-10 Bernhard.Kun 409
12:36:04 ' 410 class linPV(anyLoopPV):
' 411 """
' 412 Define a linear function: Set the pv from begin to end value in this loop
' 413 """
' 414 def __init__(self, name,begin,end,loop):
2017-02-02 Bernhard.Kun 415 anyLoopPV.__init__(self,name)
2012-10-10 Bernhard.Kun 416
12:36:04 ' 417 if type(begin) is not float: begin=float(begin)
' 418 if type(end) is not float: end=float(end)
2017-02-02 Bernhard.Kun 419 self.chSet = None
2012-10-10 Bernhard.Kun 420 self.chSet = ca.channel(self.pvName)
2017-02-02 Bernhard.Kun 421 self.chSet.wait_conn(dt=0.1,wait=10)
15:29:59 ' 422 self.loop = loop
' 423 self.begin = begin
' 424 self.end = end
2012-10-10 Bernhard.Kun 425
12:36:04 ' 426 def toDict(self):
2017-02-02 Bernhard.Kun 427 return {'TYPE':'LINEAR','PV':self.pvName,'BEGIN':self.begin,'END':self.end}
2012-10-10 Bernhard.Kun 428 def set(self, val) :
2017-02-02 Bernhard.Kun 429 if self.loop.incVal == 0: raise ZeroDivisionError
15:29:59 ' 430 steps = (self.loop.toVal - self.loop.fromVal) / self.loop.incVal
' 431 step = (val - self.loop.fromVal) / self.loop.incVal
' 432 if steps == 0: raise ZeroDivisionError
' 433 fVal = (self.end-self.begin) * step/steps + self.begin
' 434 self.setVal = fVal
2021-01-06 Bernhard.Kun 435 caput(fVal,self.pvName)
2012-10-10 Bernhard.Kun 436
12:36:04 ' 437 ################################################################################
' 438 #
' 439 # Definition of a measure loop
' 440 # ============================================
' 441 #
' 442 class loopPv(object):
' 443 """
' 444 Handle the loop parameters from - to - increment and all PVs to be set
' 445
' 446 - hold the loop parameters
' 447 - RAISE 'ValueError' if one of the parameters fromVal, toVal, incVal, delay is not float convertable
2017-02-02 Bernhard.Kun 448 if incVal == 0
2012-10-10 Bernhard.Kun 449 - add PVs to be set from the loop by method addPv
12:36:04 ' 450
' 451 - hold the parsed parameters for the breakIf and nextIf functions to be executed in
' 452 MeasThread by the function procFunc(n)
' 453
' 454 EXAMPLE:
2017-02-02 Bernhard.Kun 455 { 'FROM': -1.0, 'TO': 11.0, 'INC': 2.0, 'DELAY': 1.0
15:29:59 ' 456 'PVS': [{'PV': 'motest:mo0', 'TYPE': 'MOTOR'},
' 457 {'PV': 'motest:mo1', 'TYPE': 'PV'},
' 458 {'PV': 'motest:mo2', 'TYPE': 'FUNC', 'FUNC': 'x*x'},
' 459 {'PV': 'motest:mo3', 'BEGIN': 10, 'TYPE': 'LINEAR', 'END': 20} ]
' 460 }
2012-10-10 Bernhard.Kun 461 """
12:36:04 ' 462 def __init__(self, argDict):
2017-02-02 Bernhard.Kun 463 """
15:29:59 ' 464 init the ca objects for the PVfrom the data dictionary
' 465 """
' 466
' 467 if type(argDict) is not dict: raise TypeError
' 468
' 469 self.setPv = [] # added by self.addPv() method
' 470 self.fromVal = float(argDict['FROM'])
' 471 self.toVal = float(argDict['TO'])
' 472 self.delay = float(argDict['DELAY'])
' 473 self.incVal = float(argDict['INC'])
' 474 self.stepNr = 0
' 475
' 476
' 477 if self.incVal == 0: raise ValueError
' 478 if self.fromVal == self.toVal: raise ValueError
' 479 if self.fromVal > self.toVal and self.incVal > 0:
' 480 self.incVal = -1 * self.incVal
' 481
' 482 if self.fromVal < self.toVal and self.incVal < 0:
' 483 self.incVal = -1 * self.incVal
' 484
' 485 self.incSteps = int((self.toVal-self.fromVal)/self.incVal) + 1 # + 1 because the begin is also a step!
' 486
' 487 if argDict.has_key('PVS') :
' 488 for p in argDict['PVS']: self.addPv(p)
2012-10-10 Bernhard.Kun 489
12:36:04 ' 490
' 491 def __str__(self) :
2017-02-02 Bernhard.Kun 492 brk = ", loopFunc: "
15:29:59 ' 493 return Dumper(self.toDict())
2012-10-10 Bernhard.Kun 494
12:36:04 ' 495 def __repr__(self) :
2017-02-02 Bernhard.Kun 496 return Dumper(self.toDict())
2012-10-10 Bernhard.Kun 497
12:36:04 ' 498 def toDict(self):
2017-02-02 Bernhard.Kun 499 loopDict = { 'FROM': self.fromVal,
2012-10-10 Bernhard.Kun 500 'TO': self.toVal,
12:36:04 ' 501 'DELAY':self.delay,
' 502 'INC': self.incVal,
' 503 }
2017-02-02 Bernhard.Kun 504 if len(self.setPv) > 0: loopDict['PVS'] = map(lambda x: x.toDict(), self.setPv)
15:29:59 ' 505 return loopDict
2012-10-10 Bernhard.Kun 506
12:36:04 ' 507 def addPv(self,pvDict):
2017-02-02 Bernhard.Kun 508 """
15:29:59 ' 509 Tries to create a kind of setPV object, depending on the self.Type parameter.
' 510 Return True or False, if PV is not found
' 511 """
' 512 pvName = pvDict['PV']
' 513 if pvDict['TYPE'] == 'MOTOR' :
' 514 self.setPv.append(motorPV(pvName))
' 515 elif pvDict['TYPE'] == 'PV' :
' 516 self.setPv.append(setPV(pvName))
' 517 elif pvDict['TYPE'] == 'FUNC' :
' 518 self.setPv.append(funcPV(pvName,pvDict['FUNC']))
' 519 elif pvDict['TYPE'] == 'LINEAR' :
' 520 self.setPv.append(linPV(pvName,pvDict['BEGIN'],pvDict['END'],self))
' 521 else :
' 522 warnFunc("loopPv: Illegal PV Type :"+Type+ " for:"+setPvName)
' 523 return False
' 524 return True
' 525
2012-10-10 Bernhard.Kun 526 def getPvNames(self):
2017-02-02 Bernhard.Kun 527 """
15:29:59 ' 528 Return a list of all current PV names for set values of this loop
' 529 """
' 530 return map(lambda x: x.pvName,self.setPv)
2012-10-10 Bernhard.Kun 531 def getSetValues(self):
2017-02-02 Bernhard.Kun 532 """
15:29:59 ' 533 Return a list of all current set values of this loop
' 534 """
' 535 return map(lambda x: str(x.setVal),self.setPv)
2012-10-10 Bernhard.Kun 536
12:36:04 ' 537 ################################################################################
' 538 #
' 539 # Handle PVs and data to be measured:
' 540 # ============================================
' 541 #
' 542 class measurePvs(object) :
' 543 """
' 544 Handle PVs and data to be measured:
' 545
' 546 - init a list of channel access objects with the PV names
' 547 - printHeader(), printVals() method to print to screen or string
' 548 - hold the list of all measured data
' 549 - Need the list of set PVs to be shown
' 550
2017-02-02 Bernhard.Kun 551 'BREAK': ('PVname','condition')
15:29:59 ' 552 'NEXT': ('PVname','condition')
2012-10-10 Bernhard.Kun 553 """
12:36:04 ' 554 def __init__(self,argDict=None):
2017-02-02 Bernhard.Kun 555 """
15:29:59 ' 556 init the ca objects for the PVs to be measured
' 557
' 558 Parameters:
' 559
' 560 measPvNames, list of PV names to be measured
' 561 """
' 562 # print "Create: measurePvs(",measPvNames,")"
' 563 self.hItemWidths = []
' 564
' 565 self.chanNames = [] # names of the measured PVs
' 566 self.chanVals = [] # last measured data
' 567 self.timestamp = None # timestamp of the last measured data, set as tupel of (YYYY, MM, DD, HOUR, MIN, SEC)
' 568 self.hasTimestamp = False
' 569 self.breakPar = None
' 570 self.nextPar = None
' 571 self.measData = [] # store of all measured data
' 572 self.fileName = None# commandline call needs a place to store the filename to be written after loop ends
' 573 self.loopList = [] # point to the looplist. Get pv names and set values from there
' 574 self.loopType = 'ONCE'
' 575
' 576 if argDict is None: return
' 577 if (argDict.has_key('TIMESTAMP') is True): self.hasTimestamp=argDict['TIMESTAMP']
' 578 if argDict.has_key('BREAK') : self.breakPar = parseFuncParameter(argDict['BREAK'])
' 579 if argDict.has_key('NEXT') : self.nextPar = parseFuncParameter(argDict['NEXT'])
' 580 if argDict.has_key('LOOPS'):
' 581 for loopDict in argDict['LOOPS']:
' 582 try:
' 583 l = loopPv(loopDict)
' 584 self.loopList.append(l)
' 585 except Exception, e:
' 586 warnFunc("Loop ERROR can't create Loop:\n"+str(loopPv(loopDict))+"\nException:\n"+str(e) )
' 587 if argDict.has_key('MEASURE'):
' 588 for cn in argDict['MEASURE'] :
' 589 self.addPv(cn)
' 590 if argDict.has_key('LTYPE') and len(argDict['LTYPE']) > 0:
' 591 self.loopType = argDict['LTYPE']
2012-10-10 Bernhard.Kun 592
12:36:04 ' 593 def __str__(self) :
2017-02-02 Bernhard.Kun 594 return "measurePvs:\n"+str(self.chanNames) + "\nloops:\n" + str(self.loopList)
15:29:59 ' 595
2012-10-10 Bernhard.Kun 596 def toDict(self):
2017-02-02 Bernhard.Kun 597 mPvsDict = {'LTYPE': self.loopType,'TIMESTAMP':self.hasTimestamp}
15:29:59 ' 598 if len(self.chanNames) > 0: mPvsDict['MEASURE'] = self.chanNames
' 599 if self.breakPar is not None: mPvsDict['BREAK'] = ''.join(self.breakPar)
' 600 if self.nextPar is not None: mPvsDict['NEXT'] = ''.join(self.nextPar)
' 601 if len(self.loopList) > 0: mPvsDict['LOOPS'] = self.loopList
' 602
' 603 return mPvsDict
2012-10-10 Bernhard.Kun 604
12:36:04 ' 605 def setPvList(self,chList,hasTs):
2017-02-02 Bernhard.Kun 606 self.hasTimestamp = hasTs
15:29:59 ' 607 del(self.chanNames)
' 608 self.chanNames=[]
' 609 del(self.chanVals)
' 610 self.chanVals=[]
' 611 map(lambda x: self.addPv(x),chList)
' 612 self.clearData()
2012-10-10 Bernhard.Kun 613
12:36:04 ' 614 def delPv(self,chName):
2017-02-02 Bernhard.Kun 615 """
15:29:59 ' 616 Delete a pv to be measured.
' 617 Return None if done, errStr if failed (chName don't exist in chanNames list)
' 618 """
' 619 errStr = None
' 620 #print "\tdelPv:",chName
' 621 try :
' 622 while(1):
' 623 idx = self.chanNames.index(chName)
' 624 #print "delPv",chName, idx,self.chanNames[idx]
' 625 del(self.chanNames[idx])
' 626 del(self.chanVals[idx])
' 627 except ValueError,e:
' 628 warnFunc( chName+" not found")
2012-10-10 Bernhard.Kun 629
12:36:04 ' 630 def addPv(self,chName):
2017-02-02 Bernhard.Kun 631 """
15:29:59 ' 632 Create and store a ca.channel for this pv to be measured.
' 633 Return None if done, errStr if failed (pv don't exist)
' 634 """
' 635 errStr = None
2021-01-06 Bernhard.Kun 636 stat = caExist(chName)
13:42:59 ' 637 if stat == False:
' 638 warnFunc("Measure '"+chName+"': Not Connected")
' 639 else:
2017-02-02 Bernhard.Kun 640 self.chanVals.append(None) # default each value to None
15:29:59 ' 641 self.chanNames.append(chName)
2012-10-10 Bernhard.Kun 642
12:36:04 ' 643 def printHeader(self,toString=False,joinStr=" | ") :
2017-02-02 Bernhard.Kun 644 """
15:29:59 ' 645 Print a header with the PV names to the measured values to screen
' 646 and file if paramter fileName is set.(commandline mode of the progeam).
' 647 """
' 648 Items = self.getPvNames()
' 649 for item in Items:
' 650 strLen = len(item)
' 651 if strLen < 15: strLen = 15
' 652 self.hItemWidths.append(strLen)
' 653 header = joinStr.join( map(lambda x:("%%%ds"%x[0])%x[1],zip(self.hItemWidths,Items)) )
' 654 if toString == False:
' 655 print header
' 656 return header
2012-10-10 Bernhard.Kun 657
12:36:04 ' 658 def printVals(self,toString=False,joinStr=" | ") :
2017-02-02 Bernhard.Kun 659 """
15:29:59 ' 660 Print the measured values to screen and file if paramter fileName is set..
' 661 (commandline mode of the progeam)
' 662 """
' 663 Items = self.getVals()
' 664 itemStrs = []
' 665 for (width,item) in zip(self.hItemWidths,Items):
' 666 if type(item) is float:
' 667 l = ("%%%d.3g"%width)%item
' 668 elif type(item) is str:
' 669 l = ("%%%ds"%width)%item
' 670 itemStrs.append(l)
' 671 line = joinStr.join(itemStrs)
' 672 if toString == False:
' 673 print line
' 674 return line
2012-10-10 Bernhard.Kun 675 def writeData(self,filename=None) :
2017-02-02 Bernhard.Kun 676 """
15:29:59 ' 677 Write a Header and all measured data to the file.
' 678 Raise IOError, if file can't be opened
' 679 """
' 680 if len(self.measData) == 0: raise ValueError
' 681 if filename == None and self.fileName is not None:
' 682 f = open(self.fileName,"a")
' 683 else :
' 684 f = open(filename,"a")
' 685
' 686 line = self.printHeader(True,"\t")
' 687 f.write( "# "+time.strftime("%d.%m.%y %H:%M",time.localtime())+"\n")
' 688 f.write( "# "+line +"\n")
' 689 for Items in self.measData:
' 690 line = "\t".join(map(lambda x: ("%%%ds"%x[0])%x[1],zip(self.hItemWidths,Items)))
' 691 f.write(line+"\n")
' 692 f.close()
2012-10-10 Bernhard.Kun 693 def clearData(self) :
2017-02-02 Bernhard.Kun 694 """
15:29:59 ' 695 Clear the buffer of stored data
' 696 """
' 697 self.measData = []
2012-10-10 Bernhard.Kun 698
12:36:04 ' 699 def setFileName(self,fileName): self.fileName=fileName
' 700
' 701 def getPVs(self):
2017-02-02 Bernhard.Kun 702 """
15:29:59 ' 703 Read the values of the PVs to be meausred from channel access
' 704 """
' 705 if self.hasTimestamp is True:
' 706 self.timestamp = time.localtime() [0:6]
2021-01-06 Bernhard.Kun 707 if len(self.chanNames) <= 0:
2017-02-02 Bernhard.Kun 708 return
2021-01-06 Bernhard.Kun 709 for (pvName,idx) in zip(self.chanNames,range(0,len(self.chanNames),1)):
13:42:59 ' 710 val = caget(pvName)
' 711 if val == None:
' 712 return
' 713 self.chanVals[idx] = val
2017-02-02 Bernhard.Kun 714
15:29:59 ' 715 self.measData.append(self.getVals()[:])
' 716
2012-10-10 Bernhard.Kun 717 def getVals(self):
2017-02-02 Bernhard.Kun 718 """
15:29:59 ' 719 return string list of timestamp, actual set values for all set-Pvs in the loopList
' 720 and the measured values.
' 721 """
' 722 Items = []
' 723 if self.hasTimestamp is True:
' 724 timestr = str(self.timestamp[1])+"/"+str(self.timestamp[2])+"/"+str(self.timestamp[0])+" "+str(self.timestamp[3])+":"+str(self.timestamp[4])+":"+str(self.timestamp[5])
' 725 Items.append(timestr)
' 726 for l in self.loopList: Items += l.getSetValues()
' 727 Items += map(lambda x: str(x), self.chanVals)
' 728 return Items
2012-10-10 Bernhard.Kun 729 def getPvNames(self):
2017-02-02 Bernhard.Kun 730 """
15:29:59 ' 731 return list of timestamp-tag, actual set pv names for all set-Pvs in the loopList and
' 732 the measured pvs:
' 733
' 734 Time | Loop1 | Loop2 ..| Measured
' 735 | | |
' 736 2009.02.24 15:34:22 | [ PV1 PV2 | PV3 PV4 | PV5 PV6...]
' 737 """
' 738 Items = []
' 739 if self.hasTimestamp is True:
' 740 Items.append("Time ")
' 741 for l in self.loopList: Items += l.getPvNames()
' 742 Items += self.chanNames
' 743 return Items
2012-10-10 Bernhard.Kun 744
12:36:04 ' 745 ################################################################################
' 746 #
' 747 # Mesure loop dependant stuff
' 748 # ============================================
' 749 #
' 750 class MeasThread(object):
' 751 """
' 752 The Measure Thread - A temporary object to perform a multi dimensional measurement
' 753
' 754 - create a thread to do this measurement
' 755 - runLoop() function will set read the pvs. It is configured by the classes loopPv measurePvs
' 756 - There are methods for start, stop and pause
' 757 - There may be defined nextifFunc and breakifFunc to skip set pvs or to stop measurement
' 758 (see functions: parseFuncParameter(), procFunc() and class loopPv
' 759 """
' 760
' 761 def __init__(self,mPvs,win=None):
2017-02-02 Bernhard.Kun 762 """
15:29:59 ' 763 Create a thread with the runLoop function - but don't start it!
' 764 """
2012-10-10 Bernhard.Kun 765 # make shure that the mPvs loop list is not one from a previous run!
2017-02-02 Bernhard.Kun 766 self.mPvs = mPvs
15:29:59 ' 767 if mPvs.loopList is None:
' 768 raise ValueError("MeasThread:_init_ gets no LoopList")
' 769 self.strToRunControl = {'INAKTIVE':-1,'START':0, 'PAUSE':1,'STOP':2}
' 770 self.runControlToStr = {-1:'INAKTIVE',0:'START', 1:'PAUSE',2:'STOP'}
2012-10-10 Bernhard.Kun 771
12:36:04 ' 772 self.updateViewFunc = None
2017-02-02 Bernhard.Kun 773 self.printCallback = None
15:29:59 ' 774 #print "Start Measure with:",mPvs.toDict()
' 775 def doneCB(idx,state) :
' 776 win.loopCB(idx,state)
' 777 def printCB() :
' 778 win.setNextLine()
' 779 if win is not None:
' 780 self.updateViewFunc = doneCB
' 781 self.printCallback = printCB
' 782 maxSteps = 1
' 783 for l in mPvs.loopList:
' 784 maxSteps *= l.incSteps
' 785 win.setMaxSteps(maxSteps)
' 786 self.stepNr = 0
' 787
' 788 #print "MeasThreadCreate", len(setLoops), mPvs ,win
' 789 self.setRunControl(-1)
' 790 self.ms = threading.Thread(None, self,"RunLoop",(0,))
2012-10-10 Bernhard.Kun 791
12:36:04 ' 792 def MeasThreadStart(self):
2017-02-02 Bernhard.Kun 793 """
15:29:59 ' 794 Start measure thread after configuration. Start the measure loop itself by
' 795 startCmd() of setRunControl(x) method
' 796 """
' 797 self.setRunControl(0)
' 798 self.ms.start()
2012-10-10 Bernhard.Kun 799
12:36:04 ' 800 def setRunControl(self,x) :
2017-02-02 Bernhard.Kun 801 """
15:29:59 ' 802 Set the runControl variable by number or string:
' 803 'INAKTIVE':-1, 'START':0, 'PAUSE':1, 'STOP':2
' 804 """
' 805 if type(x) == int and x >= -1 and x <= 2:
' 806 self.runControl = x
' 807 elif type(x) == str:
' 808 self.runControl = self.strToRunControl[x]
' 809 if self.runControl == None : raise ValueError
' 810 else : raise ValueError
' 811 self.updateView()
2012-10-10 Bernhard.Kun 812
12:36:04 ' 813 def getRunControlStr(self) : return self.runControlToStr[self.runControl]
' 814 def getRunControl(self) : return self.runControl
' 815
' 816 def stopCmd(self) :
2017-02-02 Bernhard.Kun 817 """
15:29:59 ' 818 Stop a measure loop that is not allready inaktive
' 819 """
' 820 if self.getRunControl() != -1:
' 821 self.setRunControl(2)
' 822
2012-10-10 Bernhard.Kun 823 def pauseCmd(self) :
2017-02-02 Bernhard.Kun 824 """
15:29:59 ' 825 Pause a measure loop that is allready aktive or do nothing otherwise
' 826 """
' 827 if self.getRunControl() == 0:
' 828 self.setRunControl(1)
2012-10-10 Bernhard.Kun 829 def startCmd(self) :
2017-02-02 Bernhard.Kun 830 """
15:29:59 ' 831 Start a measure loop. Repeated call will cause Pause and Continue
' 832 """
' 833 if self.getRunControl() == 1 : # Pause -> Start again
' 834 self.setRunControl(0)
' 835 return
' 836 if self.getRunControl() == 0 : # Run -> Pause loop
' 837 self.setRunControl(1)
' 838 return
' 839 if self.getRunControl() == -1 :# Start, begin loop
' 840 self.setRunControl(0)
2012-10-10 Bernhard.Kun 841
12:36:04 ' 842 def updateView(self):
2017-02-02 Bernhard.Kun 843 # print "runDone"
15:29:59 ' 844 if self.updateViewFunc is not None:
' 845 self.updateViewFunc(self.stepNr,self.getRunControl())
' 846
2012-10-10 Bernhard.Kun 847 def notFinished(self,setVal,fromVal,toVal,incVal):
2017-02-02 Bernhard.Kun 848 """
15:29:59 ' 849 Check if loop has finished
' 850 """
' 851 if incVal > 0:
' 852 if setVal <= toVal: return True
' 853 else:
' 854 if setVal >= fromVal: return True
' 855 return False
' 856
2012-10-10 Bernhard.Kun 857 def __call__(self,idx) :
2017-02-02 Bernhard.Kun 858 """
15:29:59 ' 859 The Main Measure loop
' 860
' 861 - Run the list of loop objects and measure the PVs.
' 862 - Used by commandline and gui version
' 863
' 864 - set the values for each loop
' 865 - measure the PVs
' 866 - take care of stop/go/break commands
' 867 """
2012-10-10 Bernhard.Kun 868 try:
2017-02-02 Bernhard.Kun 869 setLoopList = self.mPvs.loopList
15:29:59 ' 870 l = setLoopList[idx]
' 871 l.setVal = l.fromVal
' 872 #print "runLoop: Enter idx: ",idx,self.getRunControlStr(),"Timestamp=",self.mPvs.hasTimestamp #,"loop:",l
' 873 while self.notFinished(l.setVal,l.fromVal,l.toVal,l.incVal) is True:
' 874 try:
2021-01-06 Bernhard.Kun 875 for s in l.setPv:
13:42:59 ' 876 s.set(l.setVal) # HERE: set the new value
2017-02-02 Bernhard.Kun 877 except Exception, e:
15:29:59 ' 878 warnFunc("Loop ERROR can't set:"+str(e) )
' 879 self.stopCmd()
' 880 nxt = procFunc(self.mPvs.nextPar)
' 881 if nxt is None:
' 882 warnFunc("MeasThread: Illegal next function called\n"+str(self.mPvs.nextPar))
' 883 self.stopCmd()
' 884 elif nxt is False:
' 885 brk = procFunc(self.mPvs.breakPar)
' 886 if brk is None:
' 887 warnFunc("MeasThread: Illegal break function called\n"+str(self.mPvs.breakPar))
' 888 self.stopCmd()
' 889 elif brk is True: # breakFunc Break
' 890 warnFunc("Break Loop")
' 891 if idx == 0:
' 892 self.setRunControl(-1)
' 893 return
' 894 if idx+1 < len(setLoopList) and self.getRunControl() != 2:
' 895 self.__call__(idx+1)
' 896 else :
' 897 while self.getRunControl() == 1: # Pause
' 898 time.sleep(0.5)
' 899 if self.getRunControl() == 2: # Break
' 900 if idx == 0:
' 901 self.setRunControl(-1)
' 902 return
' 903
' 904 time.sleep(l.delay) # Wait Delay
' 905 #print "runLoop: Measure"
' 906 try:
' 907 self.mPvs.getPVs() # Measure
' 908 self.mPvs.setVals = map(lambda x: x.setVal,setLoopList)
' 909 except Exception, e:
' 910 warnFunc("Loop ERROR can't read: "+str(e) )
' 911 self.stopCmd()
' 912 self.stepNr += 1
' 913 self.updateView()
' 914 if self.getRunControl() != 2:
' 915 if self.printCallback is not None:
' 916 self.printCallback()
' 917 # self.mPvs.printVals()
' 918 else :
' 919 self.mPvs.printVals()
' 920 # else :
' 921 # print "runLoop: Skip for val=:",l.setVal
' 922 # print "runLoop: Finisch"
' 923
' 924 l.setVal = l.setVal + l.incVal
' 925 if idx==0 and self.mPvs.loopType == 'SAW' and l.setVal > l.toVal:
' 926 l.setVal = l.fromVal
' 927 self.stepNr=0
' 928 self.updateView()
' 929 elif idx==0 and self.mPvs.loopType == 'TRI' and (l.setVal > l.toVal or l.setVal < l.fromVal):
' 930 l.incVal *= -1
' 931 l.setVal = l.setVal + l.incVal + l.incVal
' 932 self.stepNr=0
' 933 self.updateView()
' 934
' 935 if idx == 0:
' 936 #print "Run Done, Cleanup"
' 937 self.setRunControl(-1)
' 938 if self.mPvs.fileName is not None: # just for commandline call, GUI doesn't set mPvs.fileName!
' 939 print "Write measured Data to file: ",self.mPvs.fileName
' 940 self.mPvs.writeData()
' 941 except:
' 942 warnFunc("Any Error occured in process loops")
' 943 self.setRunControl('STOP')
' 944 return
2012-10-10 Bernhard.Kun 945
12:36:04 ' 946 # compile regexp once
2022-03-28 Bernhard.Kun 947 regPvName = re.compile('^\s*(.*?)([<=>!].*)') # 'pvName EXPR, e.g 'MDIZ3T5G:lt50' '< 0.1'
2012-10-10 Bernhard.Kun 948 def parseFuncParameter(funcPar):
12:36:04 ' 949 """
' 950 Parse a next/break function parameter to be used by MeasThread object
' 951 for the nextifFunc and the breakifFunc to provide for a user defined
' 952 measure loop control.
' 953
' 954 To skip measurement in case of nextif is true of to break measurement if breakif
' 955 is true.
' 956
' 957 The parameter 'funcPar' to this functions is of this type:
' 958
2017-02-02 Bernhard.Kun 959 "PV condition" e.g. "myPv:VAL > 15"
2012-10-10 Bernhard.Kun 960
12:36:04 ' 961 This will be parsed by regexp to the tupel: (PV, condition) or None if re doesn't match
' 962 """
' 963 #print "parseFuncParameter",funcPar
' 964 nextifPar = regPvName.match(funcPar)
' 965 if nextifPar == None: return None
' 966 else:
2017-02-02 Bernhard.Kun 967 pv = nextifPar.group(1)
15:29:59 ' 968 comp = nextifPar.group(2)
' 969 if caget(pv) is None:
' 970 return None
' 971 return (pv,comp)
2012-10-10 Bernhard.Kun 972 def procFunc(n) :
12:36:04 ' 973 """
' 974 procfunc will do:
' 975
2017-02-02 Bernhard.Kun 976 val = caget(PV)
15:29:59 ' 977 return exec( "(val"+condition+")" )
2012-10-10 Bernhard.Kun 978
12:36:04 ' 979 Return True/False or 'None' if either caget or exec will fail
' 980 """
' 981 #print "procFunc",n
' 982 if n == None : return False
' 983 val = caget(n[0])
' 984 if val == None: return None
' 985 #print n[0],"=",val
' 986 nxt = False
' 987 try:
2017-02-02 Bernhard.Kun 988 exec( "nxt = (val"+n[1]+")" )
2012-10-10 Bernhard.Kun 989 except SyntaxError:
2017-02-02 Bernhard.Kun 990 warnFunc("procFunc: '"+n[0]+n[1]+"': SyntaxError")
15:29:59 ' 991 return None
2012-10-10 Bernhard.Kun 992 return nxt
12:36:04 ' 993
' 994
' 995 ################################################################################
' 996 #
' 997 # Read and write configuration data
' 998 # ============================================
' 999 #
' 1000
' 1001 def saveConfig(argDict,fileName=None) :
' 1002 """
' 1003 Save program configuration.
' 1004
' 1005 Data format is a list of configuration dictionaries:
' 1006
' 1007 [ { 'DESC': "description",
' 1008 'LOOPS': [{LoopPV}, .. Objects]
2017-02-02 Bernhard.Kun 1009 'LTYPE': 'ONCE',
15:29:59 ' 1010 'BREAK': "PV:name < 13",
' 1011 'TIMESTAMP': False,
' 1012 'MEASURE': [ 'PV',.. Names ]
2012-10-10 Bernhard.Kun 1013 },
12:36:04 ' 1014 ...
' 1015 ]
' 1016
' 1017 Raise IOError if there is no fileName or unable to write the file
' 1018 """
' 1019 if fileName is None:
2017-02-02 Bernhard.Kun 1020 if mPvs.fileName is not None:
15:29:59 ' 1021 fileName=mPvs.fileName
' 1022 else:
' 1023 raise IOError("Function step.saveConfig: missing parameter 'filename'")
2012-10-10 Bernhard.Kun 1024 cfgList = []
12:36:04 ' 1025 if os.path.exists(fileName) is True:
2017-02-02 Bernhard.Kun 1026 cfgList = readConfig(fileName)
15:29:59 ' 1027 if cfgList == None: cfgList = []
2012-10-10 Bernhard.Kun 1028
12:36:04 ' 1029 #print argDict
' 1030 cfgList.append(argDict)
' 1031 cfgStr = Dumper(cfgList)+"\n"
' 1032 f = open(fileName,'w')
' 1033 f.write(cfgStr)
' 1034 f.close()
' 1035
' 1036 def readConfig(fileName):
' 1037 """ Read a configuration file
' 1038
' 1039 - The old data format is supported (one dict in one line)
' 1040 - May raise the exceptions IOError, SyntaxError
' 1041 - Return the list of configuration dictionaries. or None
' 1042 """
' 1043 if len(fileName) == 0:
2017-02-02 Bernhard.Kun 1044 raise IOError("No Filename")
2012-10-10 Bernhard.Kun 1045 f = open(fileName,'r')
12:36:04 ' 1046 s = f.read()
' 1047 f.close()
' 1048 loopCfg = None
' 1049 if s.startswith("{") is False: # old format is a dict in one line
2017-02-02 Bernhard.Kun 1050 try:
15:29:59 ' 1051 loopCfg = eval(s)
' 1052 except SyntaxError, e:
' 1053 raise SyntaxError("Syntax Error in File: "+fileName+"\n"+str(e))
2012-10-10 Bernhard.Kun 1054 else: # old configuration
12:36:04 ' 1055 lines = s.split('\n')
2017-02-02 Bernhard.Kun 1056 cfg = None
15:29:59 ' 1057 idx=0
' 1058 #print lines
' 1059 for cfg in lines:
' 1060 if (len(cfg)==0) or cfg.startswith('#'):
' 1061 continue
' 1062 idx += 1
' 1063 try:
' 1064 c = eval(cfg)
' 1065 except SyntaxError, e:
' 1066 raise SyntaxError("Syntax Error in File: "+fileName+", Line: "+str(idx) )
' 1067 if loopCfg == None: loopCfg = []
' 1068 loopCfg.append(c)
2012-10-10 Bernhard.Kun 1069 return loopCfg
12:36:04 ' 1070
' 1071 # static SaveRestore Object
' 1072 saveRestObj = SaveRestore()
' 1073
' 1074 if __name__ == "__main__":
' 1075 print "NEW: start program with 'stepy'\n for help type 'stepy -h'"