Merge branch 'RELEASE-3_1' into RELEASE-3_2

Conflicts:
	sics/site_ansto/instrument/bilby/config/chopper/sct_astrium_chopper.tcl
	sics/site_ansto/instrument/hipd/wombat_configuration.tcl
	sics/site_ansto/instrument/pelican/pelican_configuration.tcl
This commit is contained in:
Douglas Clowes
2014-08-06 13:53:18 +10:00
80 changed files with 2274 additions and 819 deletions

View File

@@ -0,0 +1,123 @@
#!/usr/bin/env python
# vim: ft=python ts=8 sts=4 sw=4 expandtab autoindent smartindent nocindent
# Classes for device simulation using: First Order Plus Delay Time (FOPDT)
#
# Author: Unknown
# Finder: Douglas Clowes
"""
The class fopdt is a container for one or more fopdt_sink objects.
I am guessing that an fopdt_sink is a FOPDT source such as a heater element
or FOPDT sink such as a heat leak to the environment.
TODO:
* look into the isinstance block in getAbsolute
because there doesn't seem to be a 'current_value' anywhere
"""
from math import exp
class fopdt_sink:
def __init__(self, value, Kp, Tp, Td, absolute=False):
self.value = value
self.absolute = absolute
self.Kp = Kp
self.Tp = Tp
self.Td = Td
self.vmap = dict()
def getAbsolute(self):
if isinstance(self.value, fopdt):
result = self.value.current_value
else:
result = self.value
return result
def getValue(self, tm, current):
result = self.getAbsolute()
# Do the timeshifting implied by Td
t2 = round(tm)
t1 = round(tm - self.Td)
if not t2 in self.vmap:
self.vmap[t2] = result
for key in sorted(self.vmap.keys()):
if key < t1:
del self.vmap[key]
else:
break
if t1 in self.vmap:
result = self.vmap[t1]
else:
result = self.vmap[sorted(self.vmap.keys())[0]]
# TODO should this go before timeshifting?
if not self.absolute:
result = result - current
return result
def getDelta(self, tm, current):
value = self.getValue(tm, current)
return (1 - exp(-1.0/self.Tp)) * self.Kp * value
class fopdt:
def __init__(self, pv):
self.pv = pv
self.sources = []
self.sinks = []
def AddSource(self, source):
self.sources.append(source)
def RemSource(self, source):
if source in self.sources:
self.sources.remove(source)
def AddSink(self, sink):
self.sinks.append(sink)
def RemSink(self, sink):
if sink in self.sinks:
self.sinks.remove(sink)
def iterate(self, tm):
self.source_delta = 0.0
self.sink_delta = 0.0
for sink in self.sources:
self.source_delta = self.source_delta + sink.getDelta(tm, self.pv)
for sink in self.sinks:
self.sink_delta = self.sink_delta + sink.getDelta(tm, self.pv)
self.old_value = self.pv
self.new_value = self.pv + self.source_delta + self.sink_delta
self.pv = self.new_value
if __name__ == "__main__":
dev = fopdt(20)
source = fopdt_sink(20, 2, 13, 10, False)
dev.AddSource(source)
dev.AddSource(source)
dev.AddSource(source)
dev.AddSource(source)
sink = fopdt_sink(20, 1, 30, 1, False)
dev.AddSink(sink)
dev.AddSink(sink)
min = max = dev.pv
fd = open("test.csv", "w")
fd.write("Time,value,source,source_v,sink,sink_v\n")
for i in range(0,300+ 1):
if i == 15:
source.value = 30
elif i == 45:
source.value = 10
dev.iterate(i)
current = dev.pv
if current > max:
max = current
if current < min:
min = current
#print "%3d: %6.3f = %6.3f + %6.3f + %6.3f" % ( i, current, prev, delta_in, delta_out )
line = "%d,%.3f,%.3f,%.3f,%.3f,%.3f" % (i, current, source.value, dev.source_delta, sink.value, dev.sink_delta)
fd.write(line + "\n")
fd.close()
print "Now: %6.3f, Min: %6.3f, Max: %6.3f" % (current, min, max)

View File

@@ -583,7 +583,7 @@ def init_context():
ContextStack[ContextIndex]['driveable'] = None
ContextStack[ContextIndex]['control'] = 'true'
ContextStack[ContextIndex]['data'] = 'true'
ContextStack[ContextIndex]['mutable'] = 'false'
ContextStack[ContextIndex]['mutable'] = 'true'
ContextStack[ContextIndex]['nxsave'] = 'true'
ContextStack[ContextIndex]['read_function'] = 'rdValue'
ContextStack[ContextIndex]['write_function'] = 'setValue'
@@ -730,8 +730,47 @@ def build_group(MyDriver, p):
if key in ContextStack[ContextIndex]:
ContextStack[ContextIndex][key] = item[key]
pop_context()
adjust_group(MyGroup)
return MyGroup
def adjust_group(MyGroup):
if Verbose:
print 'ante adjust_group', MyGroup
MyData = None
for var in MyGroup['Vars']:
if Verbose:
print "Var:", MyGroup['Vars'][var]
if 'data' in MyGroup['Vars'][var]:
if MyGroup['Vars'][var]['data'] == 'true':
MyData = 'true'
if 'klass' not in MyGroup['Vars'][var]['Property']:
MyGroup['Vars'][var]['Property']['klass'] = 'parameter'
else:
MyData = 'false'
if MyData is None:
for grp in MyGroup['Groups']:
if Verbose:
print "Grp:", MyGroup['Groups'][grp]
adjust_group(MyGroup['Groups'][grp])
if 'data' in MyGroup['Groups'][grp]['GroupProperty']:
if MyGroup['Groups'][grp]['GroupProperty']['data'] == 'true':
MyData = 'true'
else:
MyData = 'false'
break
if MyData is not None:
if 'GroupProperty' not in MyGroup:
MyGroup['GroupProperty'] = {}
if 'data' not in MyGroup['GroupProperty']:
MyGroup['GroupProperty']['data'] = MyData
if MyData:
if 'klass' not in MyGroup['GroupProperty']:
MyGroup['GroupProperty']['klass'] = '@none'
if 'type' not in MyGroup['GroupProperty']:
MyGroup['GroupProperty']['type'] = 'part'
if Verbose:
print 'post adjust_group', MyGroup
def build_driver(MyDriver, TheTree):
if Verbose:
print "TheTree:", TheTree
@@ -770,12 +809,19 @@ def dump_driver_vars(vars, indent):
global DriveableFunctionTypes
for item in sorted(vars):
print indent + ' VAR %s = {' % item
for subitem in sorted([i for i in vars[item] if i not in FunctionTypes + DriveableFunctionTypes]):
print indent + ' %s =' % subitem, vars[item][subitem]
Comments = ['name', 'path']
Deferred = ['Property'] + Comments + FunctionTypes + DriveableFunctionTypes
for Comment in sorted(Comments):
if Comment in vars[item]:
print indent + ' # %s = \'%s\'' % (Comment, vars[item][Comment])
for subitem in sorted([i for i in vars[item] if i not in Deferred]):
print indent + ' %s = \'%s\'' % (subitem, vars[item][subitem])
for subitem in sorted([i for i in vars[item] if i in FunctionTypes]):
print indent + ' %s =' % subitem, vars[item][subitem]
print indent + ' %s = \'%s\'' % (subitem, vars[item][subitem])
for subitem in sorted([i for i in vars[item] if i in DriveableFunctionTypes]):
print indent + ' %s =' % subitem, vars[item][subitem]
print indent + ' %s = \'%s\'' % (subitem, vars[item][subitem])
for subitem in sorted([i for i in vars[item]['Property']]):
print indent + ' Property \'%s\' = \'%s\'' % (subitem, vars[item]['Property'][subitem])
print indent + ' }'
def dump_driver_groups(groups, indent):
@@ -784,8 +830,16 @@ def dump_driver_groups(groups, indent):
print indent + 'GROUP ' + item + ' = {'
else:
print indent + 'GROUP = {'
for subitem in sorted([x for x in groups[item] if not x in ['Groups', 'Vars']]):
Comments = ['name', 'path']
Deferred = ['Groups', 'Vars', 'GroupProperty'] + Comments
for Comment in sorted(Comments):
if Comment in groups[item]:
print indent + ' # %s = \'%s\'' % (Comment, groups[item][Comment])
for subitem in sorted([x for x in groups[item] if not x in Deferred]):
print indent + ' ', subitem, '=', groups[item][subitem]
if 'GroupProperty' in groups[item]:
for subitem in groups[item]['GroupProperty']:
print indent + ' GroupProperty', subitem, '=', groups[item]['GroupProperty'][subitem]
dump_driver_vars(groups[item]['Vars'], indent)
dump_driver_groups(groups[item]['Groups'], indent + ' ')
print indent + '}'
@@ -802,8 +856,13 @@ def dump_driver_funcs(funcs):
def dump_driver(MyDriver):
print 'DRIVER ' + MyDriver['name'] + ' = {'
for item in sorted([x for x in MyDriver if x not in ['Groups', 'Funcs']]):
print ' ' + item + ' =', MyDriver[item]
Comments = ['PathName', 'Permlink']
Deferred = ['Groups', 'Funcs', 'Deferred', 'name'] + Comments
for Comment in sorted(Comments):
if Comment in MyDriver:
print '# %s = \'%s\'' % (Comment, MyDriver[Comment])
for item in sorted([x for x in MyDriver if x not in Deferred]):
print ' ' + item + ' =', '\'%s\'' % MyDriver[item]
#print 'Groups:', MyDriver['Groups']
dump_driver_groups(MyDriver['Groups'], ' ')
#print 'Funcs:', MyDriver['Funcs']
@@ -1334,6 +1393,7 @@ def put_mkDriver(MyDriver):
txt += put_group(MyDriver, MyDriver['Groups'][group])
txt += [' hsetprop ${scobj_hpath} klass %s' % MyDriver['class']]
txt += [' hsetprop ${scobj_hpath} data true']
txt += [' hsetprop ${scobj_hpath} debug_threshold %s' % str(MyDriver['debug_threshold'])]
if len(MyDriver['Deferred']) > 0:
txt += [' if {[string equal -nocase [SplitReply [%s]] "false"]} {' % MyDriver['simulation_group']]

View File

@@ -0,0 +1,119 @@
#!/usr/bin/env python
# vim: ft=python ts=8 sts=4 sw=4 expandtab autoindent smartindent nocindent
#
# Class for device simulation using: Proportional-Integral-Derivative (PID)
#
# This recipe gives simple implementation of a Discrete
# Proportional-Integral-Derivative (PID) controller.
#
# PID controller gives output value for error between desired reference input and
# measurement feedback to minimize error value.
#
# More information: http://en.wikipedia.org/wiki/PID_controller
#
# cnr437@gmail.com
#
####### Example #########
#
# p=PID(3.0,0.4,1.2)
# p.setPoint(5.0)
# while True:
# pid = p.update(measurement_value)
#
#
"""
TODO:
* Look into making the update time based because the logic seems to assume constant (1 second) time
- Derivator should be dE/dT but is just dE
- Integrator should be sigma(dT*E) but is just sigma(E)
* Look into making the Derivator based on changes in the PV instead of the Error
"""
class PID:
"""
Discrete PID control
"""
def __init__(self, P=2.0, I=0.0, D=1.0, Derivator=0, Integrator=0, Integrator_max=500, Integrator_min=-500):
self.Kp=P
self.Ki=I
self.Kd=D
self.Derivator=Derivator
self.Integrator=Integrator
self.Integrator_max=Integrator_max
self.Integrator_min=Integrator_min
self.set_point=0.0
self.error=0.0
def update(self,current_value):
"""
Calculate PID output value for given reference input and feedback
"""
self.error = self.set_point - current_value
self.P_value = self.Kp * self.error
# TODO: check the sign is correct
self.D_value = self.Kd * (current_value - self.Derivator)
self.Derivator = current_value
self.Integrator = self.Integrator + self.error
if self.Integrator > self.Integrator_max:
self.Integrator = self.Integrator_max
elif self.Integrator < self.Integrator_min:
self.Integrator = self.Integrator_min
self.I_value = self.Integrator * self.Ki
PID = self.P_value + self.I_value + self.D_value
return PID
def setPoint(self,set_point):
"""
Initilize the setpoint of PID
"""
self.set_point = set_point
self.Integrator=0
self.Derivator=0
def setIntegrator(self, Integrator):
self.Integrator = Integrator
def setDerivator(self, Derivator):
self.Derivator = Derivator
def setKp(self,P):
self.Kp=P
def setKi(self,I):
self.Ki=I
def setKd(self,D):
self.Kd=D
def getPoint(self):
return self.set_point
def getError(self):
return self.error
def getIntegrator(self):
return self.Integrator
def getDerivator(self):
return self.Derivator
if __name__ == "__main__":
#p=PID(3.0,0.4,1.2)
#p.setPoint(5.0)
#while True:
# pid = p.update(measurement_value)
p = PID(3.0, 0.4, 1.2)
p.setPoint(20.0)
pv = 20
for i in range(0,61):
print p.update(pv)