using Aitex.Core.RT.Device;
using Aitex.Core.RT.Log;
using Aitex.Core.RT.Routine;
using Aitex.Core.RT.SCCore;
using Aitex.Core.Util;
using MECF.Framework.Common.Beckhoff.Station;
using MECF.Framework.Common.Equipment;
using MECF.Framework.Common.Layout;
using MECF.Framework.Common.Routine;
using MECF.Framework.Common.Utilities;
using MECF.Framework.Common.WaferHolder;
using CyberX8_Core;
using CyberX8_RT.Devices.AXIS;
using CyberX8_RT.Devices.AXIS.CANOpen;
using CyberX8_RT.Devices.Facilities;
using CyberX8_RT.Devices.Loader;
using CyberX8_RT.Devices.TransPorter;
using CyberX8_RT.Modules.Loader;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using CyberX8_RT.Devices.Rinse;
using CyberX8_RT.Devices.Metal;
using Aitex.Core.Common;
namespace CyberX8_RT.Modules.Transporter
{
public class TransporterPickUpFromRoutine : RoutineBase, IRoutine
{
private enum PickUpStep
{
CheckPreCondition,
TargetCellUnclamp,
TargetCellUnclampWait,
VerticalLow,
VerticalLowWait,
SafeMoveTo,
CheckMoveToStatus,
GantryPosition,
GantryPoisitiolWait,
UnlockTransporterClamp,
UnlockTransporterClampWait,
CellClampRetract,
//CellClampRetractWait,
LoopPickupRetry,
LoopRetryUnlock,
LoopRetryUnlockWait,
LoopRetryBackToLow,
LoopRetryBackToLowWait,
ElevatorPosition,
ElevatorPositionWait,
Delay,
ReadyToLockCheck,
LockTransporterClamp,
LockTransporterClampWait,
LoopPickupRetryEnd,
CalculateLiftupSpeed,
PickupDelay,
VerticalUp,
VerticalUpWait,
UpdateWaferHolder,
CheckWHPresentAndLock,
//ReadBarcodeConfirm,
End
}
#region 内部变量
private string _cellName;
private JetAxisBase _gantryAxis;
private JetAxisBase _elevatorAxis;
private LoaderEntity _loaderEntity;
private JetAxisBase _loaderRotationAxis;
private SystemFacilities _facilities;
private TransporterUnlockRoutine _unlockRoutine;
private TransporterLockRoutine _lockRoutine;
private TransporterConflictRoutine _conflictRoutine;
private TransporterCommon _transporterCommon;
private LoaderPreTransferUnclampRoutine _preTransferUnclampRoutine;
private LoaderCommonDevice _loaderCommonDevice;
ProcessLayoutCellItem _cellItem;
private int _pickupTime;
private int _pickupDelayTime;
private int _velocity;
private int _acceleration;
private bool _bypassWaferHolderPresent;
private int _pickMaxRetries = 2;
#endregion
///
/// 构造函数
///
///
public TransporterPickUpFromRoutine(string module) : base(module)
{
}
///
/// 中止
///
public void Abort()
{
Runner.Stop("Manual Abort");
}
///
/// 监控
///
///
public RState Monitor()
{
Runner.Run(PickUpStep.CheckPreCondition, CheckStartPreConfition, _delay_1ms)
//确认cell clamp Retracted
.Run(PickUpStep.TargetCellUnclamp, TargetCellUnclamp, _delay_1ms)
.WaitWithStopCondition(PickUpStep.TargetCellUnclampWait, TargetCellUnclampEndStatus, TargetCellUnclampStopStatus)
.Run(PickUpStep.VerticalLow, ElevatorGotoLow, _delay_1ms)
.WaitWithStopCondition(PickUpStep.VerticalLowWait, CheckVerticalPositionStatus, CheckVerticalPositionRunStop)
//1.2 Move to 安全
.Run(PickUpStep.SafeMoveTo, SafeMoveTo, _delay_1ms)
.WaitWithStopCondition(PickUpStep.CheckMoveToStatus, () => CommonFunction.CheckRoutineEndState(_conflictRoutine),
CheckSafeMoveToStopStatus)
//1.2 Gantry 移动
.Run(PickUpStep.GantryPosition, GantryPositionToCell, _delay_1ms)
.WaitWithStopCondition(PickUpStep.GantryPoisitiolWait, CheckGantryPositionStatus, CheckGantryPositionRunStop)
//1.3 Unlock Transporter clamp
.Run(PickUpStep.UnlockTransporterClamp, () => { return _unlockRoutine.Start(null) == RState.Running; }, NullFun, _delay_1ms)
.WaitWithStopCondition(PickUpStep.UnlockTransporterClampWait, () => CommonFunction.CheckRoutineEndState(_unlockRoutine),
CheckUnlockRoutineStopStatus)
//.WaitWithStopCondition(PickUpStep.CellClampRetractWait,CheckCellRetractRoutineEndState,CheckGantryPositionRunStop)
.LoopRetryStart(PickUpStep.LoopPickupRetry,"Pick up loop retry",_pickMaxRetries,NullFun,_delay_1ms)
.LoopRetrySecondRun(PickUpStep.LoopRetryUnlock, () => { return _unlockRoutine.Start(null) == RState.Running; }, NullFun, _delay_1ms)
.LoopRetrySecondRunWithStopStatus(PickUpStep.LoopRetryUnlockWait, () => CommonFunction.CheckRoutineEndState(_unlockRoutine),
CheckUnlockRoutineStopStatus)
.LoopRetrySecondRun(PickUpStep.LoopRetryBackToLow,ElevatorGotoLow,NullFun,_delay_1ms)
.LoopRetrySecondRunWithStopStatus(PickUpStep.LoopRetryBackToLowWait, CheckVerticalPositionStatus, CheckVerticalPositionRunStop)
//1.4 Elevator 移动至cell
.LoopRetryRun(PickUpStep.ElevatorPosition, () => { return VerticalPositionToCell(); }, NullFun, _delay_1ms)
.LoopRetryRunWithStopStatus(PickUpStep.ElevatorPositionWait, CheckVerticalPositionStatus, CheckVerticalPositionRunStop)
.LoopRetryDelay(PickUpStep.Delay,500)
//1.5 确认Ready to Lock Sensor
.LoopRetryWaitBack(PickUpStep.ReadyToLockCheck,CheckReadyToLock,_delay_1s)
//1.6 Unlock Transporter clamp
.LoopRetryRun(PickUpStep.LockTransporterClamp, () => { return _lockRoutine.Start(null) == RState.Running; }, NullFun, _delay_1ms)
.LoopRetryRunWithStopStatusBack(PickUpStep.LockTransporterClampWait, () => { return CommonFunction.CheckRoutineEndState(_lockRoutine); },
CheckLockRoutineStopStatus)
.LoopRetryEnd(PickUpStep.LoopPickupRetryEnd, _delay_1ms)
//1.7 确认Elevator lift speed
.Run(PickUpStep.CalculateLiftupSpeed,CalculateLiftupSpeed,_delay_1ms)
//1.8 Pickup delay
.Delay(PickUpStep.PickupDelay,_pickupDelayTime*1000)
//1.9 Elevator goto Up
.Run(PickUpStep.VerticalUp, ElevatorGotoUP, 100)
.WaitWithStopCondition(PickUpStep.VerticalUpWait, CheckVerticalPositionStatus, CheckVerticalPositionRunStop)
//2.0 Material Tracking Update
.Run(PickUpStep.UpdateWaferHolder, WaferHolderTransfer,_delay_1ms)
//2.0 确认WSPresent与Lock
.Wait(PickUpStep.CheckWHPresentAndLock,CheckWHPresentAndLock,_delay_2s)
//2.1 Read Barcode 确认与Material Tracking的转移是否一致
//.Run(PickUpStep.ReadBarcodeConfirm,ReadBarcode,_delay_1ms)
.End(PickUpStep.End,NullFun,100);
return Runner.Status;
}
///
/// 目标cell unclamp
///
///
private bool TargetCellUnclamp()
{
if (_cellName == "Loader")
{
bool result= _preTransferUnclampRoutine.Start() == RState.Running;
if(!result)
{
NotifyError(eEvent.ERR_TRANSPORTER, "Loader Unclamp failed",0);
}
return result;
}
else
{
_cellItem = ProcessLayoutManager.Instance.GetProcessLayoutCellItemByModuleName(_cellName);
if(_cellItem!=null)
{
bool result = false;
if(Enum.TryParse(_cellItem.ModuleName,out ModuleName cellModuleName))
{
if (ModuleHelper.IsRinse(cellModuleName))
{
RinseDevice rinseDevice = DEVICE.GetDevice(_cellItem.ModuleName);
if (rinseDevice != null)
{
result= rinseDevice.WaferHolderClampValveOff();
if(!result)
{
NotifyError(eEvent.ERR_TRANSPORTER, $"{_cellItem.ModuleName} Clamp off failed", 0);
}
return result;
}
}
else if (ModuleHelper.IsMetal(cellModuleName))
{
MetalCellDevice metalCellDevice = DEVICE.GetDevice(_cellItem.ModuleName);
if (metalCellDevice != null)
{
result= metalCellDevice.WaferHolderClampOff();
if (!result)
{
NotifyError(eEvent.ERR_TRANSPORTER, $"{_cellItem.ModuleName} Clamp off failed",0);
}
return result;
}
}
}
}
}
return true;
}
///
/// 目标Cell Unclamp状态
///
///
private bool TargetCellUnclampEndStatus()
{
if (_cellName == "Loader")
{
return CommonFunction.CheckRoutineEndState(_preTransferUnclampRoutine);
}
else
{
_cellItem = ProcessLayoutManager.Instance.GetProcessLayoutCellItemByModuleName(_cellName);
if (_cellItem != null)
{
if (Enum.TryParse(_cellItem.ModuleName, out ModuleName cellModuleName))
{
if (ModuleHelper.IsRinse(cellModuleName))
{
RinseDevice rinseDevice = DEVICE.GetDevice(_cellItem.ModuleName);
if (rinseDevice != null)
{
return !rinseDevice.RinseData.WaferHolderClamp;
}
}
else if (ModuleHelper.IsMetal(cellModuleName))
{
MetalCellDevice metalCellDevice = DEVICE.GetDevice(_cellItem.ModuleName);
if (metalCellDevice != null)
{
return metalCellDevice.ClampOff;
}
}
}
}
}
return true;
}
///
/// 目标Cell Unclamp运行中止状态
///
///
private bool TargetCellUnclampStopStatus()
{
if (_cellName == "Loader")
{
bool result= CommonFunction.CheckRoutineStopState(_preTransferUnclampRoutine);
if (result)
{
NotifyError(eEvent.ERR_TRANSPORTER, "Loader Unclamp failed", 0);
}
return result;
}
return false;
}
///
/// Elevator运动至LOW
///
///
private bool ElevatorGotoLow()
{
bool result = _elevatorAxis.PositionStation("LOW");
if(!result)
{
NotifyError(eEvent.ERR_TRANSPORTER, "Elevator goto LOW failed",0);
}
return result;
}
///
/// 安全避障移动
///
///
private bool SafeMoveTo()
{
_cellItem = ProcessLayoutManager.Instance.GetProcessLayoutCellItemByModuleName(_cellName);
string stationName = _cellName;
if (_cellItem != null)
{
if (_cellName.ToLower() != "loader" && _cellName.ToLower() != "park")
{
stationName = $"Cell{_cellItem.CellId}";
}
}
else
{
NotifyError(eEvent.ERR_TRANSPORTER, $"{_cellName} not in layout", 0);
return false;
}
var result = _gantryAxis.GetPositionByStation(stationName);
if(result.success)
{
bool isPositive = false;
if(_gantryAxis.MotionData.MotorPosition
/// 检验安全避让异常结束状态
///
///
private bool CheckSafeMoveToStopStatus()
{
bool result = CommonFunction.CheckRoutineStopState(_conflictRoutine);
if (result)
{
NotifyError(eEvent.ERR_TRANSPORTER, "Safe Move failed", 0);
}
return result;
}
///
/// Gantry Position To cell
///
///
private bool GantryPositionToCell()
{
_cellItem= ProcessLayoutManager.Instance.GetProcessLayoutCellItemByModuleName(_cellName);
bool result = false;
if (_cellItem != null)
{
if (_cellName.ToLower() != "loader"&&_cellName.ToLower()!="park")
{
result= _gantryAxis.PositionStation($"Cell{_cellItem.CellId}", false);
}
else
{
result= _gantryAxis.PositionStation(_cellName,false);
}
if (!result)
{
NotifyError(eEvent.ERR_TRANSPORTER, "gantry axis motion failed", 0);
}
return result;
}
else
{
NotifyError(eEvent.ERR_TRANSPORTER, $"{_cellName} not in layout", 0);
return false;
}
}
///
/// Elevator Position to cell
///
///
private bool VerticalPositionToCell()
{
bool result = false;
if( _cellItem != null )
{
if (_cellName.ToLower()!="loader")
{
result= _elevatorAxis.PositionStation($"Cell{_cellItem.CellId}", false);
}
else
{
result= _elevatorAxis.PositionStation(_cellName, false);
}
if (!result)
{
NotifyError(eEvent.ERR_TRANSPORTER, "elevator axis motion failed", 0);
}
return result;
}
else
{
NotifyError(eEvent.ERR_TRANSPORTER, $"{_cellName} not in layout",0);
return false;
}
}
///
/// 检验Vertical移动状态
///
///
private bool CheckVerticalPositionStatus()
{
return _elevatorAxis.Status == RState.End;
}
///
/// 检验Vertical是否还在运动
///
///
private bool CheckVerticalPositionRunStop()
{
bool result = _elevatorAxis.Status == RState.Failed || _elevatorAxis.Status == RState.Timeout ;
if (result)
{
NotifyError(eEvent.ERR_TRANSPORTER, "elevator axis motion failed",0);
}
return result;
}
///
/// 检验Gantry移动状态
///
///
private bool CheckGantryPositionStatus()
{
return _gantryAxis.Status == RState.End;
}
///
/// 检验Gantry是否还在运动
///
///
private bool CheckGantryPositionRunStop()
{
bool result= _gantryAxis.Status == RState.Failed||_gantryAxis.Status==RState.Timeout;
if(result)
{
NotifyError(eEvent.ERR_TRANSPORTER, "Gantry Motion failed", 0);
}
return result;
}
///
/// 检验Unlock异常状态
///
///
private bool CheckUnlockRoutineStopStatus()
{
bool result = CommonFunction.CheckRoutineStopState(_unlockRoutine);
if (result)
{
NotifyError(eEvent.ERR_TRANSPORTER, "unlock failed", 0);
}
return result;
}
///
/// 检验Ready to lock
///
///
private bool CheckReadyToLock()
{
if(! _transporterCommon.TransporterData.ReadyToLock1)
{
NotifyError(eEvent.WARN_TRANSPORTER, $"{Module} Ready to lock1 is off",0);
return false;
}
if (!_transporterCommon.TransporterData.ReadyToLock2)
{
NotifyError(eEvent.WARN_TRANSPORTER, $"{Module} Ready to lock2 is off",0);
return false;
}
return true;
}
///
/// 检验Lock Routine异常状态
///
///
private bool CheckLockRoutineStopStatus()
{
bool result = CommonFunction.CheckRoutineStopState(_lockRoutine);
if(result)
{
NotifyError(eEvent.WARN_TRANSPORTER, "lock failed", 0);
}
return result;
}
///
/// 计算提升速度
///
///
private bool CalculateLiftupSpeed()
{
if(_pickupTime==0)
{
return true;
}
BeckhoffStationAxis stationAxis = BeckhoffStationLocationManager.Instance.GetStationAxis(Module, "Elevator", 0);
Station upStation = stationAxis.Stations.Find(O => O.Name.EndsWith("UP"));
Station cellStation = null;
if (_cellName == "Loader")
{
cellStation = stationAxis.Stations.Find(O => O.Name.EndsWith("Loader"));
}
else
{
cellStation = stationAxis.Stations.Find(O => O.Name.EndsWith($"Cell{_cellItem.CellId}"));
}
if(upStation==null)
{
NotifyError(eEvent.ERR_TRANSPORTER, "UP is not in Station List",0);
return false;
}
if(cellStation==null)
{
NotifyError(eEvent.ERR_TRANSPORTER, $"Cell{_cellItem.CellId} is not in Station List",0);
return false;
}
if(!double.TryParse(cellStation.Position,out double cellStationPosition))
{
NotifyError(eEvent.ERR_TRANSPORTER, $"Cell{_cellItem.CellId} Station Position is invalid",0);
return false;
}
if (!double.TryParse(upStation.Position, out double upStationPosition))
{
NotifyError(eEvent.ERR_TRANSPORTER, $"Cell{_cellItem.CellId} Station Position is invalid", 0);
return false;
}
int distance =(int)Math.Round(Math.Abs(cellStationPosition - upStationPosition));
double tmpVelocity = (double) distance/ _pickupTime;
_velocity = _elevatorAxis.CalculateMultiplySpeedRatio(_elevatorAxis.CalculateValueMultiplyScale(tmpVelocity));
double tmpAccelaration = (double)(2 * distance) / Math.Pow(_pickupTime, 2);
_acceleration = _elevatorAxis.CalculateMultiplyAccelerationRatio(_elevatorAxis.CalculateValueMultiplyScale(tmpAccelaration));
LOG.WriteBackgroundLog(eEvent.INFO_TRANSPORTER, Module, "adjust profile speed");
return true;
}
///
/// Elevator运动至UP
///
///
private bool ElevatorGotoUP()
{
bool result= _elevatorAxis.PositionStation("UP", false, _velocity, _acceleration, _acceleration);
if(!result)
{
NotifyError(eEvent.ERR_TRANSPORTER, "elevator goto up failed",0);
}
return result;
}
///
/// 更新WaferHolder移动信息
///
///
private bool WaferHolderTransfer()
{
bool isMetal = false;
DateTime lastMetalRecipeTime = DateTime.Now;
if(Enum.TryParse(_cellName,out ModuleName moduleName)&&ModuleHelper.IsMetal(moduleName))
{
isMetal = true;
WaferHolderInfo info = WaferHolderManager.Instance.GetWaferHolder(_cellName);
if (info != null)
{
lastMetalRecipeTime = info.LastMetalRecipeCompleteTime;
}
}
bool result= WaferHolderManager.Instance.TransferWaferHolder(Module,_cellName, Module);
if (!result)
{
NotifyError(eEvent.ERR_TRANSPORTER, "tranfer waferHolder message failed", 0);
}
else
{
if (!Singleton.Instance.IsAutoRunning)
{
return true;
}
WaferHolderInfo waferHolderInfo = WaferHolderManager.Instance.GetWaferHolder(Module);
if (waferHolderInfo == null)
{
return true;
}
string strTime = DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss");
waferHolderInfo.SchedulerModules.Add($"{strTime} {_cellName} => {Module}");
if (isMetal)
{
int waferShuttleSoakMaxTime = SC.GetValue("Metal.WaferShuttleSoakMaxTime");
double soakTime = Math.Round(DateTime.Now.Subtract(lastMetalRecipeTime).TotalMinutes,2);
if (soakTime > waferShuttleSoakMaxTime)
{
LOG.WriteLog(eEvent.WARN_METAL, _cellName, $"time of {waferHolderInfo.Id} soak in metal is {soakTime} minute over {waferShuttleSoakMaxTime}");
}
else
{
LOG.WriteLog(eEvent.INFO_METAL, _cellName, $"time of {waferHolderInfo.Id} soak in metal is {soakTime} minute");
}
}
}
return result;
}
///
/// 检验WHPresent与Lock状态
///
///
private bool CheckWHPresentAndLock()
{
bool locked = _transporterCommon.TransporterData.Locked1 && _transporterCommon.TransporterData.Locked2;
bool whPresent= _transporterCommon.TransporterData.WhPresent1 && _transporterCommon.TransporterData.WhPresent2;
bool result= locked && (whPresent||_bypassWaferHolderPresent);
if (!result)
{
NotifyError(eEvent.ERR_TRANSPORTER, "check wafer Shuttle present and locked failed", 0);
}
return result;
}
///
/// 读取条码
///
///
private bool ReadBarcode()
{
string str=_transporterCommon.ReaderBarcode();
str = str.Trim();
if(!string.IsNullOrEmpty(str))
{
TransporterEntity transporterEntity = Singleton.Instance.GetModule(Module.ToString());
if (transporterEntity == null)
{
return true;
}
WaferHolderInfo waferHolderInfo = transporterEntity.WaferHolderInfo;
if (waferHolderInfo == null)
{
return true;
}
if (waferHolderInfo.Id == str)
{
return true;
}
WaferHolderManager.Instance.SwitchWaferHolderId(waferHolderInfo,Module,str);
return true;
}
return true;
}
///
/// 启动
///
///
///
public RState Start(params object[] objs)
{
_cellName = objs[0].ToString();
_elevatorAxis = DEVICE.GetDevice($"{Module}.Elevator");
_gantryAxis = DEVICE.GetDevice($"{Module}.Gantry");
_loaderEntity = Singleton.Instance.GetModule(ModuleName.Loader1.ToString());
_loaderRotationAxis = DEVICE.GetDevice($"{ModuleName.Loader1}.Rotation");
_facilities = DEVICE.GetDevice("System.Facilities");
_unlockRoutine = new TransporterUnlockRoutine(Module);
_lockRoutine = new TransporterLockRoutine(Module);
_conflictRoutine = new TransporterConflictRoutine(Module);
_transporterCommon = DEVICE.GetDevice($"{Module}.Common");
_loaderCommonDevice = DEVICE.GetDevice($"{ModuleName.Loader1}.Common");
_bypassWaferHolderPresent = SC.GetValue("Transporter.BypassWaferHolderPresent");
_preTransferUnclampRoutine = new LoaderPreTransferUnclampRoutine(ModuleName.Loader1.ToString());
_velocity = 0;
_acceleration = 0;
_pickMaxRetries = SC.GetValue("Transporter.MaxPickTries");
string preConfig = SC.GetConfigPreContent(_cellName);
if (SC.ContainsItem($"{preConfig}.PickTimeSeconds"))
{
_pickupTime = SC.GetValue($"{preConfig}.PickTimeSeconds");
}
if (SC.ContainsItem($"{preConfig}.PickDelaySeconds"))
{
_pickupDelayTime = SC.GetValue($"{preConfig}.PickDelaySeconds");
}
return Runner.Start(Module, $"PickUpFrom {_cellName}");
}
///
/// 启动校验条件
///
///
private bool CheckStartPreConfition()
{
bool result = CheckPreCondition();
if(!result)
{
return false;
}
double elevatorPosition = _elevatorAxis.MotionData.MotorPosition;
if (!_elevatorAxis.CheckPositionIsInStation(elevatorPosition, "Up") &&
!_elevatorAxis.CheckPositionIsInStation(elevatorPosition, "Low"))
{
NotifyError(eEvent.ERR_TRANSPORTER, $"{Module} elevator axis {elevatorPosition} is not in Up or Low station",-1);
return false;
}
if (!_loaderEntity.IsHomed)
{
NotifyError(eEvent.ERR_TRANSPORTER, $"{ModuleName.Loader1} is not homed",-1);
return false;
}
if (_cellName == "Loader")
{
double loaderRotationPosition = _loaderRotationAxis.MotionData.MotorPosition;
if (!_loaderRotationAxis.CheckPositionIsInStation(loaderRotationPosition, "TRNPA") &&
!_loaderRotationAxis.CheckPositionIsInStation(loaderRotationPosition, "TRNPB"))
{
NotifyError(eEvent.ERR_TRANSPORTER, $"{ModuleName.Loader1} rotation axis {loaderRotationPosition} is not int TRNPA or TRNPB station",-1);
return false;
}
}
if (!WaferHolderManager.Instance.HasWaferHolder(_cellName))
{
NotifyError(eEvent.ERR_TRANSPORTER, $"{_cellName} does not has wafer Shuttle", -1);
return false;
}
//检验Facilities
var faciltiesResult = _facilities.CheckCDA();
if (!faciltiesResult.result)
{
NotifyError(eEvent.ERR_TRANSPORTER, faciltiesResult.reason,-1);
return false;
}
return true;
}
///
/// 检验前置条件
///
///
private bool CheckPreCondition()
{
if (!_gantryAxis.IsSwitchOn)
{
NotifyError(eEvent.ERR_TRANSPORTER, $"{Module} gantry axis is not switch on ", -1);
return false;
}
if (!_gantryAxis.IsHomed)
{
NotifyError(eEvent.ERR_TRANSPORTER, $"{Module} gantry axis is not homed ",-1);
return false;
}
if (!_elevatorAxis.IsSwitchOn)
{
NotifyError(eEvent.ERR_TRANSPORTER, $"{Module} elevator axis is not switch on ", -1);
return false;
}
if (!_elevatorAxis.IsHomed)
{
NotifyError(eEvent.ERR_TRANSPORTER, $"{Module} elevator axis is not homed ",-1);
return false;
}
return true;
}
}
}