﻿<?xml version="1.0" encoding="utf-8"?>
<TcPlcObject Version="1.1.0.1">
  <POU Name="SDR_Controller" Id="{ae4767f0-ab8e-4d80-a822-8639cb507227}" SpecialFunc="None">
    <Declaration><![CDATA[FUNCTION_BLOCK SDR_Controller // EXTENDS p6manualDevices.BaseControlUnit
VAR_INPUT
	TRW				: REFERENCE TO TRWAutoWinch;  // was autowinch
	SDR 			: REFERENCE TO autowinch;
	SDR_ewinch		: REFERENCE TO p6winch.EWinch;		// only for obtaining radius
	SWC				: REFERENCE TO SlackWireCompensator;
	SDR_controlUnit	: REFERENCE TO  BaseControlUnit; //IBaseControlUnit;								// local winch control panel
	splitMode		: BOOL;											// digital input from panel
	config			: REFERENCE TO TRW_config;
	estop			: p6com.IEmergencyStop; 
	brakeHPU		: REFERENCE TO localHPUBrake;
		
END_VAR
VAR
	_firstScan 		: BOOL := TRUE;
	_opState				: p7type.P6OpState;
	_state					: SM;
	_SWCtensionSetpoint		: LREAL; 	// (ton)
	_SDRspeedCommand		: LREAL ;	// (0-1)
	_SWCpositionSetpoint	: LREAL := 0.5; // normal is center position
	_SDR_MaxVelocity 		: LREAL ;	// (m/s)how fast can we go at current radius
	_startingTimeout 		: TON := (PT := T#15S);
	_positioningTimeout 	: TON := (PT := T#15S);
	_parkingTimeout 		: TON := (PT := T#15S);
	_errorMsg				: DebugMessage_timed;
	// debug
	SDR_Activate	 : BOOL;
	SDR_Deactivate : BOOL;

END_VAR



]]></Declaration>
    <Implementation>
      <ST><![CDATA[//No valid references on first scan!
IF  _firstScan THEN
	_firstScan := FALSE;
	RETURN;
END_IF

_SDR_MaxVelocity := _calcMaxSDRvelocity() ;
_SWCtensionSetpoint := _calcSWCtension( TRW.Tension );	// Calclulate backtension based on TRW tension (which is in KG)
_SDRspeedCommand := _calcSDRspeed( TRW.Velocity, SWC.position, _SWCpositionSetpoint);

// Timers
_startingTimeout	(in := _state = SM.STARTING); 	
_positioningTimeout (in := _state = SM.POSITIONING); 	
_parkingTimeout 	(in := _state = SM.PARKING); 

//Common state transitions
IF _state <> SM.IDLE THEN
	IF estop.Activated THEN
		_state := SM.ERROR;
		_errorMsg(st1 := 'TRW system error: E-stop activated',st2 := '',st3 := '');
	ELSIF SWC.opState = p6opstate.NOTAVAILABLE THEN
		_state := SM.ERROR;
		_errorMsg(st1 := 'TRW system error: SWC not operational',st2 := '',st3 := '');
	ELSIF TRW.opState = p6opstate.NOTAVAILABLE THEN
		_state := SM.ERROR;
		_errorMsg(st1 := 'TRW system error: Tractionwinch not operational',st2 := '',st3 := '');
	ELSIF SDR.opState = p6opstate.NOTAVAILABLE THEN
		_state := SM.ERROR;
		_errorMsg(st1 := 'TRW system error: Storagedrum not operational',st2 := '',st3 := '');
	ELSIF brakeHPU.Error THEN
		_state := SM.ERROR;
		_errorMsg(st1 := 'TRW system error: Brake HPU Error',st2 := '',st3 := '');
	END_IF
END_IF

// always run code for storage Drum/CU
_SDRstateController();

CASE _state OF
	SM.IDLE:
		_opstate := P6OpState.STOPPED; 
		IF TRW.OpState = P6Opstate.RUN AND splitMode = FALSE THEN 
			_state := SM.STARTING;
			(* EXIT STATE TRANSITION ACTIONS *)
			_SWCpositionSetpoint := 0.5; // center
			SWC.setBacktension(_SWCtensionSetpoint); 
			SWC.activate();
			SDR.TorqueAuto := 1 ;
			SDR.VelocityAuto := 0;
			SDR_Activate := TRUE;;
			TRW.InhibitManualRotation := TRUE;
			DebugMessage('TRW system starting...','','');
		 END_IF
		 			
	SM.STARTING:						//wait for winch and SlacWire to get operational
		_opstate := P6OpState.PREPARE;
		IF SWC.RegulatorAtSetpoint = TRUE AND SDR.State = p7type.UserWinchStates.AUTO_ACTIVE  THEN // SWC har riktig trykk og winch er i run/auto
			 _state := SM.POSITIONING;
		END_IF
		IF _startingTimeout.Q THEN
			_state := SM.STOP;
			IF NOT SWC.RegulatorAtSetpoint THEN
				DebugMessage('TRW system: Timeout waiting for SWC','','');
			END_IF
			IF NOT (SDR.State = p7type.UserWinchStates.AUTO_ACTIVE) THEN
				DebugMessage('TRW system: Timeout waiting for StorageDrum','','');
			END_IF
		END_IF
		
	SM.POSITIONING :
		_opstate := P6OpState.PREPARE;
		IF SWC.InCenter   (* AND SDR.IsRotating = FALSE *) THEN	// wait until SWC is in center and winch not rotating
			 _state := SM.RUN;
			 (* EXIT STATE TRANSITION ACTIONS *)
			 TRW.InhibitManualRotation := FALSE;
			 DebugMessage('TRW system ready to run','','');
		END_IF
		SWC.setBacktension(_SWCtensionSetpoint);
		SDR.TorqueAuto := config.SDRmaxTorqueInAuto ;
		SDR.VelocityAuto := LIMIT(-Config.SDR_parkingSpeed, _SDRspeedCommand, Config.SDR_parkingSpeed);
		IF _positioningTimeout.Q THEN
			_state := SM.STOP;
			DebugMessage('TRW system: Timeout waiting for SWC to reach center','','');
		END_IF
		// check rotation while in center, and stop if wire is slipping over traction winch (no load on deck)
		
	SM.RUN :
		_opstate := P6OpState.RUN;
		IF TRW.OpState = P6Opstate.STOPPED THEN	// normal exit 
			 _state := SM.PARKING;
			 (* EXIT STATE TRANSITION ACTIONS *)
			 TRW.InhibitManualRotation := TRUE;
			 DebugMessage('TRW system stopped by TRW stop command','','');
		ELSIF splitMode THEN	// normal exit 
			_state := SM.PARKING;
			 (* EXIT STATE TRANSITION ACTIONS *)
			 TRW.InhibitManualRotation := TRUE;
				DebugMessage('TRW system stopped due to split mode activated','','');
		ELSE	// normal operation or error
			SWC.setBacktension(_SWCtensionSetpoint);
			SDR.TorqueAuto := config.SDRmaxTorqueInAuto ;
			SDR.VelocityAuto := _SDRspeedCommand;
			// problems?
			IF NOT (SDR.OpState = P6OpState.RUN) THEN
				_State := SM.ERROR;
				 DebugMessage('TRW error: Stop caused by Storage Drum not operational','','');
			END_IF	
			IF NOT (SDR.OpState = P6OpState.RUN) THEN
				_State := SM.ERROR;
				 DebugMessage('TRW error: Stop caused by TRW not operational','','');
			END_IF	
		END_IF		
	SM.PARKING :
		_opstate := P6OpState.PAUSING;
		_SWCpositionSetpoint := 0.0; // Fully retracted
		SDR.VelocityAuto := LIMIT(-Config.SDR_parkingSpeed, _SDRspeedCommand, Config.SDR_parkingSpeed);
		
		IF swc.endSensorInnerTriggered OR SWC.position < 0.03 THEN // normal state exit
			_state := SM.STOP;
			DebugMessage('TRW system parked','','');
		END_IF
		IF _parkingTimeout.Q THEN
			_state := SM.STOP;
			DebugMessage('TRW system: Timeout waiting for SWC to park','','');
		END_IF
		IF NOT (SDR.OpState = P6OpState.RUN) THEN
			_State := SM.ERROR;
			 DebugMessage('TRW error: Stop caused by Storage Drum not operational','','');
		END_IF	
		IF NOT (SDR.OpState = P6OpState.RUN) THEN
			_State := SM.ERROR;
			 DebugMessage('TRW error: Stop caused by TRW not operational','','');
		END_IF	
		// measure SDR length to prevent too much haul in - will detect slip on TRW
	
	SM.STOP :
		_opstate := P6OpState.STOPPED;
		SWC.deactivate();
		SDR.TorqueAuto := 1 ;
		SDR.VelocityAuto := 0;
		TRW.InhibitManualRotation := FALSE;
		SDR_Deactivate := TRUE;		// Stop SDR
		_state := SM.IDLE;
	
	SM.ERROR :
		_opstate := P6OpState.NOTAVAILABLE;
		// Stop operation and check for problems
		SWC.deactivate();
		SDR.TorqueAuto := 1 ;
		SDR.VelocityAuto := 0;
		TRW.InhibitManualRotation := FALSE;
		SDR_Deactivate := TRUE;			// Stop SDR
		
		IF estop.Activated THEN
			_state := SM.ESTOP;
		END_IF
		IF SWC.opState = p6opstate.NOTAVAILABLE (* SlackWireSM.ERROR *) OR TRW.OpState = p6opstate.NOTAVAILABLE OR brakeHPU.Error THEN
			;	// still error
		ELSE	// all OK
			_state := SM.IDLE;
		END_IF
	
	SM.ESTOP :
		_opstate := P6OpState.NOTAVAILABLE;
		IF NOT estop.Activated THEN
			_state := SM.IDLE;
		END_IF
	
END_CASE


]]></ST>
    </Implementation>
    <Method Name="_calcMaxSDRvelocity" Id="{340081f2-9860-49ba-ada0-7165877e05f3}">
      <Declaration><![CDATA[// output in m/s
METHOD _calcMaxSDRvelocity : lreal
VAR_INPUT
END_VAR

VAR_INST
	_output : LREAL;
END_VAR
]]></Declaration>
      <Implementation>
        <ST><![CDATA[
_output :=  (SDR_ewinch.Radius * math.PI2  * SDR.SpeedMax)/ 60 ;
	
_calcMaxSDRvelocity := _output ;
]]></ST>
      </Implementation>
    </Method>
    <Method Name="_calcSWCtension" Id="{6006d920-bc70-4b4e-b9e5-0a2da1126f2b}">
      <Declaration><![CDATA[// setpoint to SWC in ton
METHOD _calcSWCtension : LREAL 
VAR_INPUT
	TRWTension : LREAL;	// actual tension from TRW
END_VAR
VAR_INST
	_input	: LREAL;
	_output : LREAL;
END_VAR]]></Declaration>
      <Implementation>
        <ST><![CDATA[// Scale and return setpoint for SWC 
_input := TRWTension;

_output := scale(
	Value:= TRWTension,
	InputMin:= 0,
	InputMax := config.TractionwinchMaxLoadPoint,
	OutputMin := config.minimumBackTension,
	OutputMax := config.maximumBackTension, 
	LimitOutput := TRUE);
	
_calcSWCtension := _output;]]></ST>
      </Implementation>
    </Method>
    <Property Name="opState" Id="{ce1c1668-491d-03a3-38dd-d070f3b7886c}">
      <Declaration><![CDATA[{attribute 'monitoring' := 'call'}
{attribute 'TcRpcEnable'}
PROPERTY opState : p7type.P6OpState
]]></Declaration>
      <Get Name="Get" Id="{a6f01228-a0b3-0445-03f4-f16564dc5580}">
        <Declaration><![CDATA[VAR
END_VAR
]]></Declaration>
        <Implementation>
          <ST><![CDATA[opState := _opState;]]></ST>
        </Implementation>
      </Get>
    </Property>
    <Method Name="_calcSDRspeed" Id="{d1d84159-ac3a-4c73-b788-310719f80908}">
      <Declaration><![CDATA[// returns speedRef (0-1) to send to SDR
METHOD _calcSDRspeed : LREAL // 0-1 of max RPM
VAR_INPUT
	TRWVelocity : LREAL;			// m/sec
	SVCposition : LREAL; 			// 0-1
	SWCposSetpoint	: LREAL;	// (0-1) where do we want the SWC to be located
END_VAR
VAR_INST
	_TRWvelocityVector 	: LREAL;	// m/s
	_TRWspeedVector 	: LREAL;	// 0-1
	_SVCspeedVector 	: LREAL;	// 0-1
	
	_requestedDrumRPM	: LREAL;
	_temp_TRWspeedVector : LREAL;
	
END_VAR]]></Declaration>
      <Implementation>
        <ST><![CDATA[
// how much do SWC contribute to the speed. NOTE long SWC means haul in - negative result-. 
_SVCspeedVector := - (SVCposition - SWCposSetpoint) * Config.SWCpositionFeedbackFactor;

// how much do TRW velocity contribute to the speed.
_TRWvelocityVector := TRWVelocity  * config.TRWvelocityFeedbackFactor;

//  m/s divided on winch circumference * 60  should give drum RPM
_requestedDrumRPM := ( _TRWvelocityVector * 60 ) / (SDR_ewinch.Radius * math.PI2 ) ;
	
// divided on max (drum) RPM, we get the speed we want to run the winch with.. Hopefully.
_TRWspeedVector :=  _requestedDrumRPM / SDR.SpeedMax (*RPM*);

_calcSDRspeed :=LIMIT(-1, _SVCspeedVector + _TRWspeedVector, 1);]]></ST>
      </Implementation>
    </Method>
    <Method Name="_SDRstateController" Id="{f8eb9e55-7246-4f45-a2dd-9ec0bb366056}">
      <Declaration><![CDATA[METHOD PRIVATE _SDRstateController : BOOL
VAR_INPUT
END_VAR
VAR_INST
	state : INT;
	WinchInRun : BOOL;
	timerwaitFoInControl	: TON := (PT:=T#1S);
	timerwaitForRun			: TON := (PT:=T#5S);
	timerOKinRun 			: TON := (PT:=T#1S);
END_VAR
VAR
	failCounter : INT;		// just to make sure we don't try forever
END_VAR]]></Declaration>
      <Implementation>
        <ST><![CDATA[
WinchInRun := SDR_controlUnit.IO_Out.winchActiveIndicator;

timerwaitFoInControl(IN:= state = 2 );
timerwaitForRun(IN:= state = 4 );
timerOKinRun  (IN:= state = 5 );

IF SDR_Deactivate THEN // skip all and shut down
	state :=7 ;
END_IF

CASE state OF
	0:	 // idle
		IF SDR_Activate THEN
			failCounter := 0;
			state := 1;
		END_IF
		
		
	1:	failCounter := failCounter +1;
		IF failCounter > 5 THEN 
			state := 7;		
			DebugMessage('SDR Controller: Cannot activate Storagedrum in Auto','','');
		END_IF
		IF NOT SDR_controlUnit.InControl AND NOT WinchInRun THEN // winch is off (monitoring)
			SDR_controlUnit.IO_In.actionButton := TRUE;
			state :=2;
		ELSIF SDR_controlUnit.InControl AND NOT WinchInRun THEN	// already in control
			state := 3;
		ELSIF SDR_controlUnit.InControl AND WinchInRun THEN	// already in control and in run
			state := 5;
		END_IF
		
			
	2:	// release button and wait for inControl
		SDR_controlUnit.IO_In.actionButton := FALSE;
		IF SDR_controlUnit.InControl THEN
			state :=3;
		END_IF
		IF timerwaitFoInControl.Q THEN
			state := 1; // start over again
		END_IF
		
	3:	// Activate winch
		SDR_controlUnit.IO_In.actionButton := TRUE;
		state :=4;
	
	4:	// wait for winch active
		SDR_controlUnit.IO_In.actionButton := FALSE;
		IF WinchInRun THEN
			state :=5;
		END_IF
		IF timerwaitForRun.Q THEN
			state := 1; // start over again
		END_IF
		
		
	5:	IF WinchInRun = FALSE THEN		
			state := 1; // start over again
		END_IF
		IF timerOKinRun.Q THEN	// RUN is ok for 1 second
			state := 6;
		END_IF

	6: // set auto mode
		SDR_controlUnit.AutoMode := TRUE;
		SDR.AutoMode := TRUE;
		state := 0 ; // go to idle
		
	7: ;// stop all
		SDR_controlUnit.AutoMode := FALSE;
		SDR.AutoMode := FALSE;
		SDR_controlUnit.StopWinch := TRUE;
		SDR_controlUnit.ReleaseControl(-1);
		state := 0 ; // go to idle
		
END_CASE
SDR_Activate := FALSE;
SDR_Deactivate := FALSE;]]></ST>
      </Implementation>
    </Method>
  </POU>
</TcPlcObject>