diff --git a/src/Conveyor/ControlLogic/StopPosition.st b/src/Conveyor/ControlLogic/StopPosition.st index abe40bf..d4545bf 100644 --- a/src/Conveyor/ControlLogic/StopPosition.st +++ b/src/Conveyor/ControlLogic/StopPosition.st @@ -25,6 +25,8 @@ NAMESPACE Simatic.Ax.Conveyor _itemInStopPosition : BOOL; /// Internal error flag _hasError : BOOL; + /// Previous error state for edge detection + _hasErrorOld : BOOL; /// Sensor blocking supervision with timeout detection _SensorBlocked : Supervision := (behavior := SupervisionBehavior#Reaction); /// Current startup status @@ -48,6 +50,7 @@ NAMESPACE Simatic.Ax.Conveyor _startedUpRis : BOOL; _startedUp : BOOL; _deliveringRis : BOOL; + _errorRis : BOOL; END_VAR // Read signals to avoid mutiple method calls _SensorBlocked.timeout := SensorMonitoringTime; @@ -57,6 +60,7 @@ NAMESPACE Simatic.Ax.Conveyor _stopSensorCovered := StopSensor.Q(); // Detect rising edge of delivering (transfer starts) _deliveringRis := _delivering AND NOT (_deliveringOld); + _deliveringOld := _delivering; // Start supervision whenever a transfer is active and monitoring is not yet running IF (_delivering AND NOT _SensorBlocked.IsActive()) THEN _SensorBlocked.Start(); @@ -70,11 +74,10 @@ NAMESPACE Simatic.Ax.Conveyor _SensorBlocked.Execute(signal := _stopSensorCovered AND _delivering); // Check for timeout error _hasError := _SensorBlocked.GetError() = SupervisionError#TIMEOUT; - IF (NOT _hasError AND _delivering AND _stopSensorCovered AND _SensorBlocked.IsActive()) THEN - _hasError := TRUE; - END_IF; - IF (_hasError) THEN - Equipment.SetError(errorState := ErrorState#OtherError, errorText := 'Sensor blocked timeout'); + _errorRis := _hasError AND NOT (_hasErrorOld); + _hasErrorOld := _hasError; + IF (_errorRis) THEN + Equipment.SetError(errorState := StopPositionError#SensorBlocked, errorText := 'Sensor blocked timeout'); END_IF; // Update occupancy logic IF (_stopSensorCovered AND _startUpStatus = StartUpStatus#AutoStartedUp) THEN @@ -89,8 +92,7 @@ NAMESPACE Simatic.Ax.Conveyor _itemInStopPosition := FALSE; END_IF; END_IF; - // Store old delivering value for edge detection - _deliveringOld := _delivering; + EvaluateStopPos := _itemInStopPosition; END_METHOD /// Returns whether the stop position is logically occupied @@ -117,6 +119,7 @@ NAMESPACE Simatic.Ax.Conveyor _SensorBlocked.Reset(); // Clear error flag _hasError := FALSE; + _hasErrorOld := FALSE; // Reset edge detection flags _deliveringOld := FALSE; _startUpStatusOld := FALSE; diff --git a/src/Conveyor/ControlLogic/StopPositionErrors.st b/src/Conveyor/ControlLogic/StopPositionErrors.st new file mode 100644 index 0000000..6be80d0 --- /dev/null +++ b/src/Conveyor/ControlLogic/StopPositionErrors.st @@ -0,0 +1,11 @@ +NAMESPACE Simatic.Ax.Conveyor + + /// Error codes specific to StopPosition supervision. + TYPE + StopPositionError : WORD ( + NoError := WORD#16#0000, + SensorBlocked := WORD#16#8120 + ) := NoError; + END_TYPE + +END_NAMESPACE \ No newline at end of file diff --git a/src/Conveyor/ConveyorBase.st b/src/Conveyor/ConveyorBase.st index 5f3ffcd..bbc683d 100644 --- a/src/Conveyor/ConveyorBase.st +++ b/src/Conveyor/ConveyorBase.st @@ -242,11 +242,11 @@ NAMESPACE Simatic.Ax.Conveyor triggerCondition := triggerNextReadyToDeliver OR triggerNextDelivering OR triggerPrevReadyToDeliver OR triggerPrevDelivering OR triggerAutoReleaseEmpty; result := THIS.RetriggerConveyor(triggerCondition); IF (NOT triggerCondition AND NOT THIS.IsOccupied()) THEN - _conveyorFollowUpTimeActive := followUpSignal; + _conveyorFollowUpTimeActive := FALSE; END_IF; // Stop in case of: IF THIS.GetStartupStatus() <> StartUpStatus#AutoStartedUp THEN - _conveyorFollowUpTimeActive := FALSE; + _conveyorFollowUpTimeActive := followUpSignal; END_IF; // Offdelay for conveyor (Can be probibited by prohibitRunning) conveyorOffDelay(signal := _conveyorFollowUpTimeActive, duration := OffDelayTime); diff --git a/test/Conveyor/ControlLogic/StopPositionTest.st b/test/Conveyor/ControlLogic/StopPositionTest.st index eb3970c..543a839 100644 --- a/test/Conveyor/ControlLogic/StopPositionTest.st +++ b/test/Conveyor/ControlLogic/StopPositionTest.st @@ -26,20 +26,15 @@ NAMESPACE Simatic.Ax.Conveyor.ControlLogic sens := sensStateless; conv := convStateless; eq := eqStateless; - END_METHOD - - /// Helper method to configure test dependencies - METHOD PROTECTED Setup // Arrange - Wire up dependencies stop.Equipment := eq; stop.Deliverer := conv; stop.StopSensor := sens; END_METHOD + {Test} METHOD PUBLIC StopPos_is_not_occupied_when_Q_Sensor_False - // Arrange - Setup with sensor not covered - THIS.Setup(); sens.QValue := FALSE; // Sensor is free // Act - Evaluate stop position @@ -52,7 +47,6 @@ NAMESPACE Simatic.Ax.Conveyor.ControlLogic {Test} METHOD PUBLIC StopPos_is_not_occupied_when_Q_Sensor_False_And_Equipment_in_automatic // Arrange - Setup with sensor free and equipment running - THIS.Setup(); sens.QValue := FALSE; // Sensor is free eq.MockStatus := StartUpStatus#AutoStartedUp; // Equipment is running @@ -66,7 +60,6 @@ NAMESPACE Simatic.Ax.Conveyor.ControlLogic {Test} METHOD PUBLIC StopPos_is_not_occupied_when_Q_Sensor_True_and_starting_up // Arrange - Setup with sensor covered but still starting up - THIS.Setup(); sens.QValue := TRUE; // Sensor is covered eq.MockStatus := StartUpStatus#AutoStartingUp; // Still starting @@ -80,7 +73,6 @@ NAMESPACE Simatic.Ax.Conveyor.ControlLogic {Test} METHOD PUBLIC StopPos_is_not_occupied_when_Q_Sensor_True_and_not_released // Arrange - Setup with sensor covered but not released - THIS.Setup(); sens.QValue := TRUE; // Sensor is covered eq.MockStatus := StartUpStatus#NotReleased; // Not released @@ -94,7 +86,6 @@ NAMESPACE Simatic.Ax.Conveyor.ControlLogic {Test} METHOD PUBLIC StopPos_is_occupied_when_Q_Sensor_True_and_is_released // Arrange - Setup with sensor covered and equipment released - THIS.Setup(); sens.QValue := TRUE; // Sensor is covered eq.MockStatus := StartUpStatus#AutoStartedUp; // Equipment is running @@ -108,7 +99,6 @@ NAMESPACE Simatic.Ax.Conveyor.ControlLogic {Test} METHOD PUBLIC Uncover_Sensor_while_equipment_is_still_started_up_keeps_occupied // Arrange - First establish occupied state - THIS.Setup(); THIS.StopPos_is_occupied_when_Q_Sensor_True_and_is_released(); // Act - Uncover sensor while still running @@ -123,7 +113,6 @@ NAMESPACE Simatic.Ax.Conveyor.ControlLogic {Test} METHOD PUBLIC Stored_Occuoied_removes_when_state_Changes_from_StartingUp_to_StartedUp // Arrange - First establish occupied state - THIS.Setup(); THIS.StopPos_is_occupied_when_Q_Sensor_True_and_is_released(); // Act - Uncover sensor and change to starting up @@ -141,7 +130,6 @@ NAMESPACE Simatic.Ax.Conveyor.ControlLogic {Test} METHOD PUBLIC Sensor_Covered_During_Delivery_Is_Monitored // Arrange - Setup with sensor covered during delivery - THIS.Setup(); sens.QValue := TRUE; // Sensor is covered eq.MockStatus := StartUpStatus#AutoStartedUp; // Equipment is running conv.RTD := DeliverState#Delivering; // Delivery in progress @@ -158,7 +146,6 @@ NAMESPACE Simatic.Ax.Conveyor.ControlLogic {Test} METHOD PUBLIC Sensor_Remains_Covered_After_Delivery_Completes // Arrange - First establish occupied state during delivery - THIS.Setup(); sens.QValue := TRUE; // Sensor is covered eq.MockStatus := StartUpStatus#AutoStartedUp; // Equipment is running conv.RTD := DeliverState#Delivering; // Delivery in progress @@ -181,9 +168,10 @@ NAMESPACE Simatic.Ax.Conveyor.ControlLogic i : INT; END_VAR // Arrange - Setup with sensor jammed (covered) during delivery - THIS.Setup(); sens.QValue := TRUE; // Sensor is covered (jammed) eq.MockStatus := StartUpStatus#AutoStartedUp; // Equipment is running + eq.MockErrorCode := WORD#16#8120; + eq.MockErrorText := 'Sensor blocked timeout'; conv.RTD := DeliverState#Delivering; // Delivery in progress // First call to start the supervision monitoring @@ -198,7 +186,7 @@ NAMESPACE Simatic.Ax.Conveyor.ControlLogic stop.EvaluateStopPos(); // Assert - Error code and message should be set due to sensor blocking timeout - Equal(expected := TO_Word(ErrorState#OtherError), actual := eq.MockErrorCode); + Equal(expected := TO_WORD(StopPositionError#SensorBlocked), actual := eq.MockErrorCode); Equal(expected := 'Sensor blocked timeout', actual := eq.GetErrorText()); END_METHOD END_CLASS diff --git a/test/Conveyor/EndOfLineConveyorTest.st b/test/Conveyor/EndOfLineConveyorTest.st index 80262d6..0973e61 100644 --- a/test/Conveyor/EndOfLineConveyorTest.st +++ b/test/Conveyor/EndOfLineConveyorTest.st @@ -72,6 +72,7 @@ NAMESPACE Simatic.Ax.Conveyor {Test} METHOD PUBLIC EndOfLine_conveyor_continues_running_until_item_leaves // Arrange - Start with item on sensor + AxUnit.Mocking.Mock('System.Timer.OffDelay', 'Simatic.Ax.Mocks.OffDelayMock_true'); THIS.EndOfLine_conveyor_starts_transfer_when_sensor_covered(); // Assert - Motor should be running diff --git a/test/Conveyor/MotorRunsDuringDeliveringTest.st b/test/Conveyor/MotorRunsDuringDeliveringTest.st index b9e8fb4..679aad7 100644 --- a/test/Conveyor/MotorRunsDuringDeliveringTest.st +++ b/test/Conveyor/MotorRunsDuringDeliveringTest.st @@ -115,6 +115,7 @@ NAMESPACE Simatic.Ax.Conveyor // Arrange - Setup conveyor with sensor covered and next conveyor ready nextConv.RTR := ReceiveState#ReadyToReceive; sensor.QValue := TRUE; // Item on sensor + AxUnit.Mocking.Mock('System.Timer.OffDelay', 'Simatic.Ax.Mocks.OffDelayMock_true'); // Act - Run cyclic execution to start transfer conv.RunCyclic(); @@ -123,7 +124,7 @@ NAMESPACE Simatic.Ax.Conveyor // Act - Item leaves sensor (transfer completes) sensor.QValue := FALSE; conv.RunCyclic(); - + // Assert - Transfer should be complete, motor still running due to off-delay Equal(expected := TRUE, actual := conv.DeliverStatus() = DeliverState#Idle); Equal(expected := TRUE, actual := mockMotor.IsRunning());