1348 lines
49 KiB
Python
1348 lines
49 KiB
Python
#!/usr/bin/env python
|
|
|
|
"""
|
|
This file contains three classes: plc, axis and pneumatic
|
|
|
|
It contains functions that interact with tc_mca_std_lib on a Beckhoff PLC.
|
|
"""
|
|
import sys, os
|
|
from datetime import datetime
|
|
import pyads as pyads
|
|
import time
|
|
from enum import *
|
|
from eAxisParameters import E_AxisParameters
|
|
|
|
|
|
class E_MotionFunctions(Enum):
|
|
eMoveAbsolute = 0
|
|
eMoveRelative = 1
|
|
eMoveVelocity = 2
|
|
eMoveModulo = 3
|
|
eGearInMultiMaster = 4
|
|
eGearOut = 5
|
|
eHome = 10
|
|
eWriteParameter = 50
|
|
eReadParameter = 60
|
|
|
|
|
|
class E_HomingRoutines(Enum):
|
|
eNoHoming = 0
|
|
eHomeToLimit_Bwd = 1
|
|
eHomeToLimit_Fwd = 2
|
|
#eHomeToLimit_Bwd_2Speeds = 3
|
|
#eHomeToLimit_Fwd_2Speeds = 4
|
|
|
|
eHomeToRef_Bwd = 11
|
|
eHomeToRef_Fwd = 12
|
|
#eHomeToRef_Bwd_2Speeds = 13
|
|
#eHomeToRef_Fwd_2Speeds = 14
|
|
|
|
eHomeToEncPulse_Bwd = 21
|
|
eHomeToEncPulse_Fwd = 22
|
|
eHomeToEncPulse_viaBwdLimit = 23
|
|
eHomeToEncPulse_viaFwdLimit = 24
|
|
|
|
eHomeDirect = 90
|
|
|
|
class E_PneumaticAxisErrors(Enum):
|
|
eNoError = 0
|
|
eExtractTimedOut = 1
|
|
eRetractTimedOut = 2
|
|
eNotMovingExtract = 3
|
|
eNotMovingRetract = 4
|
|
eInterlockOn = 5
|
|
eNoPSSPermit = 6
|
|
eAirPressureError = 7
|
|
|
|
# SLEEP_INTERVAL is the default that the software will sleep while waiting
|
|
# for a bit to change state
|
|
# Most if not all functions can specify this as an optional parameter if you
|
|
# want a different sleep time for one particular function.
|
|
# If not specified this is the default.
|
|
SLEEP_INTERVAL = 1 # s
|
|
MARGIN_OF_SAFETY = 2
|
|
verboseMode = True
|
|
dateTimeObj = datetime.now()
|
|
prevPrintString = "Empty"
|
|
|
|
|
|
class plc:
|
|
# If running on Windows then TwinCAT should create a
|
|
# route for you already and thus senderIp and
|
|
# senderAmsNetId don't need to be provided
|
|
def __init__(
|
|
self,
|
|
plcAmsNetId,
|
|
plcPort,
|
|
plcIp=None,
|
|
senderAmsNetId=None,
|
|
senderIp=None,
|
|
hostname=None,
|
|
username="Administrator",
|
|
password="1",
|
|
connection=None
|
|
):
|
|
|
|
print("Constructor for PLC")
|
|
|
|
self.plcAmsNetId = plcAmsNetId
|
|
self.plcPort = plcPort
|
|
self.plcIp = plcIp
|
|
self.senderAmsNetId = senderAmsNetId
|
|
self.senderIp = senderIp
|
|
self.hostname = hostname
|
|
self.username = username
|
|
self.password = password
|
|
self.connection = pyads.Connection(self.plcAmsNetId, self.plcPort)
|
|
|
|
def __del__(self):
|
|
print("Destructor for PLC: Close connection")
|
|
self.connection.close()
|
|
|
|
def connect(self):
|
|
print("Connect to PLC")
|
|
"""
|
|
platformIsLinux = pyads.utils.platform_is_linux()
|
|
print(f"isLinux()={platformIsLinux}")
|
|
if platformIsLinux:
|
|
if self.senderAmsNetId != None and self.senderIp != None:
|
|
pyads.add_route_to_plc(
|
|
self.senderAmsNetId,
|
|
# The second argument here should be hostname
|
|
# instead of senderIp but we prefer not to use
|
|
# hostname
|
|
self.senderIp,
|
|
self.plcIp,
|
|
self.username,
|
|
self.password,
|
|
route_name=self.hostname,
|
|
)
|
|
else:
|
|
print(
|
|
"Either senderAmsNetId or senderIp set to none so assuming"
|
|
"the route has already been created"
|
|
)
|
|
|
|
local_port = pyads.open_port()
|
|
"""
|
|
self.connection.open()
|
|
print(f"is_open()={self.connection.is_open}")
|
|
|
|
#pyads.set_local_address(self.senderAmsNetId)
|
|
print(f"get_local_address()={pyads.get_local_address()}")
|
|
|
|
# If the connection was not successful this command will fail
|
|
print(f"read_device_info()={self.connection.read_device_info()}")
|
|
self.noOfAxes = self.connection.read_by_name(
|
|
"GVL_APP.nAXIS_NUM", pyads.PLCTYPE_INT
|
|
)
|
|
print(f"GVL_APP.nAXIS_NUM={self.noOfAxes}")
|
|
|
|
return self
|
|
# For reading and writing any variable you can use the pyads function of the plc:
|
|
# E.g.: plc_obj.connection.read_by_name("varName", pyads.PLCTYPE_XXX)
|
|
# E.g.: plc_obj.connection.write_by_name("varName", value, pyads.PLCTYPE_XXX)
|
|
|
|
class axis:
|
|
def __init__(self, plcConnection, axisNum):
|
|
print("Constructor for axis")
|
|
self.plc = plcConnection
|
|
self.axisNum = axisNum
|
|
|
|
def __del__(self):
|
|
print("Destructor for axis: Resetting jog commands")
|
|
self.jogStop()
|
|
|
|
# Generic function for getting any variable on the Axis
|
|
def getGenericVariable(self, plcVarPath, plcVarType):
|
|
plcVarName = f"GVL.astAxes[{self.axisNum}].{plcVarPath}"
|
|
returnValue = self.plc.connection.read_by_name(plcVarName, plcVarType)
|
|
global prevPrintString
|
|
printString = f"{plcVarName}=: {returnValue}"
|
|
# if prevPrintString != printString:
|
|
# print(f"{dateTimeObj.now()} {printString}")
|
|
prevPrintString = printString
|
|
return returnValue
|
|
|
|
# Get ST_Status variables
|
|
def getEnabledStatus(self):
|
|
return self.getGenericVariable("stStatus.bEnabled", pyads.PLCTYPE_BOOL)
|
|
|
|
def getCommandAbortedStatus(self):
|
|
return self.getGenericVariable("stStatus.bCommandAborted", pyads.PLCTYPE_BOOL)
|
|
|
|
def getBusyStatus(self):
|
|
return self.getGenericVariable("stStatus.bBusy", pyads.PLCTYPE_BOOL)
|
|
|
|
def getDoneStatus(self):
|
|
return self.getGenericVariable("stStatus.bDone", pyads.PLCTYPE_BOOL)
|
|
|
|
def getHomedStatus(self):
|
|
return self.getGenericVariable("stStatus.bHomed", pyads.PLCTYPE_BOOL)
|
|
|
|
def getMovingStatus(self):
|
|
return self.getGenericVariable("stStatus.bMoving", pyads.PLCTYPE_BOOL)
|
|
|
|
def getMovingFwdStatus(self):
|
|
return self.getGenericVariable("stStatus.bMovingForward", pyads.PLCTYPE_BOOL)
|
|
|
|
def getMovingBwdStatus(self):
|
|
return self.getGenericVariable("stStatus.bMovingBackward", pyads.PLCTYPE_BOOL)
|
|
|
|
def getFwdEnabled(self):
|
|
return self.getGenericVariable("stStatus.bFwEnabled", pyads.PLCTYPE_BOOL)
|
|
|
|
def getBwdEnabled(self):
|
|
return self.getGenericVariable("stStatus.bBwEnabled", pyads.PLCTYPE_BOOL)
|
|
|
|
def getInterlockedFwd(self):
|
|
return self.getGenericVariable("stStatus.bInterlockedFwd", pyads.PLCTYPE_BOOL)
|
|
|
|
def getInterlockedBwd(self):
|
|
return self.getGenericVariable("stStatus.bInterlockedBwd", pyads.PLCTYPE_BOOL)
|
|
|
|
def getInTargetPosition(self):
|
|
return self.getGenericVariable("stStatus.bInTargetPosition", pyads.PLCTYPE_BOOL)
|
|
|
|
def getGearedStatus(self):
|
|
return self.getGenericVariable("stStatus.bGeared", pyads.PLCTYPE_BOOL)
|
|
|
|
def getCoupledGear1(self):
|
|
return self.getGenericVariable("stStatus.bCoupledGear1", pyads.PLCTYPE_BOOL)
|
|
|
|
def getCoupledGear2(self):
|
|
return self.getGenericVariable("stStatus.bCoupledGear2", pyads.PLCTYPE_BOOL)
|
|
|
|
def getCoupledGear3(self):
|
|
return self.getGenericVariable("stStatus.bCoupledGear3", pyads.PLCTYPE_BOOL)
|
|
|
|
def getCoupledGear4(self):
|
|
return self.getGenericVariable("stStatus.bCoupledGear4", pyads.PLCTYPE_BOOL)
|
|
|
|
def getActPos(self):
|
|
return self.getGenericVariable("stStatus.fActPosition", pyads.PLCTYPE_LREAL)
|
|
|
|
def getActVel(self):
|
|
return self.getGenericVariable("stStatus.fActVelocity", pyads.PLCTYPE_LREAL)
|
|
|
|
def getErrorStatus(self):
|
|
return self.getGenericVariable("stStatus.bError", pyads.PLCTYPE_BOOL)
|
|
|
|
def getErrorId(self):
|
|
return self.getGenericVariable("stStatus.nErrorID", pyads.PLCTYPE_UDINT)
|
|
|
|
#Status of the ST_AxisStatus of the AXIS_REF
|
|
def getConstantVelocityStatus(self):
|
|
return self.getGenericVariable("Axis.Status.ConstantVelocity", pyads.PLCTYPE_BOOL)
|
|
|
|
def getAcceleratingStatus(self):
|
|
return self.getGenericVariable("Axis.Status.Accelerating", pyads.PLCTYPE_BOOL)
|
|
|
|
def getDeceleratingStatus(self):
|
|
return self.getGenericVariable("Axis.Status.Decelerating", pyads.PLCTYPE_BOOL)
|
|
|
|
def getStandstillStatus(self):
|
|
return self.getGenericVariable("Axis.Status.Standstill", pyads.PLCTYPE_BOOL)
|
|
|
|
# Get ST_Config variables
|
|
def getHomeSequence(self):
|
|
return self.getGenericVariable("stConfig.eHomeSeq", pyads.PLCTYPE_INT)
|
|
|
|
def getHomePosition(self):
|
|
return self.getGenericVariable("stConfig.fHomePosition", pyads.PLCTYPE_LREAL)
|
|
|
|
def getHomeFinishDistance(self):
|
|
return self.getGenericVariable("stConfig.fHomeFinishDistance", pyads.PLCTYPE_LREAL)
|
|
|
|
def getVelocity(self):
|
|
return self.getGenericVariable("stControl.fVelocity", pyads.PLCTYPE_LREAL)
|
|
|
|
def getAcceleration(self):
|
|
return self.getGenericVariable("stControl.fAcceleration", pyads.PLCTYPE_LREAL)
|
|
|
|
def getDeceleration(self):
|
|
return self.getGenericVariable("stControl.fDeceleration", pyads.PLCTYPE_LREAL)
|
|
|
|
def getPosition(self):
|
|
return self.getGenericVariable("stControl.fPosition", pyads.PLCTYPE_LREAL)
|
|
|
|
def getMultiMasterAxis(self, masterIndex):
|
|
return self.getGenericVariable(
|
|
f"stConfig.astMultiMasterAxis[{masterIndex}].nIndex", pyads.PLCTYPE_UINT)
|
|
|
|
def getMultiMasterRatio(self, masterIndex):
|
|
return self.getGenericVariable(
|
|
f"stConfig.astMultiMasterAxis[{masterIndex}].fRatio", pyads.PLCTYPE_LREAL)
|
|
|
|
def getMultiMasterAxisLatched(self, masterIndex):
|
|
return self.getGenericVariable(
|
|
f"stConfig.astMultiMasterAxisLatched[{masterIndex}].nIndex",
|
|
pyads.PLCTYPE_UINT)
|
|
|
|
def getMultiMasterRatioLatched(self, masterIndex):
|
|
return self.getGenericVariable(
|
|
f"stConfig.astMultiMasterAxisLatched[{masterIndex}].fRatio",
|
|
pyads.PLCTYPE_LREAL)
|
|
|
|
def getMultiSlaveAxisRatio(self, slaveNum):
|
|
return self.getGenericVariable(
|
|
f"stConfig.afMultiSlaveAxisRatio[{slaveNum}]", pyads.PLCTYPE_LREAL)
|
|
|
|
def getVelocityHomeToCam(self):
|
|
return self.getGenericVariable("stConfig.fHomingVelToCam", pyads.PLCTYPE_LREAL)
|
|
|
|
def getVelocityHomeFromCam(self):
|
|
return self.getGenericVariable(
|
|
"stConfig.fHomingVelFromCam", pyads.PLCTYPE_LREAL)
|
|
|
|
def getVelocityMax(self):
|
|
return self.getGenericVariable("stConfig.fVeloMax", pyads.PLCTYPE_LREAL)
|
|
|
|
def getAccelMax(self):
|
|
return self.getGenericVariable("stConfig.fMaxAcc", pyads.PLCTYPE_LREAL)
|
|
|
|
def getDecelMax(self):
|
|
return self.getGenericVariable("stConfig.fMaxDec", pyads.PLCTYPE_LREAL)
|
|
|
|
def getSoftLimitFwdValue(self):
|
|
return self.getGenericVariable("stConfig.fMaxSoftPosLimit", pyads.PLCTYPE_LREAL)
|
|
|
|
def getSoftLimitBwdValue(self):
|
|
return self.getGenericVariable("stConfig.fMinSoftPosLimit", pyads.PLCTYPE_LREAL)
|
|
|
|
def getSoftLimitFwdEnableStatus(self):
|
|
return self.getGenericVariable(
|
|
"stConfig.bEnMaxSoftPosLimit", pyads.PLCTYPE_BOOL)
|
|
|
|
def getSoftLimitBwdEnableStatus(self):
|
|
return self.getGenericVariable(
|
|
"stConfig.bEnMinSoftPosLimit", pyads.PLCTYPE_BOOL)
|
|
|
|
def getAxisVeloManFast(self):
|
|
return self.getGenericVariable(
|
|
"stConfig.fVelocityDefaultFast", pyads.PLCTYPE_LREAL)
|
|
|
|
def getAxisVeloManSlow(self):
|
|
return self.getGenericVariable(
|
|
"stConfig.fVelocityDefaultSlow", pyads.PLCTYPE_LREAL)
|
|
|
|
def getAxisEnPositionLagMonitoring(self):
|
|
return self.getGenericVariable(
|
|
"stConfig.bEnPositionLagMonitoring", pyads.PLCTYPE_BOOL)
|
|
|
|
def getAxisPositionLagValue(self):
|
|
return self.getGenericVariable("stConfig.fMaxPosLagValue", pyads.PLCTYPE_LREAL)
|
|
|
|
def getAxisEnTargetPositionMonitoring(self):
|
|
return self.getGenericVariable(
|
|
"stConfig.bEnTargetPositionMonitoring", pyads.PLCTYPE_BOOL
|
|
)
|
|
|
|
def getAxisTargetPositionWindow(self):
|
|
return self.getGenericVariable(
|
|
"stConfig.fTargetPositionWindow", pyads.PLCTYPE_LREAL
|
|
)
|
|
|
|
|
|
# Get ST_Input variables
|
|
def getLimitFwd(self):
|
|
return self.getGenericVariable("stInputs.bLimitFwd", pyads.PLCTYPE_BOOL)
|
|
|
|
def getLimitBwd(self):
|
|
return self.getGenericVariable("stInputs.bLimitBwd", pyads.PLCTYPE_BOOL)
|
|
|
|
def getHomeSwitch(self):
|
|
return self.getGenericVariable("stInputs.bHome", pyads.PLCTYPE_BOOL)
|
|
|
|
# Generic function for setting any variable on the plc
|
|
def setGenericVariable(self, plcVarPath, plcVarValue, plcVarType):
|
|
plcVarName = f"GVL.astAxes[{self.axisNum}].{plcVarPath}"
|
|
print(f"{dateTimeObj.now()} {plcVarName}={plcVarValue}")
|
|
self.plc.connection.write_by_name(plcVarName, plcVarValue, plcVarType)
|
|
|
|
def defineAsSlave(self):
|
|
return setGenericVariable("Axis.NcToPlc.CoupleState", 3, pyads.PLCTYPE_UDINT)
|
|
|
|
def defineAsMaster(self):
|
|
return setGenericVariable("Axis.NcToPlc.CoupleState", 1, pyads.PLCTYPE_UDINT)
|
|
|
|
# Set ST_Control variables
|
|
def executeAxis(self):
|
|
self.setGenericVariable("stControl.bExecute", True, pyads.PLCTYPE_BOOL)
|
|
|
|
def resetAxis(self):
|
|
self.setGenericVariable("stControl.bReset", True, pyads.PLCTYPE_BOOL)
|
|
|
|
def haltAxis(self):
|
|
self.setGenericVariable("stControl.bHalt", True, pyads.PLCTYPE_BOOL)
|
|
|
|
def stopAxis(self):
|
|
self.setGenericVariable("stControl.bStop", True, pyads.PLCTYPE_BOOL)
|
|
|
|
def enableAxis(self):
|
|
self.setGenericVariable("stControl.bEnable", True, pyads.PLCTYPE_BOOL)
|
|
|
|
def disableAxis(self):
|
|
self.setGenericVariable("stControl.bEnable", False, pyads.PLCTYPE_BOOL)
|
|
|
|
def setMotionCommand(self, command): #Called by the functions regarding a move
|
|
plcVarName = f"GVL.astAxes[{self.axisNum}].stControl.eCommand"
|
|
print(f"{dateTimeObj.now()} {plcVarName}={command.name}")
|
|
self.plc.connection.write_by_name(plcVarName, command.value, pyads.PLCTYPE_INT)
|
|
|
|
def setVelocity(self, value):
|
|
self.setGenericVariable("stControl.fVelocity", value, pyads.PLCTYPE_LREAL)
|
|
|
|
def setJogVelocity(self, value):
|
|
self.setGenericVariable("stControl.fJogVelocity", value, pyads.PLCTYPE_LREAL)
|
|
|
|
def setAcceleration(self, value):
|
|
self.setGenericVariable("stControl.fAcceleration", value, pyads.PLCTYPE_LREAL)
|
|
|
|
def setDeceleration(self, value):
|
|
self.setGenericVariable("stControl.fDeceleration", value, pyads.PLCTYPE_LREAL)
|
|
|
|
def setPosition(self, value):
|
|
self.setGenericVariable("stControl.fPosition", value, pyads.PLCTYPE_LREAL)
|
|
|
|
# Set ST_Config variables
|
|
def setOverride(self, value):
|
|
self.setGenericVariable("stConfig.fOveride", value, pyads.PLCTYPE_LREAL)
|
|
|
|
def setHomePosition(self, value):
|
|
self.setGenericVariable("stConfig.fHomePosition", value, pyads.PLCTYPE_LREAL)
|
|
|
|
def setHomeFinishDistance(self, value):
|
|
self.setGenericVariable("stConfig.fHomeFinishDistance", value, pyads.PLCTYPE_LREAL)
|
|
|
|
def setHomeSequence(self, sequence): #Called by the function home() and homeSpecific()
|
|
plcVarName = f"GVL.astAxes[{self.axisNum}].stConfig.eHomeSeq"
|
|
print(f"{dateTimeObj.now()} {plcVarName}={sequence.name}")
|
|
self.plc.connection.write_by_name(plcVarName, sequence.value, pyads.PLCTYPE_INT)
|
|
|
|
def setMultiMasterAxis(self, masterNum, masterAxisNum, gearRatio):
|
|
self.setGenericVariable(
|
|
f"stConfig.astMultiMasterAxis[{masterNum}].nIndex",
|
|
masterAxisNum,
|
|
pyads.PLCTYPE_UINT)
|
|
self.setGenericVariable(
|
|
f"stConfig.astMultiMasterAxis[{masterNum}].fRatio",
|
|
gearRatio,
|
|
pyads.PLCTYPE_LREAL)
|
|
|
|
# Set some Nc Parameters that aren't exposed
|
|
def setSoftLimitsOn(self):
|
|
axis.setNcAxisParam(self, E_AxisParameters.EnableLimitFwd, 1.0)
|
|
axis.setNcAxisParam(self, E_AxisParameters.EnableLimitBwd, 1.0)
|
|
|
|
def setSoftLimitsOff(self):
|
|
axis.setNcAxisParam(self, E_AxisParameters.EnableLimitFwd, 0.0)
|
|
axis.setNcAxisParam(self, E_AxisParameters.EnableLimitBwd, 0.0)
|
|
|
|
def setFwdSoftLimitsOn(self):
|
|
axis.setNcAxisParam(self, E_AxisParameters.EnableLimitFwd, 1.0)
|
|
|
|
def setBwdSoftLimitsOn(self):
|
|
axis.setNcAxisParam(self, E_AxisParameters.EnableLimitBwd, 1.0)
|
|
|
|
def setFwdSoftLimitsOff(self):
|
|
axis.setNcAxisParam(self, E_AxisParameters.EnableLimitFwd, 0.0)
|
|
|
|
def setBwdSoftLimitsOff(self):
|
|
axis.setNcAxisParam(self, E_AxisParameters.EnableLimitBwd, 0.0)
|
|
|
|
def setSoftLimitFwdValue(self, softLimitFwdValue):
|
|
axis.setNcAxisParam(self, E_AxisParameters.SWLimitFwd, softLimitFwdValue)
|
|
|
|
def setSoftLimitBwdValue(self, softLimitBwdValue):
|
|
axis.setNcAxisParam(self, E_AxisParameters.SWLimitBwd, softLimitBwdValue)
|
|
|
|
def setAxisVeloManSlow(self, AxisVeloManSlow):
|
|
axis.setNcAxisParam(self, E_AxisParameters.AxisVeloManSlow, AxisVeloManSlow)
|
|
|
|
def setAxisEnTargetPositionMonitoringON(self):
|
|
axis.setNcAxisParam(self, E_AxisParameters.AxisEnTargetPositionMonitoring, 1.0)
|
|
|
|
def setAxisEnTargetPositionMonitoringOFF(self):
|
|
axis.setNcAxisParam(self, E_AxisParameters.AxisEnTargetPositionMonitoring, 0.0)
|
|
|
|
def setAxisTargetPositionWindow(self, axisTargetPositionWindow):
|
|
axis.setNcAxisParam(
|
|
self, E_AxisParameters.AxisTargetPositionWindow, axisTargetPositionWindow
|
|
)
|
|
|
|
###Motion commands###
|
|
def moveAbsolute(self, position):
|
|
print(f"Axis {self.axisNum}: Move absolute to position {position:.2f}")
|
|
self.setPosition(position)
|
|
self.setMotionCommand(E_MotionFunctions.eMoveAbsolute)
|
|
self.executeAxis()
|
|
|
|
def moveAbsoluteAndWait(self, position):
|
|
self.moveAbsolute(position)
|
|
timeout = self.calcTravelTimeForMove()+1
|
|
self.waitForCommandDone(timeoutDoneTrue=timeout)
|
|
return self.getDoneStatus()
|
|
|
|
def moveRelative(self, position):
|
|
print(f"Axis {self.axisNum}: Move relative to position {position:.2f}")
|
|
self.setPosition(position)
|
|
self.setMotionCommand(E_MotionFunctions.eMoveRelative)
|
|
self.executeAxis()
|
|
|
|
def moveRelativeAndWait(self, position):
|
|
self.moveRelative(position)
|
|
timeout = self.calcTravelTimeForMove()+1
|
|
self.waitForCommandDone(timeoutDoneTrue=timeout)
|
|
return self.getDoneStatus()
|
|
|
|
def jogFwd(self):
|
|
#self.setMotionCommand(E_MotionFunctions.eJog)
|
|
#self.setGenericVariable("stControl.bJogFwd", True, pyads.PLCTYPE_BOOL)
|
|
self.setVelocity(self.getAxisVeloManSlow())
|
|
self.setMotionCommand(E_MotionFunctions.eMoveVelocity)
|
|
self.executeAxis()
|
|
|
|
def jogBwd(self):
|
|
#self.setMotionCommand(E_MotionFunctions.eJog)
|
|
#self.setGenericVariable("stControl.bJogBwd", True, pyads.PLCTYPE_BOOL)
|
|
self.setVelocity(-(self.getAxisVeloManSlow()))
|
|
self.setMotionCommand(E_MotionFunctions.eMoveVelocity)
|
|
self.executeAxis()
|
|
|
|
def jogStop(self):
|
|
#self.setGenericVariable("stControl.bJogFwd", False, pyads.PLCTYPE_BOOL)
|
|
#self.setGenericVariable("stControl.bJogBwd", False, pyads.PLCTYPE_BOOL)
|
|
self.haltAxis()
|
|
|
|
def moveVelocity(self, velocity):
|
|
print(f"Axis {self.axisNum}: Move velocity with speed {velocity:.2f}")
|
|
self.setVelocity(velocity)
|
|
self.setMotionCommand(E_MotionFunctions.eMoveVelocity)
|
|
self.executeAxis()
|
|
|
|
def moveToSwitchFwd(self, velo, timeout):
|
|
print(f" Activate moving to Forward Limit Switch sequence...")
|
|
if self.getSoftLimitFwdEnableStatus():
|
|
print(' Disabling soft limits forward...')
|
|
self.setFwdSoftLimitsOff()
|
|
time.sleep(SLEEP_INTERVAL)
|
|
self.setBwdSoftLimitsOff()
|
|
time.sleep(SLEEP_INTERVAL)
|
|
if self.getSoftLimitFwdEnableStatus() or self.getSoftLimitBwdEnableStatus():
|
|
print(f' Error: Failed to disable soft limits')
|
|
return False
|
|
|
|
else:
|
|
print(f" Soft limits disabled, starting movement")
|
|
|
|
if not self.getLimitFwd():
|
|
print(" Axis possibly on the limit switch, moving away of it...")
|
|
for i in range(3):
|
|
if self.moveRelativeAndWait(-3):
|
|
time.sleep(1)
|
|
if self.getLimitFwd():
|
|
print(" Axis not on the limit switch anymore...")
|
|
break
|
|
else:
|
|
i += 1
|
|
if i == 3:
|
|
print(" ERROR: Timeout. Possibly limit without Power")
|
|
return False
|
|
|
|
print(' Moving to Fwd Swtich...')
|
|
time.sleep(SLEEP_INTERVAL)
|
|
self.moveVelocity(velo)
|
|
|
|
if self.waitForStatusBit(self.getLimitFwd, False, timeout):
|
|
print(' Fwd Limit reached...')
|
|
self.haltAxis()
|
|
time.sleep(SLEEP_INTERVAL)
|
|
return True
|
|
else:
|
|
print(f' ERROR: Axis {self.axisNum}: Timeout error waiting for LimitFwd to return False')
|
|
self.haltAxis()
|
|
return False
|
|
|
|
def moveToSwitchBwd(self, velo, timeout):
|
|
if velo is None:
|
|
velo = self.getJogVelocity()
|
|
|
|
print(f" Activate moving to Backward Limit Switch sequence...")
|
|
if self.getSoftLimitFwdEnableStatus() or self.getSoftLimitBwdEnableStatus():
|
|
print(' Disabling soft limits...')
|
|
self.setFwdSoftLimitsOff()
|
|
time.sleep(SLEEP_INTERVAL)
|
|
self.setBwdSoftLimitsOff()
|
|
time.sleep(SLEEP_INTERVAL)
|
|
if self.getSoftLimitFwdEnableStatus() or self.getSoftLimitBwdEnableStatus():
|
|
print(f' Error: Failed to disable soft limits')
|
|
return False
|
|
|
|
else:
|
|
print(f" Soft limits disabled, starting movement")
|
|
|
|
if not self.getLimitBwd():
|
|
print(" Axis possibly on the limit switch, moving away of it...")
|
|
for i in range(3):
|
|
if self.moveRelativeAndWait(3):
|
|
time.sleep(1)
|
|
if self.getLimitBwd():
|
|
print(" Axis not on the limit switch anymore...")
|
|
break
|
|
else:
|
|
i += 1
|
|
if i == 3:
|
|
print(" ERROR: Timeout. Possibly limit without Power")
|
|
return False
|
|
|
|
print(' Moving to Bwd Swtich...')
|
|
time.sleep(SLEEP_INTERVAL)
|
|
self.moveVelocity(-velo)
|
|
|
|
if self.waitForStatusBit(self.getLimitBwd, False, timeout):
|
|
print(' Bwd Limit reached...')
|
|
self.haltAxis()
|
|
time.sleep(SLEEP_INTERVAL)
|
|
return True
|
|
else:
|
|
print(f' ERROR: Axis {self.axisNum}: Timeout error waiting for LimitFwd to return False')
|
|
self.haltAxis()
|
|
return False
|
|
|
|
|
|
def gearInMultiMaster(
|
|
self,
|
|
master1=None,
|
|
ratio1=None,
|
|
master2=None,
|
|
ratio2=None,
|
|
master3=None,
|
|
ratio3=None,
|
|
master4=None,
|
|
ratio4=None,
|
|
):
|
|
print(f"Axis {self.axisNum}: Gear in multi master")
|
|
print(f"MasterAxis1 = {master1} with GearRatio1 = {ratio1}")
|
|
print(f"MasterAxis2 = {master2} with GearRatio2 = {ratio2}")
|
|
print(f"MasterAxis3 = {master3} with GearRatio3 = {ratio3}")
|
|
print(f"MasterAxis4 = {master4} with GearRatio4 = {ratio4}")
|
|
|
|
self.setMotionCommand(E_MotionFunctions.eGearInMultiMaster)
|
|
if not master1 == None:
|
|
self.setMultiMasterAxis(1, master1, ratio1)
|
|
if not master2 == None:
|
|
self.setMultiMasterAxis(2, master2, ratio2)
|
|
if not master3 == None:
|
|
self.setMultiMasterAxis(3, master3, ratio3)
|
|
if not master4 == None:
|
|
self.setMultiMasterAxis(4, master4, ratio4)
|
|
|
|
self.executeAxis()
|
|
|
|
def gearOut(self):
|
|
print(f"Axis {self.axisNum}: Gear Out")
|
|
self.setMotionCommand(E_MotionFunctions.eGearOut)
|
|
self.executeAxis()
|
|
|
|
def homeSpecific(self, homeSeq, homePos=0, homeFinishDist=0):
|
|
print(f"Axis {self.axisNum}: Home with HomeSeq={homeSeq}, HomePos={homePos}, HomeFinishDistance={homeFinishDist}")
|
|
self.setHomeSequence(E_HomingRoutines(homeSeq))
|
|
self.setHomePosition(homePos)
|
|
self.setHomeFinishDistance(homeFinishDist)
|
|
self.setMotionCommand(E_MotionFunctions.eHome)
|
|
self.executeAxis()
|
|
|
|
def home(self):
|
|
self.homeSeq = self.getHomeSequence()
|
|
self.homePos = self.getHomePosition()
|
|
self.homeFinishDist = self.getHomeFinishDistance()
|
|
print(f"Axis {self.axisNum}: Home with HomeSeq={self.homeSeq}, HomePos={self.homePos}, HomeFinishDistance={self.homeFinishDist}")
|
|
self.setMotionCommand(E_MotionFunctions.eHome)
|
|
self.executeAxis()
|
|
|
|
def setNcAxisParam(self, axisParam, writeAxisParam):
|
|
self.setMotionCommand(E_MotionFunctions.eWriteParameter)
|
|
|
|
plcVarName = f"GVL.astAxes[{self.axisNum}].stConfig.eAxisParameters"
|
|
print(f"{dateTimeObj.now()} {plcVarName}={axisParam.name}")
|
|
self.plc.connection.write_by_name(
|
|
plcVarName, axisParam.value, pyads.PLCTYPE_INT
|
|
)
|
|
|
|
plcVarName = f"GVL.astAxes[{self.axisNum}].stConfig.fWriteAxisParameter"
|
|
print(f"{dateTimeObj.now()} {plcVarName}={writeAxisParam}")
|
|
self.plc.connection.write_by_name(
|
|
plcVarName, writeAxisParam, pyads.PLCTYPE_LREAL
|
|
)
|
|
|
|
self.executeAxis()
|
|
time.sleep(0.05)
|
|
|
|
def getNcAxisParam(self, axisParam):
|
|
self.setMotionCommand(E_MotionFunctions.eReadParameter)
|
|
|
|
plcVarName = f"GVL.astAxes[{self.axisNum}].stConfig.eAxisParameters"
|
|
print(f"{dateTimeObj.now()} {plcVarName}={axisParam.name}")
|
|
self.plc.connection.write_by_name(
|
|
plcVarName, axisParam.value, pyads.PLCTYPE_INT
|
|
)
|
|
self.executeAxis()
|
|
time.sleep(SLEEP_INTERVAL)
|
|
|
|
plcVarName = f"GVL.astAxes[{self.axisNum}].stConfig.fReadAxisParameter"
|
|
readAxisParam = self.plc.connection.read_by_name(
|
|
plcVarName, pyads.PLCTYPE_LREAL
|
|
)
|
|
print(f"{dateTimeObj.now()} {plcVarName}=?: {readAxisParam}")
|
|
|
|
return readAxisParam
|
|
|
|
###Functions useful for testing###
|
|
|
|
#This function disables, reset and enables the axis
|
|
def axisInit(self):
|
|
#Disable Axis
|
|
if self.getEnabledStatus():
|
|
print(f" Disabling Axis...")
|
|
self.disableAxis()
|
|
time.sleep(1)
|
|
if self.waitForStatusBit(self.getEnabledStatus, False):
|
|
print(f" Axis disabled")
|
|
else:
|
|
print(" Axis disabled")
|
|
|
|
#Reset Axis
|
|
print(' Resetting axis...')
|
|
self.resetAxis()
|
|
time.sleep(SLEEP_INTERVAL)
|
|
|
|
#Enable Axis
|
|
if not self.getEnabledStatus():
|
|
print(f" Enabling Axis...")
|
|
self.enableAxis()
|
|
time.sleep(SLEEP_INTERVAL)
|
|
if self.waitForStatusBit(self.getEnabledStatus, True):
|
|
print(f" Axis Enabled")
|
|
|
|
def waitForVariable(self, varName, plcVarType, expectedValue, timeout=30, sleepInterval=SLEEP_INTERVAL):
|
|
# If timeout is negative time then just use a default
|
|
if timeout < 0:
|
|
timeout = 1
|
|
|
|
timeLimit = time.time() + timeout
|
|
timeoutError = False
|
|
while True:
|
|
variableValue=self.plc.connection.read_by_name(varName, plcVarType)
|
|
if str(variableValue) == str(expectedValue):
|
|
break
|
|
if time.time() > timeLimit:
|
|
timeoutError = True
|
|
break
|
|
if sleepInterval > 0:
|
|
time.sleep(sleepInterval)
|
|
|
|
if timeoutError:
|
|
print(
|
|
f" Axis {self.axisNum}: Timeout error waiting for {varName} with value {variableValue} to return {expectedValue}"
|
|
)
|
|
return False
|
|
else:
|
|
return True
|
|
|
|
# boolValue is the status you're waiting for
|
|
# if you're waiting a bit to go high then this should be True
|
|
def waitForStatusBit(
|
|
self, getStatusBitFunction, boolValue, timeout=30, sleepInterval=SLEEP_INTERVAL
|
|
):
|
|
# If timeout is negative time then just use a default
|
|
if timeout < 0:
|
|
timeout = 1
|
|
|
|
timeLimit = time.time() + timeout
|
|
timeoutError = False
|
|
while True:
|
|
statusBit = getStatusBitFunction()
|
|
if statusBit == boolValue:
|
|
break
|
|
if time.time() > timeLimit:
|
|
timeoutError = True
|
|
break
|
|
if sleepInterval > 0:
|
|
time.sleep(sleepInterval)
|
|
|
|
if timeoutError:
|
|
print(
|
|
f" Axis {self.axisNum}: Timeout error waiting for {getStatusBitFunction.__name__} to return {boolValue}"
|
|
)
|
|
return False
|
|
else:
|
|
return True
|
|
|
|
def waitForCommandAborted(self):
|
|
return self.waitForStatusBit(self.getCommandAbortedStatus, True)
|
|
|
|
# This ones a bit different to the previous generic waitForStatusBit
|
|
# It waits for bDone to go low, then bBusy high, then bDone high
|
|
def waitForCommandDone(
|
|
self,
|
|
timeoutDoneFalse=5,
|
|
timeoutBusyTrue=5,
|
|
timeoutDoneTrue=30,
|
|
sleepInterval=SLEEP_INTERVAL,
|
|
):
|
|
if not self.waitForStatusBit(
|
|
self.getDoneStatus,
|
|
False,
|
|
timeout=timeoutDoneFalse,
|
|
sleepInterval=sleepInterval,
|
|
):
|
|
print(
|
|
f" Axis {self.axisNum} Error: bDone status did not go low within {timeoutDoneFalse} seconds"
|
|
)
|
|
return False
|
|
if not self.waitForStatusBit(
|
|
self.getBusyStatus,
|
|
True,
|
|
timeout=timeoutBusyTrue,
|
|
sleepInterval=sleepInterval,
|
|
):
|
|
print(
|
|
f" Axis {self.axisNum} Error: bBusy status did not go high within {timeoutBusyTrue} seconds"
|
|
)
|
|
return False
|
|
if not self.waitForStatusBit(
|
|
self.getDoneStatus,
|
|
True,
|
|
timeout=timeoutDoneTrue,
|
|
sleepInterval=sleepInterval,
|
|
):
|
|
print(
|
|
f" Axis {self.axisNum} Error: bDone status did not go high within {timeoutDoneTrue} seconds"
|
|
)
|
|
return False
|
|
return True
|
|
|
|
# This one is also a bit special as I don't think we currently have a
|
|
# stop bit in the ast.axisStruct
|
|
# Therefore this function checks the actual velocity is 0
|
|
def waitForStop(
|
|
self, timeout=30, sleepInterval=SLEEP_INTERVAL, roundVelDecimalPlaces=2
|
|
):
|
|
# If timeout is negative time then just use a default
|
|
if timeout < 0:
|
|
timeout = 1
|
|
|
|
timeLimit = time.time() + timeout
|
|
bTimeoutError = False
|
|
while True:
|
|
if round(self.getActVel(), roundVelDecimalPlaces) == 0 or not self.getMovingStatus():
|
|
break
|
|
if time.time() > timeLimit:
|
|
bTimeoutError = True
|
|
break
|
|
if sleepInterval > 0:
|
|
time.sleep(sleepInterval)
|
|
|
|
if bTimeoutError:
|
|
print(
|
|
f" Axis {self.axisNum} Error: Timeout of {timeout} exceeded waiting for velocity to be zero"
|
|
)
|
|
return False
|
|
else:
|
|
return True
|
|
|
|
# Check that the position is within the specificed range post move
|
|
def checkTargetPositionWindow(self, targetPos=None):
|
|
# Not sure if getPos is the best variable, this is just what's in the
|
|
# axisStuct and not the setPos in the NC
|
|
actPos = self.getActPos()
|
|
if targetPos == None:
|
|
targetPos = self.getPosition()
|
|
targetPosWindow = self.getAxisTargetPositionWindow()
|
|
|
|
error = abs(actPos - targetPos)
|
|
|
|
print(
|
|
f"Set Pos: {targetPos:.2f}, Act Pos: {actPos:.2f}, Error: {error:.2f}, Pos Window: {targetPosWindow:.2f}"
|
|
)
|
|
|
|
if error < targetPosWindow:
|
|
return True
|
|
else:
|
|
return False
|
|
|
|
def calcTravelTimeForMove(self, marginOfSafety=MARGIN_OF_SAFETY):
|
|
print(f"Calculating expected travel time for current move")
|
|
|
|
vel = self.getVelocity()
|
|
acc = self.getAcceleration()
|
|
dec = self.getDeceleration()
|
|
currentPos = self.getActPos()
|
|
finalPos = self.getPosition()
|
|
|
|
print(
|
|
f"Vel={vel:.2f}, Acc={acc:.2f}, Dec={dec:.2f}, Curr Pos={currentPos:.2f},Final Pos={finalPos:.2f}"
|
|
)
|
|
|
|
timeToAcc = abs(vel / acc)
|
|
timeToDec = abs(vel / dec)
|
|
|
|
accDist = 0.5 * acc * timeToAcc * timeToAcc
|
|
decDist = 0.5 * acc * timeToDec * timeToDec
|
|
|
|
distToTravel = abs(currentPos - finalPos)
|
|
|
|
distForConstVel = distToTravel - accDist - decDist
|
|
|
|
if vel == 0:
|
|
print(" Error: Can't divide by a velocity of 0")
|
|
return -1
|
|
|
|
timeAtConstVel = abs(distForConstVel / vel)
|
|
|
|
totalTravelTime = timeToAcc + timeAtConstVel + timeToDec
|
|
|
|
estTravelTime = totalTravelTime * marginOfSafety
|
|
|
|
print(
|
|
f"Estimated time for the move is: {estTravelTime:.2f}s with a safety factor of {marginOfSafety}"
|
|
)
|
|
|
|
return estTravelTime
|
|
|
|
def calcTravelTimeForPosition(self, finalPos, marginOfSafety=MARGIN_OF_SAFETY):
|
|
print(f"Calculating expected travel time for current move")
|
|
|
|
vel = self.getVelocity()
|
|
acc = self.getAcceleration()
|
|
dec = self.getDeceleration()
|
|
currentPos = self.getActPos()
|
|
|
|
print(
|
|
f"Vel={vel:.2f}, Acc={acc:.2f}, Dec={dec:.2f}, Curr Pos={currentPos:.2f},Final Pos={finalPos:.2f}"
|
|
)
|
|
|
|
timeToAcc = abs(vel / acc)
|
|
timeToDec = abs(vel / dec)
|
|
|
|
accDist = 0.5 * acc * timeToAcc * timeToAcc
|
|
decDist = 0.5 * acc * timeToDec * timeToDec
|
|
|
|
distToTravel = abs(currentPos - finalPos)
|
|
|
|
distForConstVel = distToTravel - accDist - decDist
|
|
|
|
if vel == 0:
|
|
print(" Error: Can't divide by a velocity of 0")
|
|
return -1
|
|
|
|
timeAtConstVel = abs(distForConstVel / vel)
|
|
|
|
totalTravelTime = timeToAcc + timeAtConstVel + timeToDec
|
|
|
|
estTravelTime = totalTravelTime * marginOfSafety
|
|
|
|
print(
|
|
f"Estimated time for the move is: {estTravelTime:.2f}s with a safety factor of {marginOfSafety}"
|
|
)
|
|
|
|
return estTravelTime
|
|
|
|
# This calculation uses the soft limits to calculate the max distance the
|
|
# axis would have to move.
|
|
# This distance divided by the homing speed estimates the max homing time
|
|
# and can thus be used for the timeout when homing
|
|
def calcTravelTimeForRange(self, marginOfSafety=MARGIN_OF_SAFETY):
|
|
print(f"Calculating expected travel time for axis range")
|
|
softLimFwd = self.getSoftLimitFwdValue()
|
|
softLimNeg = self.getSoftLimitBwdValue()
|
|
homingVelocity = self.getVelocityHomeFromCam()
|
|
|
|
if (softLimFwd == 0 and softLimNeg == 0):
|
|
print(f' ERROR: No soft limits defined, cannot calculate travel range')
|
|
return -1
|
|
|
|
travelRange = abs(softLimFwd - softLimNeg)
|
|
|
|
# Use 2 * travel range because if you start homing in the wrong direction
|
|
# you might need to move the whol range twice
|
|
# Use homing from cam as it's a slower speed
|
|
if homingVelocity == 0:
|
|
print(" Error: Can't divide by a homing velocity of 0")
|
|
return -1
|
|
|
|
totalTravelTime = abs(2 * travelRange / homingVelocity)
|
|
|
|
print(f"Range={travelRange:.2f}, Homing Vel={homingVelocity:.2f}")
|
|
|
|
estTravelTime = totalTravelTime * marginOfSafety
|
|
|
|
print(
|
|
f"Estimated time for the axis range is: {estTravelTime:.2f}s with a safety factor of {marginOfSafety:.2f}"
|
|
)
|
|
|
|
return estTravelTime
|
|
|
|
def calcTimeForAccel(self, marginOfSafety=MARGIN_OF_SAFETY):
|
|
print(f"Calculating expected travel time for axis acceleration")
|
|
|
|
setVel = (
|
|
self.getVelocityHomeFromCam()
|
|
) # perhaps should be changed as it could be to cam as well
|
|
accel = self.getAcceleration()
|
|
|
|
if accel == 0:
|
|
print(" Error: Can't divide by an acceleration of 0")
|
|
return -1
|
|
|
|
timeForAccel = abs(setVel / accel)
|
|
|
|
print(f"Set Velocity={setVel:.2f}, Acceleration={accel:.2f}")
|
|
|
|
estTravelTime = timeForAccel * marginOfSafety
|
|
|
|
print(
|
|
f"Estimated Acceleration time for the axis is: {estTravelTime:.2f}s with a safety factor of {marginOfSafety:.2f}"
|
|
)
|
|
|
|
return estTravelTime
|
|
|
|
def calcTimeForDecel(self, marginOfSafety=MARGIN_OF_SAFETY):
|
|
print(f"Calculating expected travel time for axis deceleration")
|
|
|
|
# Since this is just used for timeouts mostly I've used max vel
|
|
# instead of actVel as there were issues with delays and actVel
|
|
# not always being accurate
|
|
actVel = self.getVelocityMax()
|
|
decel = self.getDeceleration()
|
|
|
|
if decel == 0:
|
|
print(" Error: Can't divide by a deceleration of 0")
|
|
return -1
|
|
|
|
timeForDecel = abs(actVel / decel)
|
|
|
|
print(f"Act Velocity={actVel:.2f}, Deceleration={decel:.2f}")
|
|
|
|
estTravelTime = timeForDecel * marginOfSafety
|
|
|
|
print(
|
|
f"Estimated deceleration time for the axis is: {estTravelTime:.2f}s with a safety factor of {marginOfSafety:.2f}"
|
|
)
|
|
|
|
return estTravelTime
|
|
|
|
class PneumaticAxis:
|
|
def __init__(self, plcConnection, axisNum):
|
|
print("Constructor for axis")
|
|
self.plc = plcConnection
|
|
self.axisNum = axisNum
|
|
|
|
def __del__(self):
|
|
print("Destructor for pneumatic axis: going to fail safe state")
|
|
self.setValveOff()
|
|
|
|
# Generic function for getting any variable on the pneumatic axis
|
|
def getGenericVariable(self, plcVarPath, plcVarType):
|
|
plcVarName = f"GVL.astPneumaticAxes[{self.axisNum}].{plcVarPath}"
|
|
returnValue = self.plc.connection.read_by_name(plcVarName, plcVarType)
|
|
global prevPrintString
|
|
printString = f"{plcVarName}=: {returnValue}"
|
|
if prevPrintString != printString:
|
|
print(f"{dateTimeObj.now()} {printString}")
|
|
prevPrintString = printString
|
|
return returnValue
|
|
|
|
# Get ST_PneumaticAxisStatus variables
|
|
def getExtendingStatus(self):
|
|
return self.getGenericVariable("stPneumaticAxisStatus.bExtending", pyads.PLCTYPE_BOOL)
|
|
|
|
def getRetractingStatus(self):
|
|
return self.getGenericVariable("stPneumaticAxisStatus.bRetracting", pyads.PLCTYPE_BOOL)
|
|
|
|
def getExtendedStatus(self):
|
|
return self.getGenericVariable("stPneumaticAxisStatus.bExtended", pyads.PLCTYPE_BOOL)
|
|
|
|
def getRetractedStatus(self):
|
|
return self.getGenericVariable("stPneumaticAxisStatus.bRetracted", pyads.PLCTYPE_BOOL)
|
|
|
|
def getSolenoidActiveStatus(self):
|
|
return self.getGenericVariable("stPneumaticAxisStatus.bSolenoidActive", pyads.PLCTYPE_BOOL)
|
|
|
|
def getInterlockedStatus(self):
|
|
return self.getGenericVariable("stPneumaticAxisStatus.bInterlocked", pyads.PLCTYPE_BOOL)
|
|
|
|
def getPSSPermitOKStatus(self):
|
|
return self.getGenericVariable("stPneumaticAxisStatus.bPSSPermitOK", pyads.PLCTYPE_BOOL)
|
|
|
|
def getErrorStatus(self):
|
|
return self.getGenericVariable("stPneumaticAxisStatus.bError", pyads.PLCTYPE_BOOL)
|
|
|
|
def getTimeElapsedExtend(self):
|
|
return self.getGenericVariable("stPneumaticAxisStatus.nTimeElapsedExtend", pyads.PLCTYPE_INT)
|
|
|
|
def getTimeElapsedRetract(self):
|
|
return self.getGenericVariable("stPneumaticAxisStatus.nTimeElapsedRetract", pyads.PLCTYPE_INT)
|
|
|
|
def getStatus(self):
|
|
return self.getGenericVariable("stPneumaticAxisStatus.sStatus", pyads.PLCTYPE_STRING)
|
|
|
|
# Get ST_PneumaticAxisConfig variables
|
|
def getTimeToExtend(self):
|
|
return self.getGenericVariable("stPneumaticAxisConfig.nTimeToExtend", pyads.PLCTYPE_INT)
|
|
|
|
def getTimeToRetract(self):
|
|
return self.getGenericVariable("stPneumaticAxisConfig.nTimeToRetract", pyads.PLCTYPE_INT)
|
|
|
|
# Get ST_PneumaticAxisInputs variables
|
|
def getEndSwitchFwd(self):
|
|
return self.getGenericVariable("stPneumaticAxisInputs.bEndSwitchFwd", pyads.PLCTYPE_BOOL)
|
|
|
|
def getEndSwitchBwd(self):
|
|
return self.getGenericVariable("stPneumaticAxisInputs.bEndSwitchBwd", pyads.PLCTYPE_BOOL)
|
|
|
|
def getSolenoidActive(self):
|
|
return self.getGenericVariable("stPneumaticAxisInputs.bSolenoidActive", pyads.PLCTYPE_BOOL)
|
|
|
|
def getPSSPermit(self):
|
|
return self.getGenericVariable("stPneumaticAxisInputs.bPSSPermit", pyads.PLCTYPE_BOOL)
|
|
|
|
def getPressureExtend(self):
|
|
return self.getGenericVariable("stPneumaticAxisInputs.bPressureExtend", pyads.PLCTYPE_BOOL)
|
|
|
|
def getPressureRetract(self):
|
|
return self.getGenericVariable("stPneumaticAxisInputs.bPressureRetract", pyads.PLCTYPE_BOOL)
|
|
|
|
def getOpenManual(self):
|
|
return self.getGenericVariable("stPneumaticAxisInputs.bOpenManual", pyads.PLCTYPE_BOOL)
|
|
|
|
def getCloseManual(self):
|
|
return self.getGenericVariable("stPneumaticAxisInputs.bCloseManual", pyads.PLCTYPE_BOOL)
|
|
|
|
def getAirPressureValve(self):
|
|
return self.getGenericVariable("stPneumaticAxisInputs.nAirPressureValve", pyads.PLCTYPE_INT)
|
|
|
|
def getPressureValue(self):
|
|
return self.getGenericVariable("stPneumaticAxisInputs.nPressureValue", pyads.PLCTYPE_INT)
|
|
|
|
# Get ST_PneumaticAxisOutputs variables
|
|
def getValveState(self):
|
|
return self.getGenericVariable("stPneumaticAxisOutputs.bValveOn", pyads.PLCTYPE_BOOL)
|
|
|
|
def getAirPressureOnState(self):
|
|
return self.getGenericVariable("stPneumaticAxisOutputs.bAirPressureOn", pyads.PLCTYPE_BOOL)
|
|
|
|
# Generic function for setting any variable on the plc
|
|
def setGenericVariable(self, plcVarPath, plcVarValue, plcVarType):
|
|
plcVarName = f"GVL.astPneumaticAxes[{self.axisNum}].{plcVarPath}"
|
|
print(f"{dateTimeObj.now()} {plcVarName}={plcVarValue}")
|
|
self.plc.connection.write_by_name(plcVarName, plcVarValue, plcVarType)
|
|
|
|
# Set ST_PneumaticAxisControl variables
|
|
def extendPneumaticAxis(self):
|
|
self.setGenericVariable("stPneumaticAxisControl.bExtend", True, pyads.PLCTYPE_BOOL)
|
|
|
|
def retractPneumaticAxis(self):
|
|
self.setGenericVariable("stPneumaticAxisControl.bRetract", True, pyads.PLCTYPE_BOOL)
|
|
|
|
def interlockPneumaticAxis(self):
|
|
self.setGenericVariable("stPneumaticAxisControl.bInterlock", True, pyads.PLCTYPE_BOOL)
|
|
|
|
def resetPneumaticAxis(self):
|
|
self.setGenericVariable("stPneumaticAxisControl.bReset", True, pyads.PLCTYPE_BOOL)
|
|
|
|
# Set ST_PneumaticAxisConfig variables
|
|
def setTimeToExtend(self, value):
|
|
return self.setGenericVariable("stPneumaticAxisConfig.nTimeToExtend", value, pyads.PLCTYPE_INT)
|
|
|
|
def setTimeToRetract(self, value):
|
|
return self.setGenericVariable("stPneumaticAxisConfig.nTimeToRetract", value, pyads.PLCTYPE_INT)
|
|
|
|
# Set ST_PneumaticAxisOutputs variables
|
|
def setValveOn(self):
|
|
self.setGenericVariable("stPneumaticAxisOutputs.bValveOn", True, pyads.PLCTYPE_BOOL)
|
|
|
|
def setValveOff(self):
|
|
self.setGenericVariable("stPneumaticAxisOutputs.bValveOn", False, pyads.PLCTYPE_BOOL)
|
|
|
|
###Motion commands###
|
|
def extendAndWait(self):
|
|
timeout = self.getTimeToExtend()
|
|
self.extendPneumaticAxis()
|
|
self.waitForExtended(timeoutExtended=timeout)
|
|
return self.getExtendedStatus()
|
|
|
|
def retractAndWait(self):
|
|
timeout = self.getTimeToRetract()
|
|
self.retractPneumaticAxis()
|
|
self.waitForRetracted(timeoutRetracted=timeout)
|
|
return self.getRetractedStatus()
|
|
|
|
def ValveOffAndWait(self, timeoutMovement):
|
|
self.setValveOff()
|
|
self.waitForValveStateChange(timeoutMovementDone=timeoutMovement)
|
|
return self.getValveState()
|
|
|
|
def ValveONAndWait(self, timeoutMovement):
|
|
self.setValveOn()
|
|
self.waitForValveStateChange(timeoutMovementDone=timeoutMovement)
|
|
return self.getValveState()
|
|
|
|
# boolValue is the status you're waiting for
|
|
# if you're waiting a bit to go high then this should be True
|
|
def waitForStatusBit(
|
|
self, getStatusBitFunction, boolValue, timeout=30, sleepInterval=SLEEP_INTERVAL
|
|
):
|
|
# If timeout is negative time then just use a default
|
|
if timeout < 0:
|
|
timeout = 1
|
|
|
|
timeLimit = time.time() + timeout
|
|
timeoutError = False
|
|
while True:
|
|
statusBit = getStatusBitFunction()
|
|
if statusBit == boolValue:
|
|
break
|
|
if time.time() > timeLimit:
|
|
timeoutError = True
|
|
break
|
|
if sleepInterval > 0:
|
|
time.sleep(sleepInterval)
|
|
|
|
if timeoutError:
|
|
print(
|
|
f" Axis {self.axisNum}: Timeout error waiting for {getStatusBitFunction.__name__} to return {boolValue}"
|
|
)
|
|
return False
|
|
else:
|
|
return True
|
|
|
|
def waitForExtended(self,
|
|
timeoutExtended=30,
|
|
timeoutRetractedFalse=3,
|
|
timeoutExtending=3,
|
|
sleepInterval=SLEEP_INTERVAL):
|
|
|
|
if not self.waitForStatusBit(
|
|
self.getRetractedStatus,
|
|
False,
|
|
timeout=timeoutRetractedFalse,
|
|
sleepInterval=sleepInterval,
|
|
):
|
|
print(
|
|
f" Axis {self.axisNum} Error: bRetracted status did not go low within {timeoutRetractedFalse} seconds"
|
|
)
|
|
return False
|
|
if not self.waitForStatusBit(
|
|
self.getExtendingStatus,
|
|
True,
|
|
timeout=timeoutExtending,
|
|
sleepInterval=sleepInterval,
|
|
):
|
|
print(
|
|
f" Axis {self.axisNum} Error: bExtending status did not go high within {timeoutExtending} seconds"
|
|
)
|
|
return False
|
|
if not self.waitForStatusBit(
|
|
self.getExtendedStatus,
|
|
True,
|
|
timeout=timeoutExtended,
|
|
sleepInterval=sleepInterval,
|
|
):
|
|
print(
|
|
f" Axis {self.axisNum} Error: bExtended status did not go high within {timeoutExtended} seconds"
|
|
)
|
|
return False
|
|
return True
|
|
|
|
def waitForRetracted(self,
|
|
timeoutRetracted=30,
|
|
timeoutExtendedFalse=3,
|
|
timeoutRetracting=3,
|
|
sleepInterval=SLEEP_INTERVAL):
|
|
|
|
if not self.waitForStatusBit(
|
|
self.getExtendedStatus,
|
|
False,
|
|
timeout=timeoutExtendedFalse,
|
|
sleepInterval=sleepInterval,
|
|
):
|
|
print(
|
|
f" Axis {self.axisNum} Error: bExtended status did not go low within {timeoutExtendedFalse} seconds"
|
|
)
|
|
return False
|
|
if not self.waitForStatusBit(
|
|
self.getRetractingStatus,
|
|
True,
|
|
timeout=timeoutRetracting,
|
|
sleepInterval=sleepInterval,
|
|
):
|
|
print(
|
|
f" Axis {self.axisNum} Error: bRetracting status did not go high within {timeoutRetracting} seconds"
|
|
)
|
|
return False
|
|
if not self.waitForStatusBit(
|
|
self.getRetractedStatus,
|
|
True,
|
|
timeout=timeoutRetracted,
|
|
sleepInterval=sleepInterval,
|
|
):
|
|
print(
|
|
f" Axis {self.axisNum} Error: bRetracted status did not go high within {timeoutRetracted} seconds"
|
|
)
|
|
return False
|
|
return True
|
|
|
|
def waitForSwitchStateChange(self,
|
|
timeoutMovementDone=30,
|
|
timeoutEndSwitchOff=3,
|
|
timeoutMoving=3,
|
|
sleepInterval=SLEEP_INTERVAL):
|
|
|
|
if self.getEndSwitchBwd():
|
|
if not self.waitForStatusBit(
|
|
self.getEndSwitchBwd,
|
|
False,
|
|
timeout=timeoutEndSwitchOff,
|
|
sleepInterval=sleepInterval,
|
|
):
|
|
print(
|
|
f" Axis {self.axisNum} Error: bEndSwitchBwd status did not go low within {timeoutEndSwitchOff} seconds"
|
|
)
|
|
return False
|
|
|
|
if not self.waitForStatusBit(
|
|
self.getEndSwitchFwd,
|
|
True,
|
|
timeout=timeoutMovementDone,
|
|
sleepInterval=sleepInterval,
|
|
):
|
|
print(
|
|
f" Axis {self.axisNum} Error: bEndSwitchFwd status did not go high within {timeoutMovementDone} seconds"
|
|
)
|
|
return False
|
|
return True
|
|
else:
|
|
if not self.waitForStatusBit(
|
|
self.getEndSwitchFwd,
|
|
False,
|
|
timeout=timeoutEndSwitchOff,
|
|
sleepInterval=sleepInterval,
|
|
):
|
|
print(
|
|
f" Axis {self.axisNum} Error: bEndSwitchFwd status did not go low within {timeoutEndSwitchOff} seconds"
|
|
)
|
|
return False
|
|
|
|
if not self.waitForStatusBit(
|
|
self.getEndSwitchBwd,
|
|
True,
|
|
timeout=timeoutMovementDone,
|
|
sleepInterval=sleepInterval,
|
|
):
|
|
print(
|
|
f" Axis {self.axisNum} Error: bEndSwitchBwd status did not go high within {timeoutMovementDone} seconds"
|
|
)
|
|
return False
|
|
return True |