Browse Source

add rotation bi-rotaiton

chenzk 1 week ago
parent
commit
c79a314283

+ 8 - 0
Framework/Common/Routine/RoutineRunner.cs

@@ -720,6 +720,14 @@ namespace MECF.Framework.Common.Routine
         {
             return LoopRun(id, () => { return true; }, delayMS);
         }
+        public RoutineRunner LoopDelayIf(Enum id, bool bRun, int delayMS)
+        {
+            if (bRun)
+            {
+                return LoopRun(id, () => { return true; }, delayMS);
+            }
+            return this;
+        }
         public RoutineRunner LoopRunDelay(Enum id, Func<bool> action, int delayMS)
         {
             return LoopRun(id, action, delayMS);

+ 85 - 31
PunkHPX8_RT/Config/Station/StationPositionsCfg.xml

@@ -1,35 +1,35 @@
 <?xml version="1.0" encoding="utf-8"?>
 <StationPositionCfg xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
   <Module Name="SRD1">
-    <Axis Name="SRD1.Arm">
-      <ToleranceDefault>0.5</ToleranceDefault>
-      <Stations>
-        <Station Name="SRD1.Arm.Home" Position="0" />
-        <Station Name="SRD1.Arm.Center" Position="-43" ModifiedDate="2024-09-04 17:16:30.547" />
-      </Stations>
-    </Axis>
-    <Axis Name="SRD1.Rotation">
-      <ToleranceDefault>0.5</ToleranceDefault>
-      <Stations>
-        <Station Name="SRD1.Rotation.Home" Position="0" />
-      </Stations>
-    </Axis>
-  </Module>
-  <Module Name="SRD2">
-    <Axis Name="SRD2.Arm">
-      <ToleranceDefault>0.5</ToleranceDefault>
-      <Stations>
-        <Station Name="SRD2.Arm.Home" Position="0" />
-        <Station Name="SRD2.Arm.Center" Position="-49.438" />
-      </Stations>
-    </Axis>
-    <Axis Name="SRD2.Rotation">
-      <ToleranceDefault>0.5</ToleranceDefault>
-      <Stations>
-        <Station Name="SRD2.Rotation.Home" Position="0" />
-      </Stations>
-    </Axis>
-  </Module>
+		<Axis Name="SRD1.Arm">
+			<ToleranceDefault>0.5</ToleranceDefault>
+			<Stations>
+				<Station Name="SRD1.Arm.Home" Position="0" />
+				<Station Name="SRD1.Arm.Center" Position="-43" ModifiedDate="2024-09-04 17:16:30.547" />
+			</Stations>
+		</Axis>
+		<Axis Name="SRD1.Rotation">
+			<ToleranceDefault>0.5</ToleranceDefault>
+			<Stations>
+				<Station Name="SRD1.Rotation.Home" Position="0" />
+			</Stations>
+		</Axis>
+	</Module>
+	<Module Name="SRD2">
+		<Axis Name="SRD2.Arm">
+			<ToleranceDefault>0.5</ToleranceDefault>
+			<Stations>
+				<Station Name="SRD2.Arm.Home" Position="0" />
+				<Station Name="SRD2.Arm.Center" Position="-49.438" />
+			</Stations>
+		</Axis>
+		<Axis Name="SRD2.Rotation">
+			<ToleranceDefault>0.5</ToleranceDefault>
+			<Stations>
+				<Station Name="SRD2.Rotation.Home" Position="0" />
+			</Stations>
+		</Axis>
+	</Module>
 	<Module Name="VPW1">
 		<Axis Name="VPW1.Rotation">
 			<ToleranceDefault>0.5</ToleranceDefault>
@@ -49,10 +49,64 @@
 		</Axis>
 	</Module>
 	<Module Name="PlatingCell1">
-		<Axis Name="PlatingCell1.Vertical">
+		<Axis Name="PlatingCell1_2.Vertical">
+			<ToleranceDefault>0.5</ToleranceDefault>
+			<Stations>
+				<Station Name="PlatingCell1_2.Vertical.Home" Position="0" />
+				<Station Name="PlatingCell1_2.Vertical.Maintenance" Position="1" />
+				<Station Name="PlatingCell1_2.Vertical.Load" Position="2" />
+				<Station Name="PlatingCell1_2.Vertical.Load2" Position="3" />
+				<Station Name="PlatingCell1_2.Vertical.Rinse" Position="4" />
+				<Station Name="PlatingCell1_2.Vertical.CCR" Position="5" />
+				<Station Name="PlatingCell1_2.Vertical.Reclaim" Position="6" />
+				<Station Name="PlatingCell1_2.Vertical.Entry" Position="7" />
+				<Station Name="PlatingCell1_2.Vertical.Plate" Position="8" />
+				<Station Name="PlatingCell1_2.Vertical.ACE" Position="9" />
+			</Stations>
+		</Axis>
+		<Axis Name="PlatingCell1.Rotation">
+			<ToleranceDefault>0.5</ToleranceDefault>
+			<Stations>
+				<Station Name="PlatingCell1.Rotation.Home" Position="0" />
+			</Stations>
+		</Axis>
+	</Module>
+	<Module Name="PlatingCell2">
+		<Axis Name="PlatingCell2.Rotation">
+			<ToleranceDefault>0.5</ToleranceDefault>
+			<Stations>
+				<Station Name="PlatingCell2.Rotation.Home" Position="0" />
+			</Stations>
+		</Axis>
+	</Module>
+		<Module Name="PlatingCell3">
+		<Axis Name="PlatingCell3_4.Vertical">
+			<ToleranceDefault>0.5</ToleranceDefault>
+			<Stations>
+				<Station Name="PlatingCell3_4.Vertical.Home" Position="0" />
+				<Station Name="PlatingCell3_4.Vertical.Maintenance" Position="1" />
+				<Station Name="PlatingCell3_4.Vertical.Load" Position="2" />
+				<Station Name="PlatingCell3_4.Vertical.Load2" Position="3" />
+				<Station Name="PlatingCell3_4.Vertical.Rinse" Position="4" />
+				<Station Name="PlatingCell3_4.Vertical.CCR" Position="5" />
+				<Station Name="PlatingCell3_4.Vertical.Reclaim" Position="6" />
+				<Station Name="PlatingCell3_4.Vertical.Entry" Position="7" />
+				<Station Name="PlatingCell3_4.Vertical.Plate" Position="8" />
+				<Station Name="PlatingCell3_4.Vertical.ACE" Position="9" />
+			</Stations>
+		</Axis>
+		<Axis Name="PlatingCell3.Rotation">
+			<ToleranceDefault>0.5</ToleranceDefault>
+			<Stations>
+				<Station Name="PlatingCell3.Rotation.Home" Position="0" />
+			</Stations>
+		</Axis>
+	</Module>
+	<Module Name="PlatingCell4">
+		<Axis Name="PlatingCell4.Rotation">
 			<ToleranceDefault>0.5</ToleranceDefault>
 			<Stations>
-				<Station Name="VPW2.Vertical.Home" Position="0" />
+				<Station Name="PlatingCell4.Rotation.Home" Position="0" />
 			</Stations>
 		</Axis>
 	</Module>

+ 30 - 2
PunkHPX8_RT/Devices/PlatingCell/PlatingCellDevice.cs

@@ -44,6 +44,7 @@ namespace PunkHPX8_RT.Devices.PlatingCell
             None,
             ClamShellClose,
             ClamShellOpen,
+            BiRotation,
         }
 
 
@@ -78,6 +79,10 @@ namespace PunkHPX8_RT.Devices.PlatingCell
         /// clamshell routine操作
         /// </summary>
         private ClamShellCloseRoutine _closeRoutine;
+        /// <summary>
+        /// 电机双向旋转操作
+        /// </summary>
+        private RotationBiDirectionRoutine _biDirectionRoutine;
         /// 变量是否初始化字典
         /// </summary>
         private Dictionary<string, bool> _variableInitializeDic = new Dictionary<string, bool>();
@@ -152,7 +157,7 @@ namespace PunkHPX8_RT.Devices.PlatingCell
         /// </summary>
         public bool IsManual { get { return _persistentValue != null ? _persistentValue.OperatingMode == MANUAL : false; } }
         /// <summary>
-        /// A面PowerSupplier
+        /// PowerSupplier
         /// </summary>
         public CellPowerSupplier PowerSupplier { get { return _powerSupplier; } }
         #endregion
@@ -205,6 +210,7 @@ namespace PunkHPX8_RT.Devices.PlatingCell
         private void InitializeRoutine()
         {
             _closeRoutine = new ClamShellCloseRoutine(Module);
+            _biDirectionRoutine = new RotationBiDirectionRoutine(Module);
         }
         /// <summary>
         /// 定时器执行
@@ -260,6 +266,8 @@ namespace PunkHPX8_RT.Devices.PlatingCell
                 case PlatingCellCommonOperation.ClamShellOpen:
                 case PlatingCellCommonOperation.ClamShellClose:
                     return _closeRoutine;
+                case PlatingCellCommonOperation.BiRotation:
+                    return _biDirectionRoutine;
                 default:
                     return null;
             }
@@ -450,6 +458,9 @@ namespace PunkHPX8_RT.Devices.PlatingCell
             OP.Subscribe($"{Module}.StartRotation", StartRotationAction);
             OP.Subscribe($"{Module}.StopRotation", StopRotationAction);
 
+            OP.Subscribe($"{Module}.StartBiRotation", StartBiRotationAction);
+
+
 
         }
         #region Operation
@@ -457,7 +468,7 @@ namespace PunkHPX8_RT.Devices.PlatingCell
         {
             if (args.Length < 2 && (int)args[0] < 0 && (int)args[1] < 0)
             {
-                LOG.WriteLog(eEvent.ERR_VPW, Module, $"Start rotation paramater is wrong");
+                LOG.WriteLog(eEvent.ERR_PLATINGCELL, Module, $"Start rotation paramater is wrong");
                 return false;
             }
             double targetPostion = (int)args[0] * 6 * (int)args[1];
@@ -471,6 +482,23 @@ namespace PunkHPX8_RT.Devices.PlatingCell
 
         }
 
+        private bool StartBiRotationAction(string cmd, object[] args)
+        {
+            int _rotationTime = (int)args[0];
+            int _frequency = (int)args[1];
+            int _speed = (int)args[2];
+            if (!JudgeRunningState(PlatingCellCommonOperation.BiRotation))
+            {
+                _currentOperation = PlatingCellCommonOperation.BiRotation;
+                _status = _biDirectionRoutine.Start(_rotationTime, _frequency, _speed);
+                return _status == RState.Running;
+            }
+            else
+            {
+                return false;
+            }
+        }
+
         private bool StopRotationAction(string cmd, object[] args)
         {
             return _rotationAxis.StopPositionOperation();

+ 33 - 5
PunkHPX8_RT/Modules/PlatingCell/PlatingCellRunRecipeRoutine.cs

@@ -35,7 +35,7 @@ namespace PunkHPX8_RT.Modules.PlatingCell
             RotationStartEntry,
             RotationChangeToEntrySpeed,
             AngleTilt,
-            VerticalGotoEntry,
+            VerticalGotoPlate,
             WaitEntryCurrentProtectedFromRinse,
             WaitEntryCurrentProtectedFromHome,
             EntryCurrentProtected,
@@ -97,7 +97,7 @@ namespace PunkHPX8_RT.Modules.PlatingCell
         /// <summary>
         /// recipe中是否存在电机反转
         /// </summary>
-        private bool _isRecipeContainsRevserseRotation;
+        private bool _isRecipeContainsRevserseRotation = false;
         /// <summary>
         /// run recipe 过程中电机会停的位置(需要根据recipe计算出来)
         /// </summary>
@@ -135,9 +135,10 @@ namespace PunkHPX8_RT.Modules.PlatingCell
                 .RunIf(RunRecipeStep.RotationChangeToEntrySpeed, _recipe.RinseBeforeEntryEnable, () => { return ChangeSpeed(_recipe.EntrySpinSpeed); }, _delay_1ms)  //有intercal rinse 直接变速
                  //Angle tilt 操作
                 .Run(RunRecipeStep.AngleTilt, _device.HeadtTiltAction, () => { return _device.PlatingCellDeviceData.HeadTilt; }, _delay_1s)
-                .Run(RunRecipeStep.VerticalGotoEntry, () => { return StartVertical("Entry", _recipe.EntryZoffset); }, _delay_1ms)
-                .DelayIf(RunRecipeStep.WaitEntryCurrentProtectedFromRinse, !_recipe.RinseBeforeEntryEnable,CalculateVerticaMoveTime("Rinse","Entry") - 100 > 0 ? CalculateVerticaMoveTime("Rinse", "Entry"):0)
-                .DelayIf(RunRecipeStep.WaitEntryCurrentProtectedFromHome, _recipe.RinseBeforeEntryEnable,CalculateVerticaMoveTime("Home","Entry") - 100 > 0 ? CalculateVerticaMoveTime("Home", "Entry") : 0)
+                .Run(RunRecipeStep.VerticalGotoPlate, () => { return StartVertical("Plate", _recipe.EntryZoffset); }, _delay_1ms)
+                .DelayIf(RunRecipeStep.WaitEntryCurrentProtectedFromRinse, !_recipe.RinseBeforeEntryEnable,CalculateVerticaMoveTime("Rinse","Entry") - 110 > 0 ? CalculateVerticaMoveTime("Rinse", "Entry"):0) //提前110ms,多10ms
+                .DelayIf(RunRecipeStep.WaitEntryCurrentProtectedFromHome, _recipe.RinseBeforeEntryEnable,CalculateVerticaMoveTime("Home","Entry") - 110 > 0 ? CalculateVerticaMoveTime("Home", "Entry") : 0)
+
                 .End(RunRecipeStep.End, NullFun);
             return Runner.Status;
         }
@@ -277,12 +278,39 @@ namespace PunkHPX8_RT.Modules.PlatingCell
             {
                 return RState.Failed;
             }
+            CalculatVerticalPosition(_recipe);
             _currentCycle = 0;
 
  
             return Runner.Start(Module, "Run Recipe");
         }
         /// <summary>
+        /// 根据dep recipe计算出整个电镀过程中Rotation profile position可能去到的位置
+        /// </summary>
+        private void CalculatVerticalPosition(DepRecipe recipe)
+        {
+            foreach(var item in recipe.DepSteps)
+            {
+                if (item.BiDireaction)
+                {
+                    _isRecipeContainsRevserseRotation = true;
+                }
+                else
+                {
+                    continue;
+                }
+            }
+            //不存在双向旋转直接给roation一个很大的值,电镀完成后再停止
+            if (!_isRecipeContainsRevserseRotation)
+            {
+                _targetPositionList.Add(12 * 60 * 60 * 500 * 6); //以500rmp 运行12个小时的位置
+            }
+            else
+            {
+
+            }
+        }
+        /// <summary>
         /// 检验前置条件
         /// </summary>
         /// <returns></returns>

+ 210 - 0
PunkHPX8_RT/Modules/PlatingCell/RotationBiDirectionRoutine.cs

@@ -0,0 +1,210 @@
+using Aitex.Core.RT.Device;
+using Aitex.Core.RT.Log;
+using Aitex.Core.RT.Routine;
+using Aitex.Core.Util;
+using MECF.Framework.Common.Equipment;
+using MECF.Framework.Common.Routine;
+using MECF.Framework.Common.SubstrateTrackings;
+using PunkHPX8_Core;
+using PunkHPX8_RT.Devices.AXIS;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace PunkHPX8_RT.Modules.PlatingCell
+{
+    public class RotationBiDirectionRoutine : RoutineBase, IRoutine
+    {
+        private enum BiDirectionStep
+        {
+            StartBiDirection,
+            LoopStart,
+            LoopStartRotation,
+            LoopRotationWait,
+            LoopStopRotation,
+            LoopCheckStopRotation,
+            LoopUpdateRemain1,
+            LoopReverseRotationStart,
+            LoopReverseRotationWait,
+            LooPStopRevserseRotation,
+            LoopCheckStopReverseRotation,
+            LoopUpdateRemain2,
+            LoopEnd,
+            StartRemainRotation,
+            RemainRotationDelay,
+            StopRemainRotation,
+            CheckStopRemainRotation,
+            End
+        }
+        #region 常量
+        private const int TARGETPOSITION = 10 * 60 * 60 * 100 * 6; //转速100rmp转10个小时的目的地
+        private const int SPEED_RATIO = 1;
+        #endregion
+
+        #region 内部变量
+        /// <summary>
+        /// rotation axis
+        /// </summary>
+        private JetAxisBase _rotationAxis;
+        /// <summary>
+        /// 运行过程总时间
+        /// </summary>
+        private int _rotationTime;
+        /// <summary>
+        /// 转向频率
+        /// </summary>
+        private int _frequency;
+        /// <summary>
+        /// 速度,单位rpm
+        /// </summary>
+        private int _speed;
+        /// <summary>
+        /// 单步电镀过程中电机需要转向的次数
+        /// </summary>
+        private int _reverseTimes;
+        /// <summary>
+        /// 反转次数整除以外的时间
+        /// </summary>
+        private int _extralTime;
+        /// <summary>
+        /// 剩余反转次数
+        /// </summary>
+        private int _remainTimes;
+        /// <summary>
+        /// monitor部分的循环次数
+        /// </summary>
+        private int _cycleCount;
+        #endregion
+
+        /// <summary>
+        /// 构造函数
+        /// </summary>
+        /// <param name="module"></param>
+        public RotationBiDirectionRoutine(string module) : base(module)
+        {
+
+        }
+
+        /// <summary>
+        /// 中止
+        /// </summary>
+        public void Abort()
+        {
+            Runner.Stop("Manual Abort");
+        }
+        /// <summary>
+        /// 监控
+        /// </summary>
+        /// <returns></returns>
+        public RState Monitor()
+        {
+            Runner.Run(BiDirectionStep.StartBiDirection, NullFun, _delay_1ms)
+                 .LoopStart(BiDirectionStep.LoopStart, "Rotation Bi-Direction Step Start", _cycleCount, NullFun, _delay_1ms)
+                 .LoopRunIf(BiDirectionStep.LoopStartRotation, _cycleCount > 0,() => { return StratRotation(true); }, _delay_1ms)
+                 .LoopDelayIf(BiDirectionStep.LoopRotationWait, _cycleCount > 0, _frequency * 1000)
+                 .LoopRunIf(BiDirectionStep.LoopStopRotation, _cycleCount > 0, _rotationAxis.StopPositionOperation,_delay_1ms)
+                 .LoopRunIfWithStopStatus(BiDirectionStep.LoopCheckStopRotation, _cycleCount > 0, CheckRotationPositionStatus, CheckRotationPositionRunStop)
+                 .LoopRunIf(BiDirectionStep.LoopUpdateRemain1, _cycleCount > 0, UpdateRemain,  _delay_1ms)
+                 .LoopRunIf(BiDirectionStep.LoopReverseRotationStart, _remainTimes != 0, () => { return StratRotation(false); }, _delay_1ms)
+                 .LoopDelayIf(BiDirectionStep.LoopReverseRotationWait, _remainTimes != 0, _frequency * 1000)
+                 .LoopRunIf(BiDirectionStep.LooPStopRevserseRotation, _remainTimes != 0, _rotationAxis.StopPositionOperation, _delay_1ms)
+                 .LoopRunIfWithStopStatus(BiDirectionStep.LoopCheckStopReverseRotation, _remainTimes != 0, CheckRotationPositionStatusWithStateupdate, CheckRotationPositionRunStop)
+                 //.LoopRunIf(BiDirectionStep.LoopUpdateRemain2, _remainTimes != 0,UpdateRemain, _delay_1ms)
+                 .LoopEnd(BiDirectionStep.LoopEnd, NullFun, _delay_1ms)
+                 //剩余旋转时间(如果翻转次数是偶数,则正转,否则反转)
+                 .RunIf(BiDirectionStep.StartRemainRotation, _extralTime !=0, () => { return _reverseTimes % 2 == 0 ? StratRotation(true): StratRotation(false); }, _delay_1ms) 
+                 .DelayIf(BiDirectionStep.RemainRotationDelay, _extralTime != 0,  _extralTime * 1000)
+                 .RunIf(BiDirectionStep.StopRemainRotation, _extralTime != 0, _rotationAxis.StopPositionOperation, _delay_1ms)
+                 .WaitWithStopConditionIf(BiDirectionStep.CheckStopRemainRotation, _extralTime != 0, CheckRotationPositionStatus, CheckRotationPositionRunStop)
+                 .End(BiDirectionStep.End, NullFun, _delay_1ms);
+            return Runner.Status;
+        }
+        private bool UpdateRemain()
+        {
+            _remainTimes--;
+            return true;
+        }
+        /// <summary>
+        /// rotation开始旋转
+        /// </summary>
+        /// <returns></returns>
+        private bool StratRotation(bool isforwardRotate)
+        {
+            bool result = false;
+            if (isforwardRotate) 
+            {
+                result = _rotationAxis.ProfilePosition(TARGETPOSITION, _speed * SPEED_RATIO * 6, 0, 0);
+            }
+            else
+            {
+                result = _rotationAxis.ProfilePosition(-TARGETPOSITION, _speed * SPEED_RATIO * 6, 0, 0);
+            }
+            if (!result)
+            {
+                NotifyError(eEvent.ERR_PLATINGCELL, "Start Rotation is failed", 0);
+                return false;
+            }
+            return true;
+        }
+        /// <summary>
+        /// 检验Rotation移动状态
+        /// </summary>
+        /// <returns></returns>
+        private bool CheckRotationPositionStatus()
+        {
+            return _rotationAxis.Status == RState.End;
+        }
+        /// <summary>
+        /// 检验Rotation移动状态(带状态更新)
+        /// </summary>
+        /// <returns></returns>
+        private bool CheckRotationPositionStatusWithStateupdate()
+        {
+            bool result = _rotationAxis.Status == RState.End;
+            if (result)
+            {
+                UpdateRemain();
+            }
+            return result;
+        }
+        /// <summary>
+        /// 检验Rotation是否还在运动
+        /// </summary>
+        /// <returns></returns>
+        private bool CheckRotationPositionRunStop()
+        {
+            return _rotationAxis.Status == RState.Failed || _rotationAxis.Status == RState.Timeout;
+        }
+        /// <summary>
+        /// 启动
+        /// </summary>
+        /// <param name="objs"></param>
+        /// <returns></returns>
+        public RState Start(params object[] objs)
+        {
+            _rotationTime = (int)objs[0];
+            _frequency = (int)objs[1];
+            _speed = (int)objs[2];
+            if(_frequency > _rotationTime)
+            {
+                NotifyError(eEvent.ERR_PLATINGCELL, "frequency can not large than rotation time", 0);
+                return RState.Failed;
+            }
+            _reverseTimes = _rotationTime / _frequency;
+            if(_reverseTimes % 2 == 0)
+            {
+                _cycleCount = _reverseTimes / 2;
+            }
+            else
+            {
+                _cycleCount = _reverseTimes / 2 + 1;
+            }
+            _remainTimes = _reverseTimes;
+            _extralTime = _rotationTime % _frequency;
+            _rotationAxis = DEVICE.GetDevice<JetAxisBase>($"{Module}.Rotation");
+            return Runner.Start(Module, "Start  Bi-Direction rotation");
+        }
+    }
+}

+ 1 - 0
PunkHPX8_RT/PunkHPX8_RT.csproj

@@ -307,6 +307,7 @@
     <Compile Include="Modules\PlatingCell\PlatingCellInitializeRoutine.cs" />
     <Compile Include="Modules\PlatingCell\PlatingCellRunRecipeRoutine.cs" />
     <Compile Include="Modules\PlatingCell\PlatingCellVerticalEntity.cs" />
+    <Compile Include="Modules\PlatingCell\RotationBiDirectionRoutine.cs" />
     <Compile Include="Modules\ReservoirCellHomeRoutine.cs" />
     <Compile Include="Modules\Reservoir\DIReservoirInitializeRoutine.cs" />
     <Compile Include="Modules\Reservoir\DMInitializeRoutine.cs" />

+ 104 - 96
PunkHPX8_Themes/UserControls/PlatingCellStatusControl.xaml

@@ -7,8 +7,7 @@
              xmlns:UserControls="clr-namespace:PunkHPX8_Themes.UserControls"
              xmlns:Control="clr-namespace:MECF.Framework.UI.Core.Control;assembly=MECF.Framework.UI.Core"
              xmlns:local="clr-namespace:PunkHPX8_Themes.UserControls"
-             mc:Ignorable="d" x:Name="self"
-             d:DesignHeight="310" d:DesignWidth="420">
+             mc:Ignorable="d" x:Name="self" d:DesignWidth="420" Height="336">
     <UserControl.Resources>
         <converters:BoolToYellowColor x:Key="boolToYellowColor"></converters:BoolToYellowColor>
         <converters:BoolToColor x:Key="boolToColor"></converters:BoolToColor>
@@ -17,115 +16,124 @@
         <converters:BoolToBool x:Key="boolToBool"></converters:BoolToBool>
     </UserControl.Resources>
     <GroupBox Header="Plating Cell Control">
-    <Grid>
-        <Grid.RowDefinitions>
-            <RowDefinition Height="40"/>
-            <RowDefinition Height="40"/>
-            <RowDefinition Height="40"/>
-            <RowDefinition Height="40"/>
-            <RowDefinition Height="40"/>
-            <RowDefinition Height="40"/>
-            <RowDefinition Height="40"/>
-            <RowDefinition/>
-        </Grid.RowDefinitions>
-        <Grid.ColumnDefinitions>
-            <ColumnDefinition Width="180"/>
-            <ColumnDefinition Width="30"/>
-            <ColumnDefinition Width="80"/>
-            <ColumnDefinition Width="80"/>
-            <ColumnDefinition Width="30"/>
-            <ColumnDefinition Width="60"/>
-            <ColumnDefinition/>
-        </Grid.ColumnDefinitions>
-        <Grid Grid.Row="0" Grid.Column="0">
-            <Label Content="Chemistry" FontSize="15" FontWeight="Bold" VerticalContentAlignment="Center" HorizontalContentAlignment="Left" VerticalAlignment="Center" HorizontalAlignment="Left" Margin="10,0,0,0"/>
-        </Grid>
-        <Grid Grid.Row="1" Grid.Column="0">
-            <Label Content="Vertical Station" FontSize="15" FontWeight="Bold" VerticalContentAlignment="Center" HorizontalContentAlignment="Left" VerticalAlignment="Center" HorizontalAlignment="Left" Margin="10,0,0,0"/>
-        </Grid>
-        <Grid Grid.Row="2" Grid.Column="0">
-            <Label Content="Wafer Size" FontSize="15" FontWeight="Bold" VerticalContentAlignment="Center" HorizontalContentAlignment="Left" VerticalAlignment="Center" HorizontalAlignment="Left" Margin="10,0,0,0"/>
-        </Grid>
-        <Grid Grid.Row="3" Grid.Column="0">
-            <Label Content="Clamshell Sensor" FontSize="15" FontWeight="Bold" VerticalContentAlignment="Center" HorizontalContentAlignment="Left" VerticalAlignment="Center" HorizontalAlignment="Left" Margin="10,0,0,0"/>
-        </Grid>
-        <Grid Grid.Row="4" Grid.Column="0">
-            <Label Content="Clamshell" FontSize="15" FontWeight="Bold" VerticalContentAlignment="Center" HorizontalContentAlignment="Left" VerticalAlignment="Center" HorizontalAlignment="Left" Margin="10,0,0,0"/>
-        </Grid>
-        <Grid Grid.Row="5" Grid.Column="0">
-            <Label Content="Angle Entry" FontSize="15" FontWeight="Bold" VerticalContentAlignment="Center" HorizontalContentAlignment="Left" VerticalAlignment="Center" HorizontalAlignment="Left" Margin="10,0,0,0"/>
-        </Grid>
-        <Grid Grid.Row="6" Grid.Column="0">
-            <Label Content="Rotation" FontSize="15" FontWeight="Bold" VerticalContentAlignment="Center" HorizontalContentAlignment="Left" VerticalAlignment="Center" HorizontalAlignment="Left" Margin="10,0,0,0"/>
-        </Grid>
-        <Border Grid.Row="1" Grid.Column="1"  Grid.ColumnSpan="4" Margin="5,5,5,5" Background="Black"  VerticalAlignment="Center" >
-            <TextBlock  Text="{Binding ElementName=self,Path=VerticalStation}" Foreground="Lime" FontSize="16" FontWeight="Bold" VerticalAlignment="Center" HorizontalAlignment="Left"/>
-        </Border>
+        <Grid>
+            <Grid.RowDefinitions>
+                <RowDefinition Height="40"/>
+                <RowDefinition Height="40"/>
+                <RowDefinition Height="40"/>
+                <RowDefinition Height="40"/>
+                <RowDefinition Height="40"/>
+                <RowDefinition Height="40"/>
+                <RowDefinition Height="40"/>
+                <RowDefinition/>
+            </Grid.RowDefinitions>
+            <Grid.ColumnDefinitions>
+                <ColumnDefinition Width="180"/>
+                <ColumnDefinition Width="30"/>
+                <ColumnDefinition Width="80"/>
+                <ColumnDefinition Width="80"/>
+                <ColumnDefinition Width="30"/>
+                <ColumnDefinition Width="60"/>
+                <ColumnDefinition/>
+            </Grid.ColumnDefinitions>
+            <Grid Grid.Row="0" Grid.Column="0">
+                <Label Content="Chemistry" FontSize="15" FontWeight="Bold" VerticalContentAlignment="Center" HorizontalContentAlignment="Left" VerticalAlignment="Center" HorizontalAlignment="Left" Margin="10,0,0,0"/>
+            </Grid>
+            <Grid Grid.Row="1" Grid.Column="0">
+                <Label Content="Vertical Station" FontSize="15" FontWeight="Bold" VerticalContentAlignment="Center" HorizontalContentAlignment="Left" VerticalAlignment="Center" HorizontalAlignment="Left" Margin="10,0,0,0"/>
+            </Grid>
+            <Grid Grid.Row="2" Grid.Column="0">
+                <Label Content="Wafer Size" FontSize="15" FontWeight="Bold" VerticalContentAlignment="Center" HorizontalContentAlignment="Left" VerticalAlignment="Center" HorizontalAlignment="Left" Margin="10,0,0,0"/>
+            </Grid>
+            <Grid Grid.Row="3" Grid.Column="0">
+                <Label Content="Clamshell Sensor" FontSize="15" FontWeight="Bold" VerticalContentAlignment="Center" HorizontalContentAlignment="Left" VerticalAlignment="Center" HorizontalAlignment="Left" Margin="10,0,0,0"/>
+            </Grid>
+            <Grid Grid.Row="4" Grid.Column="0">
+                <Label Content="Clamshell" FontSize="15" FontWeight="Bold" VerticalContentAlignment="Center" HorizontalContentAlignment="Left" VerticalAlignment="Center" HorizontalAlignment="Left" Margin="10,0,0,0"/>
+            </Grid>
+            <Grid Grid.Row="5" Grid.Column="0">
+                <Label Content="Angle Entry" FontSize="15" FontWeight="Bold" VerticalContentAlignment="Center" HorizontalContentAlignment="Left" VerticalAlignment="Center" HorizontalAlignment="Left" Margin="10,0,0,0"/>
+            </Grid>
+            <Grid Grid.Row="6" Grid.Column="0">
+                <Label Content="Rotation" FontSize="15" FontWeight="Bold" VerticalContentAlignment="Center" HorizontalContentAlignment="Left" VerticalAlignment="Center" HorizontalAlignment="Left" Margin="10,0,0,0"/>
+            </Grid>
+            <Border Grid.Row="1" Grid.Column="1"  Grid.ColumnSpan="4" Margin="5,5,5,5" Background="Black"  VerticalAlignment="Center" >
+                <TextBlock  Text="{Binding ElementName=self,Path=VerticalStation}" Foreground="Lime" FontSize="16" FontWeight="Bold" VerticalAlignment="Center" HorizontalAlignment="Left"/>
+            </Border>
 
-        <ComboBox x:Name="cbox" Grid.Row="2" Grid.Column="3" Grid.ColumnSpan="2" Height="25" Width="80" ItemsSource="{Binding WaferSizeList}" SelectedItem="{Binding SelectedWaferSize,Mode=TwoWay}" SelectionChanged="WaferSize_SelectionChanged"/>
+            <ComboBox x:Name="cbox" Grid.Row="2" Grid.Column="3" Grid.ColumnSpan="2" Height="25" Width="80" ItemsSource="{Binding WaferSizeList}" SelectedItem="{Binding SelectedWaferSize,Mode=TwoWay}" SelectionChanged="WaferSize_SelectionChanged"/>
 
-        <Grid Grid.Row="6" Grid.Column="2" >
-            <Button Style="{StaticResource SysBtnStyle}" Margin="0,0,0,0" Grid.Column="1" Height="25" Width="60" HorizontalAlignment="Center" Content="Start" Click="RotationStart_Click"></Button>
-        </Grid>
-        <Grid Grid.Row="6" Grid.Column="3" >
-            <Button Style="{StaticResource SysBtnStyle}" Margin="0,0,0,0" Grid.Column="1" Height="25" Width="60" HorizontalAlignment="Center" Content="Stop" Click="RotationStop_Click"></Button>
-        </Grid>
+            <Grid Grid.Row="6" Grid.Column="2" >
+                <Button Style="{StaticResource SysBtnStyle}" Margin="0,0,0,0" Grid.Column="1" Height="25" Width="60" HorizontalAlignment="Center" Content="Start" Click="RotationStart_Click"></Button>
+            </Grid>
+            <Grid Grid.Row="6" Grid.Column="3" >
+                <Button Style="{StaticResource SysBtnStyle}" Margin="0,0,0,0" Grid.Column="1" Height="25" Width="60" HorizontalAlignment="Center" Content="Stop" Click="RotationStop_Click"></Button>
+            </Grid>
 
-        <Control:NumbericTextBox Grid.Row="7" Grid.Column="1" Grid.ColumnSpan="2" VerticalAlignment="Center" HorizontalAlignment="Left" FontSize="15" FontWeight="Bold" Height="22" Width="50" Margin="5,0,5,0"
+            <Grid Grid.Row="7" Grid.Column="0" >
+                <Button Style="{StaticResource SysBtnStyle}" Margin="0,0,140,0" Grid.Column="1" Height="25" Width="30" HorizontalAlignment="Center" Content="BiS" Click="BiRotationStart_Click"></Button>
+            </Grid>
+            <Label Grid.Row="7" Grid.Column="0" Content="freq" Margin="0,0,70,0" Width="40" Height="30"></Label>
+            <Control:NumbericTextBox Grid.Row="7" Grid.Column="0" Grid.ColumnSpan="1" VerticalAlignment="Center" HorizontalAlignment="Left" FontSize="15" FontWeight="Bold" Height="22" Width="50" Margin="71,0,0,0"
+                 Value="{Binding ElementName=self,Path=InputRotationFrequency,Mode=TwoWay}"  
+                IsEnabled="True"/>
+            <TextBlock Grid.Row="7" Background="White"   Grid.Column="0" VerticalAlignment="Center" HorizontalAlignment="Left"  FontSize="15" FontWeight="Bold" Height="22" Width="40" Margin="126,0,0,0" Text="sec"/>
+
+            <Control:NumbericTextBox Grid.Row="7" Grid.Column="1" Grid.ColumnSpan="2" VerticalAlignment="Center" HorizontalAlignment="Left" FontSize="15" FontWeight="Bold" Height="22" Width="50" Margin="5,0,5,0"
                 Value="{Binding ElementName=self,Path=InputRotationSpeed,Mode=TwoWay}"  
                 IsEnabled="True"/>
-        <TextBlock Grid.Row="7" Background="White"   Grid.Column="2" Grid.ColumnSpan="2" VerticalAlignment="Center" HorizontalAlignment="Center"  FontSize="15" FontWeight="Bold" Height="22" Width="40" Margin="-35,0,30,0" Text="rpm"/>
+            <TextBlock Grid.Row="7" Background="White"   Grid.Column="2" Grid.ColumnSpan="2" VerticalAlignment="Center" HorizontalAlignment="Center"  FontSize="15" FontWeight="Bold" Height="22" Width="40" Margin="-35,0,30,0" Text="rpm"/>
 
-        <Control:NumbericTextBox Grid.Row="7" Grid.Column="3" Grid.ColumnSpan="2" VerticalAlignment="Center" HorizontalAlignment="Left" FontSize="15" FontWeight="Bold" Height="22" Width="50" Margin="-7,0,5,0"
+            <Control:NumbericTextBox Grid.Row="7" Grid.Column="3" Grid.ColumnSpan="2" VerticalAlignment="Center" HorizontalAlignment="Left" FontSize="15" FontWeight="Bold" Height="22" Width="50" Margin="-7,0,5,0"
           Value="{Binding ElementName=self,Path=InputRotationTime,Mode=TwoWay}"  
           IsEnabled="True"/>
-        <TextBlock Grid.Row="7" Background="White"   Grid.Column="4" Grid.ColumnSpan="2" VerticalAlignment="Center" HorizontalAlignment="Center"  FontSize="15" FontWeight="Bold" Height="22" Width="40" Margin="-65,0,50,0" Text="sec"/>
+            <TextBlock Grid.Row="7" Background="White"   Grid.Column="4" Grid.ColumnSpan="2" VerticalAlignment="Center" HorizontalAlignment="Center"  FontSize="15" FontWeight="Bold" Height="22" Width="40" Margin="-65,0,50,0" Text="sec"/>
 
 
-        <Grid Grid.Row="4" Grid.Column="1">
-            <Ellipse Grid.Column="1" Width="16" Height="16" HorizontalAlignment="Center" VerticalAlignment="Center" Stroke="Silver"
+            <Grid Grid.Row="4" Grid.Column="1">
+                <Ellipse Grid.Column="1" Width="16" Height="16" HorizontalAlignment="Center" VerticalAlignment="Center" Stroke="Silver"
                 Fill="{Binding IsClamshellOpen, Converter={StaticResource boolToColor}, ElementName=self}" />
-        </Grid>
-        <Grid Grid.Row="4" Grid.Column="2" >
-            <Button Style="{StaticResource SysBtnStyle}" Margin="0,0,0,0" Grid.Column="1" Height="25" Width="60" HorizontalAlignment="Center" Content="Open" Click="ClamshellOpen_Click"></Button>
-        </Grid>
-        <Grid Grid.Row="4" Grid.Column="3" >
-            <Button Style="{StaticResource SysBtnStyle}" Margin="0,0,0,0" Grid.Column="1" Height="25" Width="60" HorizontalAlignment="Center" Content="Close" Click="ClamshellClose_Click"></Button>
-        </Grid>
-        <Grid Grid.Row="4" Grid.Column="4">
-            <Ellipse Grid.Column="1" Width="16" Height="16" HorizontalAlignment="Center" VerticalAlignment="Center" Stroke="Silver"
+            </Grid>
+            <Grid Grid.Row="4" Grid.Column="2" >
+                <Button Style="{StaticResource SysBtnStyle}" Margin="0,0,0,0" Grid.Column="1" Height="25" Width="60" HorizontalAlignment="Center" Content="Open" Click="ClamshellOpen_Click"></Button>
+            </Grid>
+            <Grid Grid.Row="4" Grid.Column="3" >
+                <Button Style="{StaticResource SysBtnStyle}" Margin="0,0,0,0" Grid.Column="1" Height="25" Width="60" HorizontalAlignment="Center" Content="Close" Click="ClamshellClose_Click"></Button>
+            </Grid>
+            <Grid Grid.Row="4" Grid.Column="4">
+                <Ellipse Grid.Column="1" Width="16" Height="16" HorizontalAlignment="Center" VerticalAlignment="Center" Stroke="Silver"
                     Fill="{Binding IsClamshellClose, Converter={StaticResource boolToColor}, ElementName=self}" />
-        </Grid>
+            </Grid>
 
-        <Grid Grid.Row="5" Grid.Column="1">
-            <Ellipse Grid.Column="1" Width="16" Height="16" HorizontalAlignment="Center" VerticalAlignment="Center" Stroke="Silver"
+            <Grid Grid.Row="5" Grid.Column="1">
+                <Ellipse Grid.Column="1" Width="16" Height="16" HorizontalAlignment="Center" VerticalAlignment="Center" Stroke="Silver"
                 Fill="{Binding IsAngleTilt, Converter={StaticResource boolToColor}, ElementName=self}"/>
-        </Grid>
-        <Grid Grid.Row="5" Grid.Column="2" >
-            <Button Style="{StaticResource SysBtnStyle}" Margin="0,0,0,0" Grid.Column="1" Height="25" Width="60" HorizontalAlignment="Center" Content="Tilt" Click="AngleTilt_Click"></Button>
-        </Grid>
-        <Grid Grid.Row="5" Grid.Column="3" >
-            <Button Style="{StaticResource SysBtnStyle}" Margin="0,0,0,0" Grid.Column="1" Height="25" Width="60" HorizontalAlignment="Center" Content="Vertical" Click="AngleVertical_Click"></Button>
-        </Grid>
-        <Grid Grid.Row="5" Grid.Column="4">
-            <Ellipse Grid.Column="1" Width="16" Height="16" HorizontalAlignment="Center" VerticalAlignment="Center" Stroke="Silver"
+            </Grid>
+            <Grid Grid.Row="5" Grid.Column="2" >
+                <Button Style="{StaticResource SysBtnStyle}" Margin="0,0,0,0" Grid.Column="1" Height="25" Width="60" HorizontalAlignment="Center" Content="Tilt" Click="AngleTilt_Click"></Button>
+            </Grid>
+            <Grid Grid.Row="5" Grid.Column="3" >
+                <Button Style="{StaticResource SysBtnStyle}" Margin="0,0,0,0" Grid.Column="1" Height="25" Width="60" HorizontalAlignment="Center" Content="Vertical" Click="AngleVertical_Click"></Button>
+            </Grid>
+            <Grid Grid.Row="5" Grid.Column="4">
+                <Ellipse Grid.Column="1" Width="16" Height="16" HorizontalAlignment="Center" VerticalAlignment="Center" Stroke="Silver"
               Fill="{Binding IsAngleVertical, Converter={StaticResource boolToColor}, ElementName=self}"/>
+            </Grid>
+            <Border Grid.Row="0" Grid.Column="1" Grid.ColumnSpan="4" Margin="5,5,5,5" Background="Black" VerticalAlignment="Center">
+                <TextBlock  Text="{Binding ElementName=self,Path=Chemistry}" Foreground="Lime" FontSize="16" FontWeight="Bold" VerticalAlignment="Center" HorizontalAlignment="Left"/>
+            </Border>
+            <Border Grid.Row="2" Grid.Column="1"  Grid.ColumnSpan="2" Margin="5,5,5,5" Background="Black" Width="50" VerticalAlignment="Center" HorizontalAlignment="Left">
+                <TextBlock  Text="{Binding ElementName=self,Path=SelectedWaferSize,Mode=TwoWay}" Foreground="Lime" FontSize="16" FontWeight="Bold" VerticalAlignment="Center" HorizontalAlignment="Left"/>
+            </Border>
+            <Border Grid.Row="2" Grid.Column="2"  Grid.ColumnSpan="2" Margin="32,5,5,5" Background="Black" Height="22" Width="50" VerticalAlignment="Center" HorizontalAlignment="Left">
+                <TextBlock  Text="mm" Foreground="Lime" FontSize="16" FontWeight="Bold" VerticalAlignment="Center" HorizontalAlignment="Left"/>
+            </Border>
+            <Border Grid.Row="3" Grid.Column="1"  Grid.ColumnSpan="2" Margin="5,5,5,5" Background="Black" Width="50" VerticalAlignment="Center" HorizontalAlignment="Left">
+                <TextBlock  Text="{Binding ElementName=self,Path=ClamshellSensor}" Foreground="Lime" FontSize="16" FontWeight="Bold" VerticalAlignment="Center" HorizontalAlignment="Left"/>
+            </Border>
+            <Border Grid.Row="3" Grid.Column="2"  Grid.ColumnSpan="2" Margin="32,5,5,5" Background="Black" Height="22" Width="50" VerticalAlignment="Center" HorizontalAlignment="Left">
+                <TextBlock  Text="mm" Foreground="Lime" FontSize="16" FontWeight="Bold" VerticalAlignment="Center" HorizontalAlignment="Left"/>
+            </Border>
         </Grid>
-        <Border Grid.Row="0" Grid.Column="1" Grid.ColumnSpan="4" Margin="5,5,5,5" Background="Black" VerticalAlignment="Center">
-            <TextBlock  Text="{Binding ElementName=self,Path=Chemistry}" Foreground="Lime" FontSize="16" FontWeight="Bold" VerticalAlignment="Center" HorizontalAlignment="Left"/>
-        </Border>
-        <Border Grid.Row="2" Grid.Column="1"  Grid.ColumnSpan="2" Margin="5,5,5,5" Background="Black" Width="50" VerticalAlignment="Center" HorizontalAlignment="Left">
-            <TextBlock  Text="{Binding ElementName=self,Path=SelectedWaferSize,Mode=TwoWay}" Foreground="Lime" FontSize="16" FontWeight="Bold" VerticalAlignment="Center" HorizontalAlignment="Left"/>
-        </Border>
-        <Border Grid.Row="2" Grid.Column="2"  Grid.ColumnSpan="2" Margin="32,5,5,5" Background="Black" Height="22" Width="50" VerticalAlignment="Center" HorizontalAlignment="Left">
-            <TextBlock  Text="mm" Foreground="Lime" FontSize="16" FontWeight="Bold" VerticalAlignment="Center" HorizontalAlignment="Left"/>
-        </Border>
-        <Border Grid.Row="3" Grid.Column="1"  Grid.ColumnSpan="2" Margin="5,5,5,5" Background="Black" Width="50" VerticalAlignment="Center" HorizontalAlignment="Left">
-            <TextBlock  Text="{Binding ElementName=self,Path=ClamshellSensor}" Foreground="Lime" FontSize="16" FontWeight="Bold" VerticalAlignment="Center" HorizontalAlignment="Left"/>
-        </Border>
-        <Border Grid.Row="3" Grid.Column="2"  Grid.ColumnSpan="2" Margin="32,5,5,5" Background="Black" Height="22" Width="50" VerticalAlignment="Center" HorizontalAlignment="Left">
-            <TextBlock  Text="mm" Foreground="Lime" FontSize="16" FontWeight="Bold" VerticalAlignment="Center" HorizontalAlignment="Left"/>
-        </Border>
-    </Grid>
     </GroupBox>
 </UserControl>

+ 22 - 0
PunkHPX8_Themes/UserControls/PlatingCellStatusControl.xaml.cs

@@ -198,6 +198,23 @@ namespace PunkHPX8_Themes.UserControls
             }
         }
 
+        public static readonly DependencyProperty InputRotationFrequencyProperty = DependencyProperty.Register(
+           "InputRotationFrequency", typeof(int), typeof(PlatingCellStatusControl), new FrameworkPropertyMetadata(0, FrameworkPropertyMetadataOptions.AffectsRender));
+        /// <summary>
+        /// InputRotationFrequency
+        /// </summary>
+        public int InputRotationFrequency
+        {
+            get
+            {
+                return (int)this.GetValue(InputRotationFrequencyProperty);
+            }
+            set
+            {
+                this.SetValue(InputRotationFrequencyProperty, value);
+            }
+        }
+
         public static readonly DependencyProperty InputRotationSpeedProperty = DependencyProperty.Register(
            "InputRotationSpeed", typeof(int), typeof(PlatingCellStatusControl), new FrameworkPropertyMetadata(0, FrameworkPropertyMetadataOptions.AffectsRender));
         /// <summary>
@@ -266,5 +283,10 @@ namespace PunkHPX8_Themes.UserControls
         {
             InvokeClient.Instance.Service.DoOperation($"{ModuleName}.SetPlatingCellWaferSize", cbox.SelectedItem.ToString());
         }
+
+        private void BiRotationStart_Click(object sender, RoutedEventArgs e)
+        {
+            InvokeClient.Instance.Service.DoOperation($"{ModuleName}.StartBiRotation", InputRotationTime, InputRotationFrequency, InputRotationSpeed);
+        }
     }
 }