Files
sics/site_ansto/instrument/TEST_SICS/fakeGalil/galilmotor.py

408 lines
14 KiB
Python

#!/usr//bin/env python
# vim: ts=8 sts=4 sw=4 ft=python expandtab
# Author: Douglas Clowes (dcl@ansto.gov.au) 2012-06-29
import inspect
import time
import math
class GalilMotor(object):
"""Galil Motor Object"""
def __init__(self, theAxis):
print "GalilMotor ctor: %s" % theAxis
self.name = None
self.axis = theAxis
self.motorState = "OFF"
self.stepsPerX = 25000.0
self.cntsPerX = 8192.0
self.speed = 25000
self.accel = 2500
self.decel = 2500
self.lowerLim = -20.0
self.upperLim = 20.0
self.hasAbsEnc = True
self.unitsHome = 0.0
self.absEncHome = 100 * 1000 * 1000
self.motorHome = 0.0
self.currentSteps = 0
self.targetSteps = self.currentSteps
self.configDone = False
self.switches = 44
self.fwdlim = 1
self.revlim = 1
self.stopcode = 0
self.stopCallback = None
self.moveCallback = None
self.trace = False
self.rotary_bits = 0
def doTrace(self, arg):
print "Trace %s: %s" % (self.axis, arg),
if self.trace:
print "(ON =>",
else:
print "(OFF =>",
if arg == "ON":
self.trace = True
else:
self.trace = False
if self.trace:
print "ON)"
else:
print "OFF)"
def doConfigure(self, arg):
print "Configure %s: %s" % (self.axis, arg)
the_parts = arg.split(",")
configDoneAlready = self.configDone
for part in the_parts:
(lh, rh) = part.split("=")
if lh == "SPX":
self.stepsPerX = float(rh)
self.configDone = True
elif lh == "CPX":
self.cntsPerX = float(rh)
self.configDone = True
elif lh == "RL":
self.lowerLim = float(rh)
self.configDone = True
elif lh == "FL":
self.upperLim = float(rh)
self.configDone = True
elif lh == "UH":
self.unitsHome = float(rh)
self.configDone = True
elif lh == "EH":
self.hasAbsEnc = True
self.absEncHome = float(rh)
self.configDone = True
elif lh == "MH":
self.hasAbsEnc = False
self.motorHome = float(rh)
self.configDone = True
elif lh == "NAM":
self.name = rh[1:-1]
self.configDone = True
elif lh == "ROT":
self.rotary_bits = int(rh)
self.configDone = True
else:
print "Unrecognized config item \"%s\" in \"%s\"" % (lh, arg)
if self.configDone and not configDoneAlready:
self.currentSteps = 0
self.targetSteps = 0
def getEncoder(self):
'''Calculate the encoder from the currentSteps'''
value = self.absEncHome + self.currentSteps * (self.cntsPerX / self.stepsPerX)
return int(value)
def getPosition(self):
'''Calculate the unit position'''
if self.hasAbsEnc and self.cntsPerX != 0:
# absolute encoder case
value = self.getEncoder() - self.absEncHome
value /= self.cntsPerX
value += self.unitsHome
return value
elif self.stepsPerX != 0:
# handle non-AbsEncoder case
value = self.currentSteps - self.motorHome
value /= self.stepsPerX
return value
else:
# not-configured case
return -123.456
def doIteration(self, now):
'''If moving then update speed and position using
v = u + at
s = ut + 1/2 at**2 = (u + v)t / 2
v**2 = u**2 + 2as
'''
if self.motorState in ["MOVING"]:
t = now - self.motorStartTime
distance = abs(self.targetSteps - self.motorStartSteps)
topSpeed = math.sqrt(2.0 * distance / ( 1.0 / self.accel + 1.0 / self.decel))
if topSpeed < 1e-6:
topSpeed = 0
if topSpeed > self.speed:
topSpeed = self.speed
t1 = topSpeed / self.accel
s1 = 0.5 * self.accel * t1**2
t3 = topSpeed / self.decel
s3 = 0.5 * self.decel * t3**2
s2 = distance - s1 - s3
if s2 < 1e-6:
s2 = 0
if s2 == 0 or topSpeed == 0:
t2 = 0
else:
t2 = s2 / topSpeed
if t >= (t1 + t2 + t3):
# Finished
s = distance
v = 0
self.motorState = "STOPPED"
self.stopcode = 1
self.switches = 44 # stopped, both limits clear
self.fwdlim = 1
self.revlim = 1
elif t >= (t1 + t2):
# Ramp Down
dt = t - (t1 + t2)
# v = u + at
v = topSpeed - self.decel * dt
# s = (u + v)t / 2
s = s1 + s2 + 0.5 * (topSpeed + v) * dt
elif t >= t1:
# Run Flat
v = topSpeed
s = s1 + topSpeed * (t - t1)
else:
# Ramp Up
v = 0.0 + self.accel * t
s = 0.5 * self.accel * t**2
if self.speed < 1e-6:
self.percentVelocity = 100.0
else:
self.percentVelocity = 100.0 * v / self.speed
if distance < 1e-6:
self.percentDistance = 100.0
else:
self.percentDistance = 100.0 * s / distance
if self.targetSteps > self.motorStartSteps:
self.currentSteps = self.motorStartSteps + int(s)
if self.currentSteps >= self.targetSteps:
self.motorState = "STOPPED"
self.stopcode = 1
self.switches = 44 # stopped, both limits clear
self.fwdlim = 1
self.revlim = 1
elif self.targetSteps < self.motorStartSteps:
self.currentSteps = self.motorStartSteps - int(s)
if self.currentSteps <= self.targetSteps:
self.motorState = "STOPPED"
self.stopcode = 1
self.switches = 44 # stopped, both limits clear
self.fwdlim = 1
self.revlim = 1
else:
self.motorState = "STOPPED"
self.stopcode = 1
self.switches = 44 # stopped, both limits clear
self.fwdlim = 1
self.revlim = 1
currentPosition = self.getPosition()
if not self.moveCallback == None:
self.moveCallback(self)
if self.targetSteps > self.motorStartSteps and currentPosition >= self.upperLim:
# Forward Limit
self.motorState = "STOPPED"
self.stopcode = 2 # stopped by forward limit
self.switches = 44 - 8 # stopped, reverse limit clear
self.fwdlim = 0
self.revlim = 1
pass
if self.targetSteps < self.motorStartSteps and currentPosition <= self.lowerLim:
# Reverse Limit
self.motorState = "STOPPED"
self.stopcode = 3 # stopped by reverse limit
self.switches = 44 - 4 # stopped, forward limit clear
self.fwdlim = 1
self.revlim = 0
pass
if self.motorState == "STOPPED":
print "Axis %s stopped at:" % self.axis, currentPosition
self.currentSteps = self.targetSteps
if not self.stopCallback == None:
self.stopCallback(self)
else:
pass
def doAssignment(self, cmd, arg):
'''This is for commands like "PAA=1234567" which are for the motor.
The axis and the assignment operator have already been removed.
'''
if self.trace:
print "Trace ASG %s: %s" % (self.axis, str(cmd) + " " + str(arg))
if cmd == "PA":
self.targetSteps = int(arg)
elif cmd == "PR":
self.targetSteps = self.currentSteps + int(arg)
elif cmd == "SP":
self.speed = max(1, abs(int(arg)))
elif cmd == "AC":
self.accel = max(1, abs(int(arg)))
elif cmd == "DC":
self.decel = max(1, abs(int(arg)))
else:
print "Unknown assignment", cmd, arg
def doQuery(self, arg):
if arg == "TD":
response = self.currentSteps
elif arg == "ZZ":
response = self.getPosition()
elif arg == "TP":
response = self.getEncoder()
if self.rotary_bits > 0:
response = response & ((1 << self.rotary_bits) - 1)
elif arg == "SP":
response = self.speed
elif arg == "AC":
response = self.accel
elif arg == "DC":
response = self.decel
elif arg == "TS":
response = self.switches
elif arg == "LF":
response = self.fwdlim
elif arg == "LR":
response = self.revlim
elif arg == "SC":
response = self.stopcode
elif arg == "MO":
if self.motorState in ["OFF"]:
response = 1
else:
response = 0
elif arg == "BG":
if self.motorState in ["MOVING"]:
response = 1
else:
response = 0
else:
print "Unknown Query:", arg
response = None
if self.trace:
print "Trace QRY %s: %s" % (self.axis, str(arg) + " = " + str(response))
return response
def doCommandTD(self, arg):
return self.doQuery("TD")
def doCommandZZ(self, arg):
result = self.doQuery("ZZ")
print "Position:", result
return result
def doCommandTP(self, arg):
return self.doQuery("TP")
def doCommandSC(self, arg):
return self.doQuery("SC")
def doCommandTS(self, arg):
return self.doQuery("TS")
def doCommandST(self, arg):
if self.motorState in ["MOVING"]:
self.motorState = "STOPPED"
self.stopcode = 4
self.switches = 44
self.fwdlim = 1
self.revlim = 1
return True
def doCommandBG(self, arg):
if self.motorState in ["ON", "STOPPED"]:
self.motorState = "MOVING"
self.stopcode = 0
self.switches = 140 # moving, both limits clear
self.fwdlim = 1
self.revlim = 1
self.motorStartTime = time.time()
self.motorStartSteps = self.currentSteps
return True
return False
def doCommandSH(self, arg):
if self.motorState in ["ON", "OFF", "STOPPED"]:
self.motorState = "ON"
return True
return False
def doCommandMO(self, arg):
if self.motorState in ["ON", "OFF", "STOPPED"]:
self.motorState = "OFF"
return True
return False
def doCommandPA(self, arg):
self.targetSteps = int(arg)
return True
def doCommandPR(self, arg):
self.targetSteps = self.currentSteps + int(arg)
return True
def doCommand(self, cmd, arg):
'''This is for commands like "BGA" which are for the motor.
The axis and other cruft have already been removed.
'''
action = "inspect.ismethod(GalilMotor.doCommand%s)" % cmd
if eval(action):
if self.trace:
data = cmd
if arg:
data = data + " " + arg
print "Trace CMD: \"" + data + "\""
action = "response = self.doCommand%s(%s)" % (cmd, arg)
exec action
return response
else:
print "Unknown Action:", eval(action), "for cmd:", action
if __name__ == '__main__':
motor = GalilMotor("A")
motor.doConfigure("SPX=25000.0,CPX=8192.0,RL=-10.0,FL=10.0,UH=0.0,EH=100000000")
print "SP", motor.doAssignment("SP", "25000")
print "AC", motor.doAssignment("AC", "25000")
print "DC", motor.doAssignment("DC", "25000")
print "PA", motor.doAssignment("PA", "100000")
print "SH", motor.doCommand("SH", None)
print "BG", motor.doCommand("BG", None)
print "MO", motor.doQuery("MO")
print "BG", motor.doQuery("BG")
count = 0
while motor.doQuery("BG") and count <= 100:
t = 0.1 * count
motor.doIteration(motor.motorStartTime + t)
rnge = 70
vspot = int(0.01 * rnge * motor.percentVelocity)
sspot = int(0.01 * rnge * motor.percentDistance)
bth = "b"
if sspot >= 0:
ess = "s"
else:
ess = "*"
bth = "B"
sspot = 0
if vspot >= 0:
vee = "v"
else:
vee = "^"
bth = "B"
vspot = 0
if vspot < sspot:
line = " "*(vspot) + vee + " "*(sspot - vspot - 1) + ess + " "*(rnge - sspot) + "|"
line += " %2d %2d" % (vspot, sspot)
elif vspot > sspot:
line = " "*(sspot) + ess + " "*(vspot - sspot - 1) + vee + " "*(rnge - vspot) + "|"
line += " %2d %2d" % (sspot, vspot)
else:
line = " "*(sspot) + bth + " "*(rnge - sspot) + "|"
line += " %2d" % (sspot)
print "%3.2f: %s" % (t, line)
count += 1
print "_MO", motor.doQuery("MO")
print "_BG", motor.doQuery("BG")
print "TP", motor.doCommand("TP", None)
print "TD", motor.doCommand("TD", None)
print "ZZ", motor.doCommand("ZZ", None)
print "_ZZ", motor.doQuery("ZZ")
print "MO", motor.doCommand("MO", None)
print "_MO", motor.doQuery("MO")
print "Iterations:", count