From 018891301f5fc8bffa3a8b92142ff48e981320f7 Mon Sep 17 00:00:00 2001 From: berti_r Date: Tue, 9 Sep 2025 09:47:45 +0200 Subject: [PATCH] initial commit --- .idea/.gitignore | 0 .../inspectionProfiles/profiles_settings.xml | 6 + .idea/workspace.xml | 50 + __pycache__/eAxisParameters.cpython-313.pyc | Bin 0 -> 4682 bytes .../motionFunctionsLib.cpython-313.pyc | Bin 0 -> 65165 bytes eAxisParameters.py | 131 ++ motionFunctionsLib.py | 1348 +++++++++++++++++ 7 files changed, 1535 insertions(+) create mode 100644 .idea/.gitignore create mode 100644 .idea/inspectionProfiles/profiles_settings.xml create mode 100644 .idea/workspace.xml create mode 100644 __pycache__/eAxisParameters.cpython-313.pyc create mode 100644 __pycache__/motionFunctionsLib.cpython-313.pyc create mode 100644 eAxisParameters.py create mode 100644 motionFunctionsLib.py diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..e69de29 diff --git a/.idea/inspectionProfiles/profiles_settings.xml b/.idea/inspectionProfiles/profiles_settings.xml new file mode 100644 index 0000000..105ce2d --- /dev/null +++ b/.idea/inspectionProfiles/profiles_settings.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/.idea/workspace.xml b/.idea/workspace.xml new file mode 100644 index 0000000..a30dda7 --- /dev/null +++ b/.idea/workspace.xml @@ -0,0 +1,50 @@ + + + + + + { + "associatedIndex": 0 +} + + + + { + "keyToString": { + "ModuleVcsDetector.initialDetectionPerformed": "true", + "RunOnceActivity.ShowReadmeOnStart": "true", + "last_opened_file_path": "C:/Users/berti_r/Python_Projects/templates/motion_libs", + "nodejs_package_manager_path": "npm", + "vue.rearranger.settings.migration": "true" + } +} + + + + + + + + + + 1752073467700 + + + + + + \ No newline at end of file diff --git a/__pycache__/eAxisParameters.cpython-313.pyc b/__pycache__/eAxisParameters.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..84604dc802871fab6bf83fa02fbda74288eea876 GIT binary patch literal 4682 zcma)yWki9R9tCQ*B6n(~7S`m{>2oLa zFdgwCGZ{H88Sx)J{K!Y&D_aUiK*1V_U@g?33bl9+RIGz@c%IMILmgg#^Vk6Oco8mO zBQ#(WT*PK*#1?46R%pgc(1LByitTU-JD?3ap&g^pfnCsvF^FO}bYTy~uot?q4|=d4 zdT{{ya1i=&2nKK%25|(2a1@4d3`TGqMsWhha1zFG3MOzGCUFL)a2BTVGR)u=n8mAb z8Lz<=ybf3K23*6Na2;>K4ZICE@ebU=yKo!t!5zF0ckuz-!-sGmAHf5B3=i=MJi@2& z7@xrtd=5`>4xZsWJjXc9VFKnc32|J21ipYIzJvu_gcrC3FL4Iah#!YyIpTleX0ygnW_#D52FK`RK#INBi{06pAgRk*h z_y%>*a2vkG4Ct7JZDf$a9mrx181BLj8j!;`u#0_6YfWl% z2S#CCN8MsB!@>#W%L!feNezJ#<3uIY5zZ6p36yas7YU7oWZUJg(X<@{3$AI{%yzi=QrxjD-OjS?x>GP*!?C>v*jzCz!(DinJsr>UadXnvw@r2` z3qO-QD&>5g6+M@7Y{PZXuybB>NN7g@P;Ws)U<{o#n?GkalSubIc7CLWaVXYN>BI zM3&W(ikTk!9(_Jo7}(B<+loWw^GpQ(&&wD?=it5(csi zg=WzfxhAW_*H7t%J-JmdK~?R%*3BZ5d72UvX@}~8`{l<^B*kep#vSzckUR5;Ri;9j zCeHMVm+hj>lP{0Vl>rek=eObDC(bs}N#chYlZo3AD^L-E>*T@6*}5rWaFNt#o|mq7 z`oPQ86Tv2lDA90WAfxjpm0K-ZRO$`}g&?Bl3lqGDS%xjdK>-<7(fYJQ@X8`VRflLJ zujm4oidKF>&(J2aDuQs%>!vwZJRm)9kSGgOw8;e9H$;2_1!0;*bZ{M=yq*(6en0+A zot20rmWPTv#k|SDxFE%ICT~-+8sb(q?FdETlc@2t;^ct~%oTERLO*q& zxGmDjPkj+ap0^Ac&l@=8d2M-pWaaAzbtMyIJ$<7}8Z^O*RCEpBHXQ$ohUmz?hKNhp zR4Q-&=K#p_fST1cuNd?FPdryYD-WQ#WM zHGxNd%AU?w0zN{~lNJfak0f>xwiO*wHd3ehAflay1)+ou-*2^*xQ$Q6(sq*k`G2vs z9LL?2K8b$uvlQv&IQ27DTtnV@J<+Me-s1X&pZsyh4flS(KZ~OW*B^Im7aenX->+2{ zPk*SaBa8FLO=jeFT~8F7ug%-B*hbEAow=gx_^XSElU-$wnkUL%4b=jX9I|VRsu!Jt z*Gb@1CNJM_WEDyn=}Yx1T)xRRn57#wUxBtvJ1-pn=T0ykyf%eM#OwvHL|GcmtNe3j zr_$}|d4Id|+^K2&o$DGIO?O>1wu>$+Xd1TjZ;ucBzv3;%A8x%W@VDsJHbd9YaBKb0 z-F0k@H_SKuEm+v%O(<{b+-}PfzZW!K$%QROw_N!Wx|%;k`WOsHFD#jK+@E-q$J3ZAS~`&TcX$ z42_l4{&K93w0rnMUFCrhvKbsLseS%Yt>uAL{l}25?CAH&NIx;d*QU}Y?9)1`pl&eV3gzz{tf7}r}O8FCQ z(WB+E0BvwA7ZUy-OKZvQ{*oH?J)D>+siRfi%JWLf^Gbc``RHUx9V$!82na{zaO!eN z9WTpmWeBCCav90+0&*;Mr2h1kk~-lBTZTV1R44@D{Rt5y7UkdCXB-Qs}R+{2xqBN;Uuh literal 0 HcmV?d00001 diff --git a/__pycache__/motionFunctionsLib.cpython-313.pyc b/__pycache__/motionFunctionsLib.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..db763bfdfaf1e4035253c41323058aad0f8138e5 GIT binary patch literal 65165 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 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 \ No newline at end of file