﻿<?xml version="1.0" encoding="utf-8"?>
<TcPlcObject Version="1.1.0.1">
  <POU Name="LocalEDriveSimple" Id="{e630c43b-c1b6-4b04-b59d-5f9eff2fa0ee}" SpecialFunc="None">
    <Declaration><![CDATA[(*
Purpose
=======
This class is the base class for managing N inverter objects without switching motors. 

Features
========

#. Distributes torque to inverters
#. A drive is able to run with less than all assigned inverters
#. Managing the encoder counter source
#. Calculates available torque for the drive. 

Methods And Properties
======================
*)
FUNCTION_BLOCK LocalEDriveSimple EXTENDS ProtoDriveInverter IMPLEMENTS p7type.IDrive, p6com.IProtoService
VAR_INPUT
	Config 							: REFERENCE TO ConfigElectricDrive;
	{attribute 'hide'} DriveMux						: IDriveMux; // dummy
END_VAR
VAR
	startupDelay										: p6com.StartupDelay(T#6S);
	{attribute 'hide'} Inverter							: IInverter;	
	{attribute 'hide'} drvcmd							: DriveCMD;
	{attribute 'hide'} startEncoderTimer				: TON;
	{attribute 'hide'} _measuredSpeed					: LREAL;
	{attribute 'hide'} t 								: INT;
	
	{attribute 'hide'} _event 							: BOOL;						// Is true when drive is in error state 
	{attribute 'hide'} _eventId 						: p6com.EventIdentifier;		// This var stores the last reason for an error 
	{attribute 'hide'} speedInput						: LREAL;
	{attribute 'hide'} torqueRelInput					: LREAL;
		
	//
	{attribute 'hide'} N 								: DINT;
	
	// vars for properties
	{attribute 'hide'} CMD								: OneShotSignalDriveCMD;
	
	// working vars
	{attribute 'hide'} oneSecTimer						: TON;
	{attribute 'hide'} speedProcess					: LREAL;
	{attribute 'hide'} torqueRelProcess				: LREAL;
	{attribute 'hide'} speedOut						: LREAL;
	{attribute 'hide'} torqueRelOut					: LREAL;
	// added function for changing gear ratio
	currentGearRatio 	: 	LREAL;						// value copied from config based on gear
	currentGear		    :	p7type.DriveGearSet;				// current gear (0 = default)
    
	
	// State machine vars
	{attribute 'hide'} resetTimer 						: TON;
	{attribute 'hide'} inverterWaitTimer 				: TON;
	{attribute 'hide'} activeTimer 						: TON;
	{attribute 'hide'} _state 							: EDriveStateMachine := EDriveStateMachine.START;
END_VAR
// Alarms
VAR
	{attribute 'hide'} notEnoughInverters				: p6com.AlarmDigital(TC_Events.LibElectricEvtCls.WINCH_INVERTER_MISSING);
END_VAR]]></Declaration>
    <Implementation>
      <ST><![CDATA[startupDelay();

//
// Processing of set values for speed and torqueRel
//
// _torqueRelInput and _speedInput are set by setProcessValues() and then used in the statemachine.
// The statemachine is calling _setProcessValues() which sets _torqueRelProcess and _speedProcess 
//
IF NOT startupDelay.Done THEN
	RETURN;
END_IF

CASE currentGear OF
	p7type.DriveGearSet.DEFAULT:	// default
		currentGearRatio :=  Config.gearRatio;
	ELSE
		currentGearRatio := Config.gearRatio_2; 
END_CASE


_processSpeedAndTorque();
_setInverterProcessValues(speedOut, torqueRelOut);


notEnoughInverters(
		ID:=DINT_TO_UDINT(THIS^.ID), 
		IDName:= IDName, 
		TimeSpan := T#10S, 
		Value :=   
					(_getNoInvertersAvailable() <  config.numberOfInvertersMinSTD) AND
					(_state = EDriveStateMachine.WAIT_FOR_INVERTERS OR _state=EDriveStateMachine.RUN)
);


//---------------------------------------------
// Statemachine 
//---------------------------------------------
resetTimer(IN := _state = EDriveStateMachine.RESET, PT := TIME#1S);
inverterWaitTimer(IN:=_state=EDriveStateMachine.WAIT_FOR_INVERTERS, PT:=T#4S);



CASE _state OF
	EDriveStateMachine.START:
		_state := EDriveStateMachine.START_A;

	EDriveStateMachine.START_A: 
		_resetInverters();
		_state := EDriveStateMachine.START_B;
		
	EDriveStateMachine.START_B: 
		IF _getNoInvertersAvailable() >= config.numberOfInvertersMinSTD THEN
			_state := EDriveStateMachine.IDLE;
		END_IF
				
	EDriveStateMachine.IDLE:
		drvcmd := CMD.Get();
		_setProcessValues(0, config.torqueMaxRel);
		IF ( drvcmd = DriveCMD.ACTIVATE ) THEN
			_state := EDriveStateMachine.PREPARE;
		ELSIF drvcmd = DriveCMD.RESET THEN
			_state := EDriveStateMachine.RESET;			
		END_IF

	EDriveStateMachine.PREPARE: 
		_setInverterProcessValues(0, config.torqueMaxRel);
		IF drvcmd = DriveCMD.FASTSTOP THEN
			_deactivateInverter();
			_state := EDriveStateMachine.IDLE; 				
		ELSIF _getNoInvertersAvailable() >= config.numberOfInvertersMinSTD THEN
			_state := EDriveStateMachine.WAIT_FOR_INVERTERS;
			_activateInverter();
		ELSE
			_state := EDriveStateMachine.IDLE;			
		END_IF
		
	EDriveStateMachine.WAIT_FOR_INVERTERS:
		_setInverterProcessValues(0, config.torqueMaxRel);
		IF drvcmd = DriveCMD.FASTSTOP THEN
			_deactivateInverter();
			_state := EDriveStateMachine.IDLE; 				
		ELSIF inverterWaitTimer.Q THEN
			_deactivateInverter();
			_setFault(EDriveStateMachine.ERROR, p6com.EventIdentifier.DRIVE_ACTIVATION_FAILED);
		ELSIF _getNoActiveInverters() >= config.numberOfInvertersMinSTD THEN
			_state := EDriveStateMachine.RUN; 						
		END_IF
		
	EDriveStateMachine.RUN:
		drvcmd := THIS^.CMD.Get();		
		IF drvcmd = DriveCMD.FASTSTOP THEN
			_deactivateInverter();
			_state := EDriveStateMachine.IDLE; 				
		ELSIF drvcmd = DriveCMD.RESET THEN
			_state := EDriveStateMachine.RESET;
		ELSIF _getNoActiveInverters() <  config.numberOfInvertersMinSTD THEN
			_deactivateInverter();
			_setFault(EDriveStateMachine.ERROR, p6com.EventIdentifier.DRIVE_NOT_ENOUGH_MOTORS); 						
		ELSIF drvcmd = DriveCMD.STOP THEN 
			_deactivateInverter();
			_state := EDriveStateMachine.IDLE;
		ELSE
			_setProcessValues(speedInput, torqueRelInput);
		END_IF


	EDriveStateMachine.RESET:
		_resetInverters();
		_event := FALSE;
		_state := EDriveStateMachine.IDLE;
			
	EDriveStateMachine.ERROR:
		drvcmd := THIS^.CMD.Get();		
		IF _getNoActiveInverters() >= config.numberOfInvertersMinSTD THEN
			_state := EDriveStateMachine.IDLE; 			
		ELSIF _getNoInvertersAvailable() >= config.numberOfInvertersMinSTD THEN
			_state := EDriveStateMachine.START;
		ELSIF drvcmd = DriveCMD.RESET THEN
			_state := EDriveStateMachine.RESET;
			_event := FALSE;
		ELSIF NOT _event THEN	
			_state := EDriveStateMachine.IDLE;
		END_IF
		
END_CASE
]]></ST>
    </Implementation>
    <Property Name="OpState" Id="{0ae0cced-c25f-4c8a-aa69-1b9b0ce90841}">
      <Declaration><![CDATA[{attribute 'monitoring' := 'call'}
{attribute 'TcRpcEnable'}
PROPERTY OpState : p6com.P6OpState]]></Declaration>
      <Get Name="Get" Id="{dab47a2f-4ed0-4293-910e-4a98e4915970}">
        <Declaration><![CDATA[VAR
END_VAR
]]></Declaration>
        <Implementation>
          <ST><![CDATA[CASE _state OF 
	EDriveStateMachine.RUN:
		OpState := p6com.P6OpState.RUN;

	EDriveStateMachine.IDLE:
		OpState := p6com.P6OpState.STOPPED;
	
	EDriveStateMachine.PREPARE:
		OpState := p6com.P6OpState.PREPARE;

	EDriveStateMachine.WAIT_FOR_INVERTERS:
		OpState := p6com.P6OpState.PREPARE;
ELSE
	OpState := p6com.P6OpState.NOTAVAILABLE;	
END_CASE
]]></ST>
        </Implementation>
      </Get>
    </Property>
    <Method Name="isActivationPossible" Id="{27ca8784-11a1-48a0-9984-dcab9c92e0d7}">
      <Declaration><![CDATA[{attribute 'TcRpcEnable'}
METHOD isActivationPossible : BOOL
]]></Declaration>
      <Implementation>
        <ST><![CDATA[
isActivationPossible := _getNoInvertersAvailable() >= config.numberOfInvertersMinSTD;


]]></ST>
      </Implementation>
    </Method>
    <Method Name="_setInverterIdle" Id="{3149c2cf-b1e0-458e-93ff-695c40183ac6}">
      <Declaration><![CDATA[// Set inverters to idle mode
METHOD PRIVATE _setInverterIdle : BOOL
VAR_INPUT
END_VAR
VAR
	N : DINT;
END_VAR]]></Declaration>
      <Implementation>
        <ST><![CDATA[{warning disable C0371}
FOR N := 0 TO THIS^.getNoServices()-1 DO
	_getInverter(N).setOperation(InverterCMD.PAUSE);
END_FOR

_setInverterIdle := TRUE;
]]></ST>
      </Implementation>
    </Method>
    <Method Name="_getNoInvertersAvailable" Id="{41349183-690a-466c-9117-c95a93e3dc00}">
      <Declaration><![CDATA[(*
	return number of inverters acquired and OK 
*)
METHOD PRIVATE _getNoInvertersAvailable : DINT
VAR
	N 			: DINT;
	number 		: DINT;
END_VAR
]]></Declaration>
      <Implementation>
        <ST><![CDATA[number := 0;
FOR N := 0 TO THIS^.getNoServices()-1 DO
	IF _getInverter(N).State = p6com.P6State.OP THEN
		number := number + 1;
	END_IF
END_FOR
	
_getNoInvertersAvailable := number;

]]></ST>
      </Implementation>
    </Method>
    <Method Name="getSpeedMax" Id="{4190beb6-1212-48f2-8508-2af523c81189}">
      <Declaration><![CDATA[{attribute 'TcRpcEnable'}
METHOD getSpeedMax : LREAL // returns winch RPM (not motor)
]]></Declaration>
      <Implementation>
        <ST><![CDATA[getSpeedMax := config.speedMax/getGearRatio();]]></ST>
      </Implementation>
    </Method>
    <Method Name="_setInverterProcessValues" Id="{47b3eb6f-d01c-42b9-bd57-6dabd7e4ee53}">
      <Declaration><![CDATA[METHOD PRIVATE _setInverterProcessValues : BOOL
VAR_INPUT
	Speed, TorqueRel : LREAL;
END_VAR
VAR_INST
	N 					: DINT;
	inverter 			: IInverter;
	speed_updated				: LREAL;
	avgtorqueRel		: LREAL;
END_VAR]]></Declaration>
      <Implementation>
        <ST><![CDATA[
avgtorqueRel := _getAvgInverterTorqueRel();

FOR N := 0 TO THIS^.getNoServices()-1 DO
	inverter := _getInverter(N);  

	// Distribute torque so, that every inverter has a similar torque
	IF (inverter.State = p6com.P6State.OP) THEN
		IF inverter.getSpeed() > 5 THEN
			IF avgTorqueRel >= 0 THEN
				IF inverter.getTorqueRel() < (avgTorqueRel - 0.02) THEN
					speed_updated := Speed + 10;
				ELSE
					speed_updated := Speed;
				END_IF
			ELSE
				IF inverter.getTorqueRel() > (avgTorqueRel + 0.02) THEN
					speed_updated := Speed - 10;
				ELSE
					speed_updated := Speed;
				END_IF
			END_IF				
			inverter.setProcessValues(speed_updated, TorqueRel);
		ELSE
			inverter.setProcessValues(Speed, TorqueRel);		
		END_IF
	ELSE
		inverter.setProcessValues(Speed, TorqueRel);				
	END_IF
END_FOR

_setInverterProcessValues := TRUE;
]]></ST>
      </Implementation>
    </Method>
    <Method Name="_processSpeedAndTorque" Id="{51c9afe3-e132-4c8e-b916-e9738ccbd6e1}">
      <Declaration><![CDATA[(*
	This method is isolating processing steps for speed and torqueRel and stores the 
	result into the instance variables _speedOut and _torqueRelOut 
*)
METHOD PRIVATE _processSpeedAndTorque : BOOL
VAR_INST
	motorSpeed				: LREAL;
	_torqueRel				: LREAL;
END_VAR]]></Declaration>
      <Implementation>
        <ST><![CDATA[// MBARI - skip all this, use torque directly

//motorSpeed := _getAverageMotorSpeed();
//IF _measuredSpeed >= config.rpmLimiterMin THEN
//	_torqueRel := _limitSpeedByTorque(motorSpeed, torqueRelProcess);
//	_torqueRel := _calcBoostAndPush(_torqueRel);
//ELSE
//	_torqueRel := _calcBoostAndPush(THIS^.torqueRelProcess);
//END_IF

speedOut := speedProcess;
//torqueRelOut := _torqueRel;
torqueRelOut := THIS^.torqueRelProcess;
]]></ST>
      </Implementation>
    </Method>
    <Method Name="_setFault" Id="{5263b57c-1b9b-495c-bf1d-4a608db9fe53}">
      <Declaration><![CDATA[METHOD PRIVATE _setFault : BOOL
VAR_INPUT
	state : EDriveStateMachine;
	error_reason : p6com.EventIdentifier;
END_VAR
]]></Declaration>
      <Implementation>
        <ST><![CDATA[{warning disable C0371}
_state := state;
_event := TRUE;
_eventId := error_reason;
]]></ST>
      </Implementation>
    </Method>
    <Method Name="getTorqueRel" Id="{52eb4916-6688-4508-ac83-4c92972b5246}">
      <Declaration><![CDATA[{attribute 'TcRpcEnable'}
(* Returns drive torque in %, can be over '1' (not normalized)*)
METHOD getTorqueRel : LREAL
VAR_INST
	torqueRel : LREAL;
END_VAR]]></Declaration>
      <Implementation>
        <ST><![CDATA[torqueRel := _getAvgInverterTorqueRel();
getTorqueRel := - torqueRel;

]]></ST>
      </Implementation>
    </Method>
    <Method Name="getGearRatio" Id="{5c2e2caa-58bc-48bb-9564-9ae984f18d48}">
      <Declaration><![CDATA[METHOD getGearRatio : LREAL

]]></Declaration>
      <Implementation>
        <ST><![CDATA[getGearRatio := MAX(1,currentGearRatio);

]]></ST>
      </Implementation>
    </Method>
    <Method Name="_getAvgInverterTorqueRel" Id="{6651ce6f-b31d-4791-a4dc-a0c6013d062c}">
      <Declaration><![CDATA[(* This method is returning the measured average relative torque of all inverters 
drive torque in %, can be over '1' (not normalized)*)
METHOD PRIVATE _getAvgInverterTorqueRel : LREAL 
VAR_INST
	torque : LREAL;
	N, noInverters : DINT;
END_VAR
]]></Declaration>
      <Implementation>
        <ST><![CDATA[{warning disable C0371}

torque := 0.0;
noInverters := _getNoInvertersAvailable();
IF noInverters > 0 THEN 
	FOR N := 0 TO THIS^.getNoServices()-1 DO
		IF _getInverter(N).State = p6com.P6State.OP THEN 
			torque := torque + _getInverter(N).getTorqueRel();
		END_IF
	END_FOR
	_getAvgInverterTorqueRel := torque / DINT_TO_LREAL(noInverters);
ELSE
	_getAvgInverterTorqueRel := 0.0;	
END_IF

]]></ST>
      </Implementation>
    </Method>
    <Method Name="getInverterArray" Id="{6f8cadd6-1bfe-0570-1696-cc804308ed74}">
      <Declaration><![CDATA[{attribute 'TcRpcEnable'}
METHOD getInverterArray : REFERENCE TO p7type.DriveIInverterArray
VAR_INST
	N : DINT;
	inverters : p7type.DriveIInverterArray;	
END_VAR
]]></Declaration>
      <Implementation>
        <ST><![CDATA[FOR N := 0 TO THIS^.getNoServices()-1 DO
	inverters[N] := _getInverter(N);
END_FOR

getInverterArray REF= inverters;
]]></ST>
      </Implementation>
    </Method>
    <Method Name="getTorqueMax" Id="{748f700b-0a1e-421d-be46-dd7c0d5cb309}">
      <Declaration><![CDATA[{attribute 'TcRpcEnable'}
(*Returns config torque max in Nm*)
METHOD getTorqueMax : LREAL
VAR_INST
	noInverter : DINT;
	noInvertersMax : DINT;
END_VAR]]></Declaration>
      <Implementation>
        <ST><![CDATA[// This is torque max of a drive and not of an inverter
noInverter := _getNoInvertersAvailable();
noInvertersMax := THIS^.getNoServices();

IF noInvertersMax > 0 THEN
	getTorqueMax := MAX(config.torqueMax * TO_LREAL(noInverter) / TO_LREAL(noInvertersMax), 1);
ELSE
	getTorqueMax := 1;
END_IF

]]></ST>
      </Implementation>
    </Method>
    <Property Name="ID" Id="{7c774be6-aed1-4af9-bfb5-de2ed5cdab17}">
      <Declaration><![CDATA[{attribute 'monitoring' := 'call'}
{attribute 'TcRpcEnable'}
PROPERTY ID : DINT]]></Declaration>
      <Set Name="Set" Id="{42de4460-cb7b-4d92-81ea-aff0dccb6a29}">
        <Declaration><![CDATA[VAR
END_VAR
]]></Declaration>
        <Implementation>
          <ST><![CDATA[IF __ISVALIDREF(config) THEN
	config.ID := ID;
END_IF
]]></ST>
        </Implementation>
      </Set>
      <Get Name="Get" Id="{dc541085-2b56-48be-bbb9-f2d385c86f6e}">
        <Declaration><![CDATA[VAR
END_VAR
]]></Declaration>
        <Implementation>
          <ST><![CDATA[IF __ISVALIDREF(config) THEN
	ID := config.ID;
ELSE
	ID := 0;
END_IF
]]></ST>
        </Implementation>
      </Get>
    </Property>
    <Method Name="_getSpeedMeasured" Id="{8203d298-067f-473e-a2c6-a40c649cb463}">
      <Declaration><![CDATA[// This method returns the average measured speed of all inverters after the gear
METHOD PRIVATE _getSpeedMeasured : LREAL
VAR 
	N, noInverters 	: DINT;
	speedInv 		: LREAL;
END_VAR]]></Declaration>
      <Implementation>
        <ST><![CDATA[{warning disable C0371}
noInverters := 0;
FOR N := 0 TO THIS^.getNoServices()-1 DO
	// For calculation of the avg speed
	IF _getInverter(N).State = p6com.P6State.OP  THEN
		speedInv := speedInv + _getInverter(N).getSpeed();
		noInverters := noInverters + 1;	
	END_IF	
END_FOR

IF (noInverters > 0)  THEN
	_getSpeedMeasured := speedInv / DINT_TO_LREAL(noInverters) / getGearRatio();
ELSE
	_getSpeedMeasured := 0.0;
END_IF
		
]]></ST>
      </Implementation>
    </Method>
    <Method Name="_activateInverter" Id="{85a9df8d-3180-46f3-b770-7e24ff21b71a}">
      <Declaration><![CDATA[METHOD PRIVATE _activateInverter : BOOL
VAR
	N : DINT;
END_VAR]]></Declaration>
      <Implementation>
        <ST><![CDATA[FOR N := 0 TO THIS^.getNoServices()-1 DO
	IF _getInverter(N).State = p6com.P6State.OP THEN
		_getInverter(N).setOperation(InverterCMD.ACTIVATE);
	END_IF;
END_FOR


]]></ST>
      </Implementation>
    </Method>
    <Method Name="_getNoActiveInverters" Id="{8e69d57e-0031-4ecc-8052-2b587f2a5553}">
      <Declaration><![CDATA[(*
	return number of running inverters 
*)
METHOD PRIVATE _getNoActiveInverters : DINT
VAR
	N 				: DINT;
	noInverters 	: DINT; 
END_VAR]]></Declaration>
      <Implementation>
        <ST><![CDATA[noInverters := 0;
FOR N := 0 TO THIS^.getNoServices()-1 DO
	IF _getInverter(N).OpState = p6com.P6OpState.RUN THEN 
		noInverters := noInverters + 1;
	END_IF 
END_FOR

_getNoActiveInverters := noInverters;
]]></ST>
      </Implementation>
    </Method>
    <Method Name="_calcBoostAndPush" Id="{979293fa-7747-4fe3-b594-da4dad3c32ec}">
      <Declaration><![CDATA[(*
	Calculate torque in the case motors are missing and calculate boosting *) 
METHOD PRIVATE _calcBoostAndPush : LREAL
VAR_INPUT
	TorqueRelUser : LREAL;
END_VAR
VAR_INST
	N					: UDINT;
	torque	 			: LREAL;
	boostCounter 		: UDINT;		// Max time to boost
	boostCycle 			: UDINT;		// cyle time which includes boostCounter
	wantBoosting		: BOOL;
	wantedBoostTimer	: TON;
	tickTimer			: p6com.CycleGenerator;
	state 				: DriveBoostSM;
END_VAR
]]></Declaration>
      <Implementation>
        <ST><![CDATA[tickTimer.update(IN := T#100MS, PLCTask_CycleTime := p6com.CycleTime_Task0);
	
torque :=  LIMIT(0.0, TorqueRelUser, config.torqueMaxRel);

// This is the section for boosting
wantedBoostTimer(
	IN := (getTorqueRel() >= config.torqueRelBoostStart) AND config.option_Boosting AND  (THIS^.OpState = p6com.P6OpState.RUN),
	PT := T#100MS, Q => wantBoosting
);

	
// The idea to allow boosting for boostTime in a time range of a boostCycle (boostTime < boostCycle).
// The cycle is floating forwards in time
CASE state OF
	DriveBoostSM.NOT_BOOSTING :
		IF wantBoosting AND (THIS^.OpState = p6com.P6OpState.RUN) THEN
			state := DriveBoostSM.INIT;
		END_IF;
		
	DriveBoostSM.INIT: 
		boostCounter := MAX(1, TIME_TO_UDINT(config.boostTime/100));	
		boostCycle := MAX(1, TIME_TO_UDINT(config.boostCycle/100));
		state := DriveBoostSM.BOOSTING;
		
	DriveBoostSM.BOOSTING:
		// Boosting reduces both times 
		IF (NOT wantBoosting) AND tickTimer.Q THEN
			state := DriveBoostSM.PAUSE;
		ELSIF (boostCounter > 0) AND tickTimer.Q THEN
			boostCounter := MAX(boostCounter - 1, 0);
			boostCycle 	 := MAX(boostCycle - 1, 0);
		ELSIF (boostCounter <= 0) AND tickTimer.Q THEN
			state := DriveBoostSM.WAIT_FOR_NEXT_CYCLE;
		END_IF;	
				 
	DriveBoostSM.PAUSE:
		IF NOT (THIS^.OpState = p6com.P6OpState.RUN) THEN
			state := DriveBoostSM.NOT_BOOSTING;			
		ELSIF (NOT wantBoosting) AND tickTimer.Q THEN
			boostCounter := MIN(boostCounter + 1, TIME_TO_UDINT(config.boostTime)/100);
			boostCycle 	 := MIN(boostCycle + 1, TIME_TO_UDINT(config.boostCycle)/100);
		ELSIF (NOT wantBoosting) AND tickTimer.Q AND boostCycle>=(TIME_TO_UDINT(config.boostCycle)/100) THEN
			state := DriveBoostSM.NOT_BOOSTING; 
		ELSIF wantBoosting THEN
			state := DriveBoostSM.BOOSTING;
		END_IF
	
	DriveBoostSM.WAIT_FOR_NEXT_CYCLE:
		IF NOT (THIS^.OpState = p6com.P6OpState.RUN) THEN
			state := DriveBoostSM.NOT_BOOSTING;			
		ELSIF  tickTimer.Q AND (boostCycle > 0) THEN
			boostCycle := MAX(boostCycle - 1, 0);
		ELSIF   tickTimer.Q AND (boostCycle <= 0) THEN
			state := DriveBoostSM.NOT_BOOSTING;
		END_IF;
END_CASE

IF state = DriveBoostSM.BOOSTING THEN
	torque := MIN(TorqueRelUser, config.torqueMaxRel); // Boosting
ELSE
	IF THIS^.Config.option_Boosting THEN
		torque := MIN(TorqueRelUser, config.torqueRelBoostStart); //Limit to boost range start
	ELSE
		torque := MIN(TorqueRelUser, config.torqueMaxRel);	
	END_IF;				
END_IF

_calcBoostAndPush := torque;
	
]]></ST>
      </Implementation>
    </Method>
    <Method Name="getTorque" Id="{a6870651-786b-4789-b87b-c2031a694462}">
      <Declaration><![CDATA[{attribute 'TcRpcEnable'}
(* returns actual torque in Nm *)
METHOD getTorque : LREAL
VAR_INST
	torqueMax : LREAL;
	averageTorqueRel : LREAL;
	torqueRelMax : LREAL;
	noOfInverters : LREAL;
	noOfInvertersAvailable : LREAL;
END_VAR]]></Declaration>
      <Implementation>
        <ST><![CDATA[
torqueMax := getTorqueMax();
averageTorqueRel := _getAvgInverterTorqueRel();
torqueRelMax := getTorqueRelMax();

IF torqueMax < 1 THEN
	getTorque := 1;
ELSE
	getTorque := - averageTorqueRel / torqueRelMax * torqueMax;
END_IF
]]></ST>
      </Implementation>
    </Method>
    <Method Name="getSpeed" Id="{aba1578b-c757-46b7-bad1-6572eb731d93}">
      <Declaration><![CDATA[{attribute 'TcRpcEnable'}
METHOD getSpeed : LREAL
]]></Declaration>
      <Implementation>
        <ST><![CDATA[getSpeed := _getSpeedMeasured();
]]></ST>
      </Implementation>
    </Method>
    <Property Name="Event" Id="{b0711928-c1ec-4ec9-9fda-1edd932cef7d}">
      <Declaration><![CDATA[{attribute 'monitoring' := 'call'}
{attribute 'TcRpcEnable'}
PROPERTY Event : BOOL
]]></Declaration>
      <Get Name="Get" Id="{845d76dd-b93a-4f6d-81ca-d75fb8f291b6}">
        <Declaration><![CDATA[]]></Declaration>
        <Implementation>
          <ST><![CDATA[Event := THIS^._event;]]></ST>
        </Implementation>
      </Get>
    </Property>
    <Method Name="_setProcessValues" Id="{b33d9946-b93b-4a13-a660-eb844774e5b3}">
      <Declaration><![CDATA[METHOD PRIVATE _setProcessValues : BOOL
VAR_INPUT
	Speed 		: LREAL;
	TorqueRel 	: LREAL;
END_VAR
]]></Declaration>
      <Implementation>
        <ST><![CDATA[THIS^.speedProcess := Speed;

THIS^.torqueRelProcess := TorqueRel;


]]></ST>
      </Implementation>
    </Method>
    <Method Name="setProcessValues" Id="{b9af6634-60b5-44c6-a9ad-a16067b5d81a}">
      <Declaration><![CDATA[{attribute 'TcRpcEnable'}
METHOD PUBLIC setProcessValues : BOOL
VAR_INPUT
	Speed : LREAL;
	TorqueRel : LREAL;
END_VAR
VAR
	limitGearRatio : LREAL;
	limitSpeed : LREAL;
END_VAR]]></Declaration>
      <Implementation>
        <ST><![CDATA[
limitGearRatio := getGearRatio();
limitSpeed := LIMIT(-config.speedMax/limitGearRatio, Speed, config.speedMax/limitGearRatio);
 
speedInput := limitSpeed*getGearRatio();

//torqueRelInput := LIMIT(0, TorqueRel, config.torqueMaxRel);
torqueRelInput := LIMIT( -config.torqueMaxRel, TorqueRel, config.torqueMaxRel);



]]></ST>
      </Implementation>
    </Method>
    <Method Name="isAtZeroSpeed" Id="{c13d814e-3a4d-42e7-9ed2-e7c71616b82b}">
      <Declaration><![CDATA[{attribute 'TcRpcEnable'}
METHOD isAtZeroSpeed : BOOL
VAR_INST
	N : DINT;
	inverter : IInverter;
END_VAR]]></Declaration>
      <Implementation>
        <ST><![CDATA[isAtZeroSpeed := TRUE;
FOR N := 0 TO THIS^.getNoServices()-1 DO
	inverter := _getInverter(N);
	IF inverter.State = p6com.P6State.OP AND (NOT inverter.isAtZeroSpeed()) THEN
		isAtZeroSpeed := FALSE;
		EXIT;
	END_IF
END_FOR
]]></ST>
      </Implementation>
    </Method>
    <Property Name="State" Id="{c97178d0-036f-45ff-8212-edfcc3451ba5}">
      <Declaration><![CDATA[{attribute 'monitoring' := 'call'}
{attribute 'TcRpcEnable'}
PROPERTY State : p6com.P6State]]></Declaration>
      <Get Name="Get" Id="{ed953c21-a7b0-4e19-aaf7-8d174a201dc2}">
        <Declaration><![CDATA[]]></Declaration>
        <Implementation>
          <ST><![CDATA[IF (_state >= EDriveStateMachine.IDLE) AND (_state < EDriveStateMachine.RESET) THEN
	State := p6com.P6State.OP;
ELSIF (_state >= EDriveStateMachine.RESET) THEN
	State := p6com.P6State.PREOP;
ELSE
	State := p6com.P6State.INIT;		
END_IF]]></ST>
        </Implementation>
      </Get>
    </Property>
    <Property Name="IDName" Id="{cdd470b3-56fb-4a1c-83b8-f4e188d83612}">
      <Declaration><![CDATA[{attribute 'monitoring' := 'call'}
{attribute 'TcRpcEnable'}
PROPERTY IDName : p7type.P6IdName]]></Declaration>
      <Set Name="Set" Id="{ce1adf00-132f-4355-b441-ccfcc4869dc0}">
        <Declaration><![CDATA[VAR
END_VAR
]]></Declaration>
        <Implementation>
          <ST><![CDATA[IF __ISVALIDREF(config) THEN
	config.name := IDName;
END_IF
]]></ST>
        </Implementation>
      </Set>
      <Get Name="Get" Id="{de4cf3d8-df4e-46f5-af77-44fe9adb4caf}">
        <Declaration><![CDATA[VAR
END_VAR
]]></Declaration>
        <Implementation>
          <ST><![CDATA[IF __ISVALIDREF(config) THEN
	IDName := config.name;
ELSE
	IDName := '';
END_IF
]]></ST>
        </Implementation>
      </Get>
    </Property>
    <Method Name="_getInverter" Id="{cfade7cc-68b9-44c7-b454-776bf3507948}">
      <Declaration><![CDATA[METHOD PRIVATE _getInverter : IInverter
VAR_INPUT
	Index			: DINT;
END_VAR
VAR
	inverter		: IInverter;	
END_VAR]]></Declaration>
      <Implementation>
        <ST><![CDATA[__QUERYINTERFACE(THIS^.getServiceByIdx(Index), inverter);
_getInverter := inverter;
]]></ST>
      </Implementation>
    </Method>
    <Method Name="setOperation" Id="{d59f7e84-c7e0-49fd-ac15-23944ebbdb5d}">
      <Declaration><![CDATA[{attribute 'TcRpcEnable'}
METHOD setOperation : BOOL
VAR_INPUT
	CMD : p7type.DriveCMD;
END_VAR
]]></Declaration>
      <Implementation>
        <ST><![CDATA[THIS^.CMD.Set(CMD);

]]></ST>
      </Implementation>
    </Method>
    <Method Name="_deactivateInverter" Id="{d9e2a177-2a36-4c1d-89c3-bc657fb4862d}">
      <Declaration><![CDATA[METHOD PRIVATE _deactivateInverter : BOOL
VAR
	N 		 : DINT;
END_VAR
]]></Declaration>
      <Implementation>
        <ST><![CDATA[FOR N := 0 TO THIS^.getNoServices()-1 DO
	IF _getInverter(N).State = p6com.P6State.OP THEN 
		_getInverter(N).setOperation(InverterCMD.STOP);
	END_IF;
END_FOR





]]></ST>
      </Implementation>
    </Method>
    <Method Name="setGearSet" Id="{e2a94b59-79ca-047c-17cd-3bb4ce3461fc}">
      <Declaration><![CDATA[{attribute 'TcRpcEnable'}
METHOD setGearSet : BOOL		// selects one of two gear ratios from edrive config (0: default)
VAR_INPUT
	GearSet : p7type.DriveGearSet ;
END_VAR
]]></Declaration>
      <Implementation>
        <ST><![CDATA[
currentGear := GearSet;]]></ST>
      </Implementation>
    </Method>
    <Method Name="_limitSpeedByTorque" Id="{ef73985d-f672-43db-ae59-5228a86caee8}">
      <Declaration><![CDATA[(*
	Returns a quadratic raising torque when speed > config.rpmLimiterMin
*)
METHOD _limitSpeedByTorque : LREAL
VAR_INPUT
	MotorRPM 				: LREAL;
	TorqueRelDefault 	: LREAL;
END_VAR
]]></Declaration>
      <Implementation>
        <ST><![CDATA[IF config.rpmLimiterMin > 0 AND MotorRPM > config.rpmLimiterMin THEN
	IF torqueRelProcess < config.torqueMaxRel THEN
		_limitSpeedByTorque := torqueRelProcess + (config.torqueMaxRel - torqueRelProcess) *  
						(ABS(MotorRPM) - config.rpmLimiterMin) / MAX(1,(config.rpmLimiterMax - config.rpmLimiterMin)) ;		
	ELSE
		_limitSpeedByTorque := config.torqueMaxRel;
	END_IF
ELSE
	_limitSpeedByTorque := TorqueRelDefault;
END_IF
]]></ST>
      </Implementation>
    </Method>
    <Method Name="getTorqueRelMax" Id="{f04779a4-f2ea-46e8-a88e-fbd2cd197d64}">
      <Declaration><![CDATA[{attribute 'TcRpcEnable'}
(* Returns config value for max torque rel in %, can be over '1' (not normalized)*)
METHOD getTorqueRelMax : LREAL
]]></Declaration>
      <Implementation>
        <ST><![CDATA[getTorqueRelMax := THIS^.Config.torqueMaxRel;
]]></ST>
      </Implementation>
    </Method>
    <Property Name="EventId" Id="{f3ef3fe7-3b69-42d8-94f4-c359ad2c35ff}">
      <Declaration><![CDATA[{attribute 'monitoring' := 'call'}
{attribute 'TcRpcEnable'}
PROPERTY EventId : p7type.EventIdentifier
]]></Declaration>
      <Get Name="Get" Id="{0b5473ca-15ce-4321-ab04-142e9adc5320}">
        <Declaration><![CDATA[]]></Declaration>
        <Implementation>
          <ST><![CDATA[EventId := _eventId;]]></ST>
        </Implementation>
      </Get>
    </Property>
    <Method Name="_resetInverters" Id="{f4a685c5-16b3-4dfd-a8cb-4f9af9f1116f}">
      <Declaration><![CDATA[// Reset all acquired inverters
METHOD PRIVATE _resetInverters : BOOL
VAR_INPUT
END_VAR
VAR
	N : DINT;
	inverter : IInverter;
END_VAR]]></Declaration>
      <Implementation>
        <ST><![CDATA[{warning disable C0371}
FOR N := 0 TO THIS^.getNoServices()-1 DO
	inverter := _getInverter(N);
 	IF inverter.State <> p6com.P6State.OP THEN
		inverter.setOperation(InverterCMD.RESET);
	END_IF
END_FOR

_resetInverters := TRUE;
]]></ST>
      </Implementation>
    </Method>
    <Method Name="_getAverageMotorSpeed" Id="{ffcc7cd1-e7f0-0dd0-285c-bd8ac4b5707c}">
      <Declaration><![CDATA[// This method returns the average measured speed of all inverters after the gear
METHOD PRIVATE _getAverageMotorSpeed : LREAL
VAR 
	N, noInverters 	: DINT;
	speedInv 		: LREAL;
END_VAR]]></Declaration>
      <Implementation>
        <ST><![CDATA[{warning disable C0371}
noInverters := 0;
FOR N := 0 TO THIS^.getNoServices()-1 DO
	// For calculation of the avg speed
	IF _getInverter(N).State = p6com.P6State.OP  THEN
		speedInv := speedInv + _getInverter(N).getSpeed();
		noInverters := noInverters + 1;	
	END_IF	
END_FOR

IF (noInverters > 0) THEN
	_getAverageMotorSpeed := speedInv / DINT_TO_LREAL(noInverters);
ELSE
	_getAverageMotorSpeed := 0.0;
END_IF
		
]]></ST>
      </Implementation>
    </Method>
  </POU>
</TcPlcObject>