<?xml version="1.0" encoding="utf-8"?>
<TcPlcObject Version="1.1.0.1" ProductVersion="3.1.4026.8">
  <POU Name="FB_Unit" Id="{e9bb815b-eb46-4920-800d-910484e58b22}" SpecialFunc="None">
    <Declaration><![CDATA[FUNCTION_BLOCK FB_Unit
VAR_INPUT
	// Unit configuration
	stUnitConfig : REFERENCE TO ST_UNIT_CONFIG;
	
	// HMI interface
	stHMIInterface : REFERENCE TO ST_UNIT_HMI_INTERFACE;
	
	// Start unit
	xEnable : BOOL;
	
	// Star balancing
	xStartBalancing : BOOL;
	
	// Unit in safety check mode
	xInSafetyCheckMode : BOOL;
	
	// String operation mode
	eStringOperatingMode : E_STRING_OPERATING_MODE;
	
	// Emergency stop ok
	xEmergencyStopOk : BOOL;
	
	// All safetyinterlocks are ok
	xSafetyIntlksOk : BOOL;
	
	// Release alarms
	xReleaseErrors : BOOL;
	
	// Release analog io limit errors
	xReleaseLimitErrors : BOOL;
	
	// Release manual mode
	xReleaseManualMode : BOOL;
	
	// Input to confirm all errors
	xConfirmAlarms : BOOL;
	
	// Balancing target Voltage
	rBalancingTargetVoltage : REAL;
	
	// Switch all components to manual mode
	xAllToManualMode : BOOL;
	
	// Inverter enabled status
	xInverterEnabled : BOOL;
END_VAR
VAR_IN_OUT
	// Modbus warning register
	stModbusWarnings : U_UNIT_WARNING_REGISTER;
	
	// Modbus error register
	stModbusErrors : U_UNIT_ERROR_REGISTER;
END_VAR
VAR_OUTPUT
	// Unit state
	//eUnitState : E_COMPONENT_STATUS := E_COMPONENT_STATUS.OFF;
	
	// Unit ready
	xReady : BOOL;
	
	// Unit completely off
	xOff : BOOL := TRUE;
	
	// Unit can be discharged during shutdown sequence
	xShutdownDischargeAllowed : BOOL;
	
	// Unit in shutdown segment discharge mode
	xInShutdownDischargeMode : BOOL;

	// Error active
	xError : BOOL;
	
	// Warning active
	xWarning : BOOL;
	
	// Current Unit voltage
	rCurrentVoltage : REAL;
	
	// All components in automatic mode
	xAllComponentsInAuto : BOOL;
	
	// Balancing done
	xBalancingDone : BOOL;
	
	// Reset MCB
	xResetMCB AT %Q* : BOOL;
END_VAR
VAR
	// Check unit condition after some time during startup
	_timUnitStartupWaitTime : TIME := T#1M;	

	// Valves posolyt
	_fbPosolytValveTankOutlet : FB_Valve(CONCAT(_sName, ' - Posolyt tank outlet'));
	
	// Valves negolyt
	_fbNegolytValveTankOutlet : FB_Valve(CONCAT(_sName, ' - Negolyt tank outlet'));

	// Pumps posolyt
	_fbPosolytPumpInlet : FB_MotorAnalog(CONCAT(_sName, ' - Posolyt segment inlet'));
	
	// Pumps negolyt
	_fbNegolytPumpInlet : FB_MotorAnalog(CONCAT(_sName, ' - Negolyt segment inlet'));

	// Pressure sensors posolyt
	_fbPressurePosolytSegmentInlet : FB_AnalogInput(CONCAT(_sName, ' - P1_P'));
	_fbPressurePosolytTankInlet : FB_AnalogInput(CONCAT(_sName, ' - P3_P'));
	
	// Pressure sensors negolyt
	_fbPressureNegolytSegmentInlet : FB_AnalogInput(CONCAT(_sName, ' - P1_N'));
	_fbPressureNegolytTankInlet : FB_AnalogInput(CONCAT(_sName, ' - P3_N'));
	
	// Temperature sensor posolyt
	_fbTempSensorPosolyt : FB_AnalogInput(CONCAT(Name, ' - T1_P'));
	
	// Temperature sensor negolyt
	_fbTempSensorNegolyt : FB_AnalogInput(CONCAT(Name, ' - T1_N'));
	
	// Unit voltage
	_fbVoltageSegment : FB_AnalogInput('Voltage');
	
	// Valve posolyt tank outlet interlocks
	_stPosolytValveTankOutletPIntlk : T_INTERLOCK;
	_stPosolytValveTankOutletPIntlkUsed : T_INTERLOCK := 2#0000_0000_0000_0000;
	
	_stPosolytValveTankOutletSIntlk : T_INTERLOCK;
	_stPosolytValveTankOutletSIntlkUsed : T_INTERLOCK := 2#0000_0000_0000_0001;
	
	// Valve negolyt tank outlet interlocks
	_stNegolytValveTankOutletPIntlk : T_INTERLOCK;
	_stNegolytValveTankOutletPIntlkUsed : T_INTERLOCK := 2#0000_0000_0000_0000;
	
	_stNegolytValveTankOutletSIntlk : T_INTERLOCK;
	_stNegolytValveTankOutletSIntlkUsed : T_INTERLOCK := 2#0000_0000_0000_0001;
	
	// Pump posolyt inlet interlocks
	_stPosolytPumpInletPIntlk : T_INTERLOCK;
	_stPosolytPumpInletPIntlkUsed : T_INTERLOCK := 2#0000_0000_0000_0001;
	_stPosolytPumpInletSIntlk : T_INTERLOCK;
	_stPosolytPumpInletSIntlkUsed : T_INTERLOCK := 2#0000_0000_0000_0111;
	
	// Pump negolyt inlet interlocks
	_stNegolytPumpInletPIntlk : T_INTERLOCK;
	_stNegolytPumpInletPIntlkUsed : T_INTERLOCK := 2#0000_0000_0000_0001;
	_stNegolytPumpInletSIntlk : T_INTERLOCK;
	_stNegolytPumpInletSIntlkUsed : T_INTERLOCK := 2#0000_0000_0000_0111;
	
	// Error active
	_xErrorActive : BOOL;
	
	// Warning active
	_xWarningActive : BOOL;
	
	// Shutdown discharge allowed voltage
	_rShutdownDischargeVoltageThreshold : REAL;
	
	// Current state
	_iState: INT;
	
	// All components in automatic mode
	_xAllComponentsInAutomatic : BOOL;
	
	// Not all components in automatic mode alarm
	_fbNotAllAutomaticAlarm : FB_TcAlarm;
	
	// Pressure difference between pos and neg inlet to big
	_fbPressureDiffToBig : FB_TcAlarm;
	
	// General unit error
	_fbUnitError : FB_TcAlarm;
	
	// Unit name
	_sName : STRING;
	
	// Unit startup check timer
	_tonStartupCheck : TON;
	
	// Enable voltage limit checks
	_xEnableVoltageLimitChecks : BOOL;
	
	// Manual mode allowed flag
	_xReleaseManualMode : BOOL;
	
	_xReleaseSignalDeltaPSegment : FB_ReleaseSignal;
	_xDeltaPSegmentInletToHigh : BOOL;
	_xEnableCheckForDeltaPSegmentInlet : BOOL;
	_xErrorDeltaPSegmentInlet : BOOL;
	
	// Setpoint posolyt pump inlet
	_rSetpointPosolytPumpInlet : REAL;
	
	// Setpoint negolyt pump inlet
	_rSetpointNegolytPumpInlet : REAL;
	
	// Timer for resetting the MCB (minimum on time 0,5s)
	_tofResetMCB : TOF := (PT := T#0.75S);
	
	// Timeout Voltage not reached
	_tonVoltageCheckTimeput : TON := (PT := T#2M);
	
	// Freezed balancing target voltage
	_rBalancingTargetVoltage : REAL;
	
	// Trigger for switching to manual mode
	_rtrigSwitchToManualMode : R_TRIG;
	
	// Trigger for switching to automatic mode
	_rtrigSwitchToAutoMode : R_TRIG;
	_tonManualMode : TON;
	
	// Filtered pressure sensor posolyt segment inlet data for HMI
	_rFilteredPressPosolytInlet : REAL;
	
	// Filtered pressure sensor posolyt segment inlet data for HMI
	_rFilteredPressNegolytInlet : REAL;
	
	// Filtered pressure sensor posolyt tank inlet data for HMI
	_rFilteredPressPosolytTank : REAL;
	
	// Filtered pressure sensor posolyt tank inlet data for HMI
	_rFilteredPressNegolytTank : REAL;
	
	// Pumps ready
	_xPumpsReady : BOOL;
	
	// Indicate if it ise the first cycle
	xFirstCycle : BOOL := TRUE;
END_VAR
]]></Declaration>
    <Implementation>
      <ST><![CDATA[IF xFirstCycle THEN
	_fbPosolytValveTankOutlet.Name := CONCAT(_sName, ' - Posolyt tank outlet');
	stHMIInterface.stNS12.sName := 'Posolyt tank outlet';
	
	_fbNegolytValveTankOutlet.Name := CONCAT(_sName, ' - Negolyt tank outlet');
	stHMIInterface.stNS22.sName := 'Negolyt tank outlet';

	_fbPosolytPumpInlet.Name := CONCAT(_sName, ' - Posolyt segment inlet');
	stHMIInterface.stNS11.sName := 'Posolyt segment inlet';
	
	_fbNegolytPumpInlet.Name := CONCAT(_sName, ' - Negolyt segment inlet');
	stHMIInterface.stNS21.sName := 'Negolyt segment inlet';
	
	_fbPressurePosolytSegmentInlet.Name := CONCAT(_sName, ' - Pressure Posolyt Segment Inlet');
	_fbPressurePosolytTankInlet.Name := CONCAT(_sName, ' - Pressure Posolyt Tank inlet');
	
	_fbPressureNegolytSegmentInlet.Name := CONCAT(_sName, ' - Pressure Negolyt Segment Inlet');
	_fbPressureNegolytTankInlet.Name := CONCAT(_sName, ' - Pressure Negolyt Tank Inlet');
	
	_fbTempSensorPosolyt.Name := CONCAT(_sName, ' - Temperature Posolyt Tank Inlet');
	_fbTempSensorNegolyt.Name := CONCAT(_sName, ' - Temperature Negolyt Tank Inlet');
	
	_fbVoltageSegment.Name := CONCAT(_sName, ' - Voltage');
	
	_fbUnitError.ipArguments.Clear().AddString(_sName);
	
	_fbPressureDiffToBig.CreateEx(stEventEntry := TC_EVENTS.General.PressureDiffToBig, TRUE, 0);
	_fbPressureDiffToBig.ipArguments.Clear().AddString(_sName);
	
	xFirstCycle := FALSE;
END_IF


// Manual mode trigger
_tonManualMode(IN := (xAllToManualMode AND xReleaseManualMode), PT := T#10S);
_rtrigSwitchToManualMode(CLK := _tonManualMode.Q);
IF _rtrigSwitchToManualMode.Q THEN
	_fbPosolytValveTankOutlet.ReqManualMode();
	_fbNegolytValveTankOutlet.ReqManualMode();
	_fbPosolytPumpInlet.ReqManualMode();
	_fbNegolytPumpInlet.ReqManualMode();
END_IF

// Auto mode trigger
_rtrigSwitchToAutoMode(CLK := (NOT _tonManualMode.Q));
IF _rtrigSwitchToAutoMode.Q THEN
	_fbPosolytValveTankOutlet.ReqAutomaticMode();
	_fbNegolytValveTankOutlet.ReqAutomaticMode();
	_fbPosolytPumpInlet.ReqAutomaticMode();
	_fbNegolytPumpInlet.ReqAutomaticMode();
END_IF

// Reset MCB logic
_tofResetMCB(IN := xConfirmAlarms);
xResetMCB := _tofResetMCB.Q;

// Reset error active
_xErrorActive := FALSE;

// Reset warning active
_xWarningActive := FALSE;

// Reset safety interlocks flag
xSafetyIntlksOk := TRUE;

// Copy release manual mode
_xReleaseManualMode := xReleaseManualMode;

// ===============================
// Valve posolyt tank outlet
// ===============================

// Safety Interlocks
stHMIInterface.stNS12.stInterlock.asSafetyINTLKName[0] := 'Emergency stop ok';
_stPosolytValveTankOutletSIntlk.0 := xEmergencyStopOk;

// Valve fb
_fbPosolytValveTankOutlet(
	xReleaseManualMode:= _xReleaseManualMode, 
	wProcessINTLK:= _stPosolytValveTankOutletPIntlk, 
	wProcessINTLKUsed:= _stPosolytValveTankOutletPIntlkUsed, 
	wSafetyINTLK:= _stPosolytValveTankOutletSIntlk, 
	wSafetyINTLKUsed:= _stPosolytValveTankOutletSIntlkUsed, 
	xConfirmAlarms:= xConfirmAlarms, 
	xReleaseErrors:= xReleaseErrors, 
	stValveConfig:= stUnitConfig.stConfigPosolytValve, 
	stHMIInterface:= stHMIInterface.stNS12);


// Set error active if fb has error
IF _fbPosolytValveTankOutlet.xError THEN
	stModbusErrors.stBitmap.bValvePos := 1;
	_xErrorActive := TRUE;
END_IF


// Set safety interlock flag if fb has safety interlocks active
IF NOT stHMIInterface.stNS12.stInterlock.xSafetyINTLKOk THEN
	xSafetyIntlksOk := FALSE;
END_IF


// ===============================
// Valve negolyt tank outlet
// ===============================

// Safety Interlocks
stHMIInterface.stNS22.stInterlock.asSafetyINTLKName[0] := 'Emergency stop ok';
_stNegolytValveTankOutletSIntlk.0 := xEmergencyStopOk;

// Valve fb
_fbNegolytValveTankOutlet(
	xReleaseManualMode:= _xReleaseManualMode, 
	wProcessINTLK:= _stNegolytValveTankOutletPIntlk, 
	wProcessINTLKUsed:= _stNegolytValveTankOutletPIntlkUsed, 
	wSafetyINTLK:= _stNegolytValveTankOutletSIntlk, 
	wSafetyINTLKUsed:= _stNegolytValveTankOutletSIntlkUsed, 
	xConfirmAlarms:= xConfirmAlarms, 
	xReleaseErrors:= xReleaseErrors, 
	stValveConfig:= stUnitConfig.stConfigNegolytValve, 
	stHMIInterface:= stHMIInterface.stNS22);

// Set error active if fb has error
IF _fbNegolytValveTankOutlet.xError THEN
	stModbusErrors.stBitmap.bValveNeg := 1;
	_xErrorActive := TRUE;
END_IF

// Set safety interlock flag if fb has safety interlocks active
IF NOT stHMIInterface.stNS22.stInterlock.xSafetyINTLKOk THEN
	xSafetyIntlksOk := FALSE;
END_IF


// ===============================	
// Pump posolyt segment inlet
// ===============================

// Safety Interlocks
stHMIInterface.stNS11.stInterlock.asSafetyINTLKName[0] := 'Emergency stop ok';
_stPosolytPumpInletSIntlk.0 := xEmergencyStopOk;
stHMIInterface.stNS11.stInterlock.asSafetyINTLKName[1] := 'Segment inlet pressure to high';
_stPosolytPumpInletSIntlk.1 := NOT _fbPressurePosolytSegmentInlet.xErrorHigh;
stHMIInterface.stNS11.stInterlock.asSafetyINTLKName[2] := 'Segment inlet pressure to low';
_stPosolytPumpInletSIntlk.2 := NOT _fbPressurePosolytSegmentInlet.xErrorLow;

// Process interlocks
stHMIInterface.stNS21.stInterlock.asProcessINTLKName[0] := 'Negolyt Pump Error';
_stPosolytPumpInletPIntlk.0 := NOT _fbNegolytPumpInlet.xError;


// Pump fb
_fbPosolytPumpInlet(
	xReleaseManualMode:= _xReleaseManualMode, 
	wProcessINTLK:= _stPosolytPumpInletPIntlk, 
	wProcessINTLKUsed:= _stPosolytPumpInletPIntlkUsed, 
	wSafetyINTLK:= _stPosolytPumpInletSIntlk, 
	wSafetyINTLKUsed:= _stPosolytPumpInletSIntlkUsed, 
	xReleaseErrors:= xReleaseErrors, 
	xConfirmAlarms:= xConfirmAlarms, 
	rSPautomatic:= _rSetpointPosolytPumpInlet,  
	xReleaseAnalogInLimitErrors:= xReleaseLimitErrors AND GVL_SCADA.xDummy, 
	stMotorAnalogConfig:= stUnitConfig.stConfigPosolytPump, 
	stHMIInterface:= stHMIInterface.stNS11, 
	xWarning=> );
	
// Set error active if fb has error
IF _fbPosolytPumpInlet.xError THEN
	stModbusErrors.stBitmap.bPumpPos := 1;
	_xErrorActive := TRUE;
END_IF

// Set warning if fb has warning
IF _fbPosolytPumpInlet.xWarning THEN
	_xWarningActive := TRUE;
END_IF

// Set safety interlock flag if fb has safety interlocks active
IF NOT stHMIInterface.stNS11.stInterlock.xSafetyINTLKOk THEN
	xSafetyIntlksOk := FALSE;
END_IF


// ===============================
// Pump negolyt segment inlet
// ===============================

// Safety Interlocks
stHMIInterface.stNS21.stInterlock.asSafetyINTLKName[0] := 'Emergency stop ok';
_stNegolytPumpInletSIntlk.0 := xEmergencyStopOk;
stHMIInterface.stNS11.stInterlock.asSafetyINTLKName[1] := 'Segment inlet pressure to high';
_stNegolytPumpInletSIntlk.1 := NOT _fbPressureNegolytSegmentInlet.xErrorHigh;
stHMIInterface.stNS11.stInterlock.asSafetyINTLKName[2] := 'Segment inlet pressure to low';
_stNegolytPumpInletSIntlk.2 := NOT _fbPressureNegolytSegmentInlet.xErrorLow;

// Process interlocks
stHMIInterface.stNS11.stInterlock.asProcessINTLKName[0] := 'Posolyt Pump Error';
_stNegolytPumpInletPIntlk.0 := NOT _fbPosolytPumpInlet.xError;

	
// Pump fb
_fbNegolytPumpInlet(
	xReleaseManualMode:= _xReleaseManualMode, 
	wProcessINTLK:= _stNegolytPumpInletPIntlk, 
	wProcessINTLKUsed:= _stNegolytPumpInletPIntlkUsed, 
	wSafetyINTLK:= _stNegolytPumpInletSIntlk, 
	wSafetyINTLKUsed:= _stNegolytPumpInletSIntlkUsed,  
	xReleaseErrors:= xReleaseErrors, 
	xConfirmAlarms:= xConfirmAlarms, 
	rSPautomatic:= _rSetpointNegolytPumpInlet, 
	xReleaseAnalogInLimitErrors:= xReleaseLimitErrors AND GVL_SCADA.xDummy, 
	stMotorAnalogConfig:= stUnitConfig.stConfigNegolytPump, 
	stHMIInterface:= stHMIInterface.stNS21,  
	xWarning=> );
	
// Set error active if fb has error
IF _fbNegolytPumpInlet.xError THEN
	stModbusErrors.stBitmap.bPumpNeg := 1;
	_xErrorActive := TRUE;
END_IF

// Set warning if fb has warning
IF _fbNegolytPumpInlet.xWarning THEN
	_xWarningActive := TRUE;
END_IF

// Set safety interlock flag if fb has safety interlocks active
IF NOT stHMIInterface.stNS21.stInterlock.xSafetyINTLKOk THEN
	xSafetyIntlksOk := FALSE;
END_IF

_xPumpsReady := stHMIInterface.stNS21.stInterlock.xProcessINTLKOk AND stHMIInterface.stNS21.stInterlock.xSafetyINTLKOk AND stHMIInterface.stNS11.stInterlock.xProcessINTLKOk AND stHMIInterface.stNS11.stInterlock.xSafetyINTLKOk;

// ===============================
// Pressure sensor posolyt segment inlet
// ===============================
_fbPressurePosolytSegmentInlet(
	stScalingConfig:= stUnitConfig.stConfigPosolytPressureSegmentInlet, 
	stEWConfig:= stUnitConfig.stEWLPosolytPressureSegmentInlet, 
	stEWDelayConfig:= stUnitConfig.stEWDPosolytPressureSegmentInlet, 
	xReleaseErrors:= xReleaseErrors, 
	xReleaseLimitErrors:= xReleaseLimitErrors AND _fbPosolytPumpInlet.IsRunning AND _fbPosolytPumpInlet.xInTarget AND (NOT xStartBalancing), // AND (_fbPosolytPumpInlet.rSPautomatic > 40), 
	xReleaseHardwareErrors:= xReleaseErrors, 
	xConfirmAlarms:= xConfirmAlarms,
	stHMIInterface => stHMIInterface.stP11);
	
// Filter pressure sensor data for HMI
_rFilteredPressPosolytInlet := _rFilteredPressPosolytInlet * 0.9 + stHMIInterface.stP11.rValue * 0.1;
stHMIInterface.stP11.rValue := _rFilteredPressPosolytInlet;

// Set modbus error register bits
IF _fbPressurePosolytSegmentInlet.xErrorLow THEN
	stModbusErrors.stBitmap.bPInPosLow := 1;
END_IF
IF _fbPressurePosolytSegmentInlet.xErrorHigh THEN
	stModbusErrors.stBitmap.bPInPosHigh := 1;
END_IF
IF _fbPressurePosolytSegmentInlet.xError THEN
	stModbusErrors.stBitmap.bPInPosError := 1;
END_IF

// Set modbus warning register bits
IF _fbPressurePosolytSegmentInlet.xWarningLow THEN
	stModbusWarnings.stBitmap.bPInPosLow := 1;
END_IF
IF _fbPressurePosolytSegmentInlet.xWarningHigh THEN
	stModbusWarnings.stBitmap.bPInPosHigh := 1;
END_IF


// Set error active if fb has error
IF _fbPressurePosolytSegmentInlet.xError THEN
	_xErrorActive := TRUE;
END_IF

// Set warning if fb has warning
IF _fbPressurePosolytSegmentInlet.xWarning THEN
	_xWarningActive := TRUE;
END_IF


// ===============================
// Pressure sensors negolyt segment inlet
// ===============================
_fbPressureNegolytSegmentInlet(
	stScalingConfig:= stUnitConfig.stConfigNegolytPressureSegmentInlet, 
	stEWConfig:= stUnitConfig.stEWLNegolytPressureSegmentInlet, 
	stEWDelayConfig:= stUnitConfig.stEWDNegolytPressureSegmentInlet, 
	xReleaseErrors:= xReleaseErrors, 
	xReleaseLimitErrors:= xReleaseLimitErrors AND _fbNegolytPumpInlet.IsRunning AND _fbNegolytPumpInlet.xInTarget AND (NOT xStartBalancing),//  AND (_fbNegolytPumpInlet.rSPautomatic > 40), 
	xReleaseHardwareErrors:= xReleaseErrors, 
	xConfirmAlarms:= xConfirmAlarms,
	stHMIInterface => stHMIInterface.stP21);
	
// Filter pressure sensor data for HMI
_rFilteredPressNegolytInlet := _rFilteredPressNegolytInlet * 0.9 + stHMIInterface.stP21.rValue * 0.1;
stHMIInterface.stP21.rValue := _rFilteredPressNegolytInlet;

// Set modbus error register bits
IF _fbPressureNegolytSegmentInlet.xErrorLow THEN
	stModbusErrors.stBitmap.bPInNegLow := 1;
END_IF
IF _fbPressureNegolytSegmentInlet.xErrorHigh THEN
	stModbusErrors.stBitmap.bPInNegHigh := 1;
END_IF
IF _fbPressureNegolytSegmentInlet.xError THEN
	stModbusErrors.stBitmap.bPInNegError := 1;
END_IF

// Set modbus warning register bits
IF _fbPressureNegolytSegmentInlet.xWarningLow THEN
	stModbusWarnings.stBitmap.bPInNegLow := 1;
END_IF
IF _fbPressureNegolytSegmentInlet.xWarningHigh THEN
	stModbusWarnings.stBitmap.bPInNegHigh := 1;
END_IF
	
// Set error active if fb has error
IF _fbPressureNegolytSegmentInlet.xError THEN
	_xErrorActive := TRUE;
END_IF

// Set warning if fb has warning
IF _fbPressureNegolytSegmentInlet.xWarning THEN
	_xWarningActive := TRUE;
END_IF


// ===============================
// Pressure sensors posolyt tank inlet
// ===============================
_fbPressurePosolytTankInlet(
	stScalingConfig:= stUnitConfig.stConfigPosolytPressureTankInlet, 
	stEWConfig:= stUnitConfig.stEWLPosolytPressureTankInlet, 
	stEWDelayConfig:= stUnitConfig.stEWDPosolytPressureTankInlet, 
	xReleaseErrors:= xReleaseErrors, 
	xReleaseLimitErrors:= xReleaseLimitErrors, 
	xReleaseHardwareErrors:= xReleaseErrors, 
	xConfirmAlarms:= xConfirmAlarms,
	stHMIInterface => stHMIInterface.stP12);
	
// Filter pressure sensor data for HMI
_rFilteredPressPosolytTank := _rFilteredPressPosolytTank * 0.9 + stHMIInterface.stP12.rValue * 0.1;
stHMIInterface.stP12.rValue := _rFilteredPressPosolytTank;

// Set modbus error register bits
IF _fbPressurePosolytTankInlet.xErrorLow THEN
	stModbusErrors.stBitmap.bPTankPosLow := 1;
END_IF
IF _fbPressurePosolytTankInlet.xErrorHigh THEN
	stModbusErrors.stBitmap.bPTankPosHigh := 1;
END_IF
IF _fbPressurePosolytTankInlet.xError THEN
	stModbusErrors.stBitmap.bPTankPosError := 1;
END_IF

// Set modbus warning register bits
IF _fbPressurePosolytTankInlet.xWarningLow THEN
	stModbusWarnings.stBitmap.bPTankPosLow := 1;
END_IF
IF _fbPressurePosolytTankInlet.xWarningHigh THEN
	stModbusWarnings.stBitmap.bPTankPosHigh := 1;
END_IF
	
// Set error active if fb has error
IF _fbPressurePosolytTankInlet.xError THEN
	_xErrorActive := TRUE;
END_IF

// Set warning if fb has warning
IF _fbPressurePosolytTankInlet.xWarning THEN
	_xWarningActive := TRUE;
END_IF


// ===============================
// Pressure sensors negolyt tank inlet
// ===============================
_fbPressureNegolytTankInlet(
	stScalingConfig:= stUnitConfig.stConfigNegolytPressureTankInlet, 
	stEWConfig:= stUnitConfig.stEWLNegolytPressureTankInlet, 
	stEWDelayConfig:= stUnitConfig.stEWDNegolytPressureTankInlet, 
	xReleaseErrors:= xReleaseErrors, 
	xReleaseLimitErrors:= xReleaseLimitErrors, 
	xReleaseHardwareErrors:= xReleaseErrors, 
	xConfirmAlarms:= xConfirmAlarms,
	stHMIInterface => stHMIInterface.stP22);
	
// Filter pressure sensor data for HMI
_rFilteredPressNegolytTank := _rFilteredPressNegolytTank * 0.9 + stHMIInterface.stP22.rValue * 0.1;
stHMIInterface.stP22.rValue := _rFilteredPressNegolytTank;

// Set modbus error register bits
IF _fbPressureNegolytTankInlet.xErrorLow THEN
	stModbusErrors.stBitmap.bPTankNegLow := 1;
END_IF
IF _fbPressureNegolytTankInlet.xErrorHigh THEN
	stModbusErrors.stBitmap.bPTankNegHigh := 1;
END_IF
IF _fbPressureNegolytTankInlet.xError THEN
	stModbusErrors.stBitmap.bPTankNegError := 1;
END_IF

// Set modbus warning register bits
IF _fbPressureNegolytTankInlet.xWarningLow THEN
	stModbusWarnings.stBitmap.bPTankNegLow := 1;
END_IF
IF _fbPressureNegolytTankInlet.xWarningHigh THEN
	stModbusWarnings.stBitmap.bPTankNegHigh := 1;
END_IF
	
// Set error active if fb has error
IF _fbPressureNegolytTankInlet.xError THEN
	_xErrorActive := TRUE;
END_IF

// Set warning if fb has warning
IF _fbPressureNegolytTankInlet.xWarning THEN
	_xWarningActive := TRUE;
END_IF


// ===============================
// Temperature sensor posolyt tank inlet
// ===============================
_fbTempSensorPosolyt(
	stScalingConfig:= stUnitConfig.stConfigPosolytTempTankInlet, 
	stEWConfig:= GVL_CONFIG.stUnitConfig.stEWLPosolytTempTankInlet, 
	stEWDelayConfig:= GVL_CONFIG.stUnitConfig.stEWDPosolytTempTankInlet,  
	xReleaseErrors:= xReleaseErrors, 
	xReleaseLimitErrors:= xReleaseLimitErrors, 
	xReleaseHardwareErrors:= xReleaseErrors, 
	xConfirmAlarms:= xConfirmAlarms, 
	stHMIInterface=> stHMIInterface.stT11);
	
// Set modbus error register bits
IF _fbTempSensorPosolyt.xErrorLow THEN
	stModbusErrors.stBitmap.bTPosLow := 1;
END_IF
IF _fbTempSensorPosolyt.xErrorHigh THEN
	stModbusErrors.stBitmap.bTPosHigh := 1;
END_IF
IF _fbTempSensorPosolyt.xError THEN
	stModbusErrors.stBitmap.bTPosError := 1;
END_IF

// Set modbus warning register bits
IF _fbTempSensorPosolyt.xWarningLow THEN
	stModbusWarnings.stBitmap.bTPosLow := 1;
END_IF
IF _fbTempSensorPosolyt.xWarningHigh THEN
	stModbusWarnings.stBitmap.bTPosHigh := 1;
END_IF
	
IF _fbTempSensorPosolyt.xError THEN
	_xErrorActive := TRUE;
END_IF

IF _fbTempSensorPosolyt.xWarning THEN
	_xWarningActive := TRUE;
END_IF


// ===============================
// Temperature sensor negolyt tank inlet
// ===============================
_fbTempSensorNegolyt(
	stScalingConfig:= stUnitConfig.stConfigNegolytTempTankInlet, 
	stEWConfig:= GVL_CONFIG.stUnitConfig.stEWLNegolytTempTankInlet, 
	stEWDelayConfig:= GVL_CONFIG.stUnitConfig.stEWDNegolytTempTankInlet,  
	xReleaseErrors:= xReleaseErrors, 
	xReleaseLimitErrors:= xReleaseLimitErrors, 
	xReleaseHardwareErrors:= xReleaseErrors, 
	xConfirmAlarms:= xConfirmAlarms, 
	stHMIInterface=> stHMIInterface.stT21);
	
// Set modbus error register bits
IF _fbTempSensorNegolyt.xErrorLow THEN
	stModbusErrors.stBitmap.bTNegLow := 1;
END_IF
IF _fbTempSensorNegolyt.xErrorHigh THEN
	stModbusErrors.stBitmap.bTNegHigh := 1;
END_IF
IF _fbTempSensorNegolyt.xError THEN
	stModbusErrors.stBitmap.bTNegError := 1;
END_IF

// Set modbus warning register bits
IF _fbTempSensorNegolyt.xWarningLow THEN
	stModbusWarnings.stBitmap.bTNegLow := 1;
END_IF
IF _fbTempSensorNegolyt.xWarningHigh THEN
	stModbusWarnings.stBitmap.bTNegHigh := 1;
END_IF
	
IF _fbTempSensorNegolyt.xError THEN
	_xErrorActive := TRUE;
END_IF

IF _fbTempSensorNegolyt.xWarning THEN
	_xWarningActive := TRUE;
END_IF


// ===============================
// Voltage segment
// ===============================
_fbVoltageSegment(
	stScalingConfig:= GVL_CONFIG.stUnitConfig.stConfigVoltageSegment, 
	stEWConfig:= GVL_CONFIG.stUnitConfig.stEWLVoltageSegment, 
	stEWDelayConfig:= GVL_CONFIG.stUnitConfig.stEWDVoltageSegment, 
	xReleaseErrors:= xReleaseErrors, 
	xReleaseLimitErrors:= xReleaseLimitErrors AND _xEnableVoltageLimitChecks, 
	xReleaseHardwareErrors:= xReleaseErrors, 
	xConfirmAlarms:= xConfirmAlarms, 
	stHMIInterface=> stHMIInterface.stE31,
	rScaledValue => rCurrentVoltage);
	
// Set modbus error register bits
IF _fbVoltageSegment.xErrorLow THEN
	stModbusErrors.stBitmap.bVLow := 1;
END_IF
IF _fbVoltageSegment.xErrorHigh THEN
	stModbusErrors.stBitmap.bVHigh := 1;
END_IF
IF _fbVoltageSegment.xError THEN
	stModbusErrors.stBitmap.bVError := 1;
END_IF

// Set modbus warning register bits
IF _fbVoltageSegment.xWarningLow THEN
	stModbusWarnings.stBitmap.bVLow := 1;
END_IF
IF _fbVoltageSegment.xWarningHigh THEN
	stModbusWarnings.stBitmap.bVHigh := 1;
END_IF
	
// Set error active if fb has error
IF _fbVoltageSegment.xError THEN
	_xErrorActive := TRUE;
END_IF

// Set warning if fb has warning
IF _fbVoltageSegment.xWarning THEN
	_xWarningActive := TRUE;
END_IF


// ===============================
// Check for EStop
// ===============================
IF (NOT xEmergencyStopOk) THEN
	_xErrorActive := TRUE;
END_IF


// ===============================
// Write Voltage to HMI
// ===============================
stHMIInterface.rVoltage := stHMIInterface.rVoltage * 0.9 + _fbVoltageSegment.rScaledValue * 0.1;


// ===============================
// Check for pressure difference
// ===============================
_xDeltaPSegmentInletToHigh := (ABS(_fbPressurePosolytSegmentInlet.rScaledValue - _fbPressureNegolytSegmentInlet.rScaledValue) > GVL_CONFIG.stUnitConfig.rMaxDeltaPSegmentInlet);
_xReleaseSignalDeltaPSegment(
	xSignal:= _xDeltaPSegmentInletToHigh, 
	xRelease:= _xEnableCheckForDeltaPSegmentInlet, 
	timOnDelay:= T#5S, 
	timOffDelay:= T#2S, 
	xReleaseSignal=> _xErrorDeltaPSegmentInlet);

IF _xErrorDeltaPSegmentInlet AND (NOT _fbPressureDiffToBig.bRaised) THEN
	_fbPressureDiffToBig.Raise(0);
END_IF

IF (NOT _xErrorDeltaPSegmentInlet) AND _fbPressureDiffToBig.bRaised THEN
	_fbPressureDiffToBig.Clear(0, FALSE);
END_IF

IF _fbPressureDiffToBig.eConfirmationState = TcEventConfirmationState.WaitForConfirmation THEN
	_fbPressureDiffToBig.Confirm(0);
END_IF
	
IF _xErrorDeltaPSegmentInlet THEN
	_xErrorActive := TRUE;
END_IF


// ===============================
// Check if all components are in automatic
// ===============================
_xAllComponentsInAutomatic := _fbPosolytValveTankOutlet.IsInAutomaticMode
							AND _fbNegolytValveTankOutlet.IsInAutomaticMode
							AND _fbPosolytPumpInlet.IsInAutomaticMode
							AND _fbNegolytPumpInlet.IsInAutomaticMode;
							
// Raise warning
IF NOT _fbNotAllAutomaticAlarm.bRaised AND (NOT _xAllComponentsInAutomatic) AND (NOT xAllToManualMode) THEN
	_fbNotAllAutomaticAlarm.Raise(0);
END_IF

// Clear warning and reset
IF _fbNotAllAutomaticAlarm.bRaised AND _xAllComponentsInAutomatic THEN
	_fbNotAllAutomaticAlarm.Clear(0, TRUE);
END_IF

// Calculate shutdown discharge threshold
_rShutdownDischargeVoltageThreshold := GVL_CONFIG.rMinimumUnitVoltage + GVL_CONFIG.rDeltaUnitVoltageShutdownDischarge;


// ===============================
// Run state machine for startup
// and shutdown
// NEEDS TO BE CALLED AFTER ALL COMPONENTS BECAUSE OF RESET AND ALARM ACKNOWLEDGEMENT!!!
// (e.g. restart pumps after estop if segment voltage is still to high)
// ===============================
StateMachine();


// ===============================
// Output error and warning flags
// ===============================
xError := _xErrorActive;
xWarning := _xWarningActive;

IF (NOT xSafetyIntlksOk) THEN
	stModbusErrors.stBitmap.bSafetyIntlk := 1;
END_IF


// ===============================
// Reset modbus error register with alarm confirmation
// ===============================
IF xConfirmAlarms THEN
	stModbusErrors.dwRegister := 0;
	stModbusWarnings.dwRegister := 0;
END_IF

// ===============================
// Handle general error message
// ===============================
IF xError AND (NOT _fbUnitError.bRaised) THEN
	_fbUnitError.Raise(0);
END_IF

IF (NOT xError) AND _fbUnitError.bRaised THEN
	_fbUnitError.Clear(0, FALSE);
END_IF

IF _fbUnitError.eConfirmationState = TcEventConfirmationState.WaitForConfirmation AND xConfirmAlarms THEN
	_fbUnitError.Confirm(0);
END_IF


// ===============================
// Output all components in auto mode
// ===============================
xAllComponentsInAuto := _xAllComponentsInAutomatic;]]></ST>
    </Implementation>
    <Method Name="FB_init" Id="{08f1cd44-6483-4d20-ab45-d1938e8ec885}">
      <Declaration><![CDATA[METHOD FB_init : BOOL
VAR_INPUT
	bInitRetains : BOOL; // if TRUE, the retain variables are initialized (warm start / cold start)
	bInCopyCode : BOOL;  // if TRUE, the instance afterwards gets moved into the copy code (online change)
	
	sName : STRING;
END_VAR
]]></Declaration>
      <Implementation>
        <ST><![CDATA[_sName := sName;

// Create not all components in automatic mode alarm
_fbNotAllAutomaticAlarm.CreateEx(TC_EVENTS.General.NotAllCompInAutomatic, bWithConfirmation := FALSE, 0);
_fbNotAllAutomaticAlarm.ipArguments.Clear().AddString(_sName);

// Create unit error
_fbUnitError.CreateEx(stEventEntry := TC_EVENTS.General.UnitError, bWithConfirmation := TRUE, 0);
_fbUnitError.ipArguments.Clear().AddString(_sName);]]></ST>
      </Implementation>
    </Method>
    <Property Name="Name" Id="{a82783d4-f1f5-481b-bbb0-7ea8279de793}">
      <Declaration><![CDATA[PROPERTY Name : String]]></Declaration>
      <Get Name="Get" Id="{62ca6bce-5aa4-40f1-aa68-f3e5d22ad4a4}">
        <Declaration><![CDATA[VAR
END_VAR
]]></Declaration>
        <Implementation>
          <ST><![CDATA[Name := _sName;]]></ST>
        </Implementation>
      </Get>
      <Set Name="Set" Id="{b2e0f6da-5412-45b8-aacd-c140a5beab95}">
        <Declaration><![CDATA[VAR
END_VAR
]]></Declaration>
        <Implementation>
          <ST><![CDATA[_sName := Name;

// Create alarm message
_fbNotAllAutomaticAlarm.ipArguments.Clear().AddString(_sName);
_fbPosolytPumpInlet.Name :=CONCAT(_sName, ' - Posolyt segment inlet');
_fbNegolytPumpInlet.Name := CONCAT(_sName, ' - Negolyt segment inlet');]]></ST>
        </Implementation>
      </Set>
    </Property>
    <Action Name="StateMachine" Id="{2467f04e-fe89-4b6c-a53d-a49981dd0ce6}">
      <Implementation>
        <ST><![CDATA[// ===============================
// Run state machine for startup and shutdown
// ===============================
CASE _iState OF
	0: // Off
		// Start in enable
		IF xEnable AND (NOT xStartBalancing) AND _xAllComponentsInAutomatic AND (NOT _xErrorActive) THEN
			_xReleaseManualMode := FALSE;
			_timUnitStartupWaitTime := GVL_CONFIG.timUnitStartupTime;
			_iState := 10;
			stHMIInterface.eStatus := E_COMPONENT_STATUS.STARTING;
		END_IF
		
		// Start in balancing mode
		IF (NOT xEnable) AND xStartBalancing AND _xAllComponentsInAutomatic AND (NOT _xErrorActive) THEN
			_xReleaseManualMode := FALSE;
			xBalancingDone := FALSE;
			_timUnitStartupWaitTime := GVL_CONFIG.timUnitBalancingStartupTime;
			_iState := 10;
		END_IF
		
		IF _xErrorActive THEN
			_iState := 1000;
		END_IF
	
	10: // Open all valves and start pumps
		_fbPosolytValveTankOutlet.ReqAutomaticOpen();
		_fbNegolytValveTankOutlet.ReqAutomaticOpen();
		
		_fbPosolytPumpInlet.ReqAutomaticStart();
		_fbNegolytPumpInlet.ReqAutomaticStart();
		_xEnableCheckForDeltaPSegmentInlet := TRUE;
		_rSetpointNegolytPumpInlet := GVL_CONFIG.rPumpNegolytOnPower;
		_rSetpointPosolytPumpInlet := GVL_CONFIG.rPumpPosolytOnPower;
		
		xOff := FALSE;
		_iState := 15;
		
	15: // Wait for all valves to be open and all pumps to be running
		IF _fbPosolytValveTankOutlet.IsOpen AND _fbNegolytValveTankOutlet.IsOpen AND _fbPosolytPumpInlet.xInTarget AND _fbNegolytPumpInlet.xInTarget THEN
			IF (NOT _fbPosolytValveTankOutlet.xError) AND (NOT _fbNegolytValveTankOutlet.xError) AND (NOT _fbPosolytPumpInlet.xError) AND (NOT _fbNegolytPumpInlet.xError) THEN
				_iState := 30;
			END_IF
		END_IF
		
		// If enable signal is lost
		IF ((NOT xEnable) AND (NOT xStartBalancing)) THEN
			stHMIInterface.eStatus := E_COMPONENT_STATUS.SHUTDOWN;
			_iState := 40;
		END_IF
		
		// If enable signal is lost, or there is an error, goto shutdown
		IF _xErrorActive THEN
			_xReleaseManualMode := TRUE;
			_iState := 1000;
		END_IF
		
		IF (NOT xEnable) THEN
			_iState := 51;
		END_IF
	
	30: // Wait some time
		_tonStartupCheck(In := TRUE, PT := _timUnitStartupWaitTime);
		
		// After some time, check if all values are ok
		IF _tonStartupCheck.Q THEN
			_tonStartupCheck(In := FALSE);
			IF xEnable AND (NOT xStartBalancing) THEN
				_iState := 31;
			ELSIF (NOT xEnable) AND xStartBalancing THEN
				xReady := TRUE;
				_iState := 69;
			END_IF
		END_IF
		
		// If enable signal is lost, goto shutdown
		IF (NOT xEnable) AND (NOT xStartBalancing) THEN
			stHMIInterface.eStatus := E_COMPONENT_STATUS.SHUTDOWN;
			_iState := 40;
		END_IF
		
		IF _xErrorActive THEN
			_iState := 900;
		END_IF
		
	31: // Check if voltage limit can be reached
		_tonVoltageCheckTimeput(IN := TRUE);
		
		IF (NOT _xErrorActive) AND (NOT _xWarningActive) THEN
			CASE eStringOperatingMode OF
				E_STRING_OPERATING_MODE.SAFETY_CHECK:
					_xEnableVoltageLimitChecks := TRUE;
					_tonVoltageCheckTimeput(IN := FALSE);
					xReady := TRUE;
					stHMIInterface.eStatus := E_COMPONENT_STATUS.ON;
					_iState := 35;
					
				E_STRING_OPERATING_MODE.PRECHARGE:
					_tonVoltageCheckTimeput(IN := FALSE);
					_xEnableVoltageLimitChecks := FALSE;
					xReady := TRUE;
					stHMIInterface.eStatus := E_COMPONENT_STATUS.ON;
					_iState := 35;
					
				ELSE
					IF (_fbVoltageSegment.rScaledValue >= GVL_CONFIG.rMinimumUnitVoltage) THEN
						_xEnableVoltageLimitChecks := TRUE;
						xReady := TRUE;
						_iState := 35;
					END_IF
			END_CASE
		END_IF
		// Check for minimum unit voltage
		(*
		IF ((_fbVoltageSegment.rScaledValue >= GVL_CONFIG.rMinimumUnitVoltage) OR xInSafetyCheckMode) AND (NOT _xErrorActive) AND (NOT _xWarningActive) THEN
			_tonVoltageCheckTimeput(IN := FALSE);
			xReady := TRUE;
			
			// Only enable Voltage Checks if not in safety check mode
			IF (NOT xInSafetyCheckMode) THEN
				_xEnableVoltageLimitChecks := TRUE;
			END_IF
			
			// Continue on normal startup path
			IF xEnable THEN
				stHMIInterface.eStatus := E_COMPONENT_STATUS.ON;
				_iState := 35;
			END_IF
		END_IF
		*)
		
		// Voltage check timeout
		IF _tonVoltageCheckTimeput.Q OR _xErrorActive THEN
			_tonVoltageCheckTimeput(IN := FALSE);
			xError := TRUE;
			_iState := 900;
		END_IF
		
		// If enable signal is lost, goto shutdown
		IF (NOT xEnable) THEN
			_iState := 40;
		END_IF
		
	35: // Unit in enabled state
		_rSetpointNegolytPumpInlet := GVL_CONFIG.rPumpNegolytOnPower;
		_rSetpointPosolytPumpInlet := GVL_CONFIG.rPumpPosolytOnPower;
		
		IF (NOT xEnable) THEN
			stHMIInterface.eStatus := E_COMPONENT_STATUS.SHUTDOWN;
			_iState := 40;
		END_IF
		
		// Goto error shutdown if an error occured
		IF _xErrorActive THEN
			_iState := 900;
		END_IF
	
	40: // Close all valves
		_fbNegolytValveTankOutlet.ReqAutomaticClose();
		_fbPosolytValveTankOutlet.ReqAutomaticClose();
		xReady := FALSE;
		_iState := 45;
		
	45: // Wait for valves to be closed
		IF _fbNegolytValveTankOutlet.IsClosed AND _fbPosolytValveTankOutlet.IsClosed THEN
			xInShutdownDischargeMode := TRUE;
			IF (_fbVoltageSegment.rScaledValue > _rShutdownDischargeVoltageThreshold) THEN
				xShutdownDischargeAllowed := TRUE;
			ELSE
				xShutdownDischargeAllowed := FALSE;
			END_IF
			_iState := 50;
		END_IF
		
		// When there is an error trying to close the valves,
		// dont try to discharge the segment
		IF _fbNegolytValveTankOutlet.xError OR _fbPosolytValveTankOutlet.xError THEN
			_xEnableVoltageLimitChecks := FALSE;
			_iState := 60;
		END_IF
		
		// Check for restart condition
		IF xEnable AND (NOT _xErrorActive) THEN
			_iState := 0;
		END_IF
	
	50: // Wait for unit voltage to drop below a certain threshold
		IF (_fbVoltageSegment.rScaledValue > _rShutdownDischargeVoltageThreshold) AND xInverterEnabled THEN
			xShutdownDischargeAllowed := TRUE;
		ELSE
			xShutdownDischargeAllowed := FALSE;
			_xEnableVoltageLimitChecks := FALSE;
			_rSetpointNegolytPumpInlet := GVL_CONFIG.rPumpNegolytDisChrgPower;
			_rSetpointPosolytPumpInlet := GVL_CONFIG.rPumpPosolytDisChrgPower;
			_xEnableCheckForDeltaPSegmentInlet := FALSE;
			_iState := 51;
		END_IF
		
		// Check for restart condition
		IF (xEnable OR xStartBalancing) AND (NOT _xErrorActive) THEN
			xShutdownDischargeAllowed := FALSE;
			xInShutdownDischargeMode := FALSE;
			_iState := 0;
		END_IF
		
		IF _xErrorActive THEN
			_iState := 1000;
		END_IF
		
	51: // Discharge without inverter
		_rSetpointNegolytPumpInlet := GVL_CONFIG.rPumpNegolytDisChrgPower;
		_rSetpointPosolytPumpInlet := GVL_CONFIG.rPumpPosolytDisChrgPower;
	
		IF (_fbVoltageSegment.rScaledValue <= GVL_CONFIG.rPumpshutoffThreshold) THEN
			xShutdownDischargeAllowed := FALSE;
			xInShutdownDischargeMode := FALSE;
			_iState := 60;
		END_IF
		
		// Check for restart condition
		IF xEnable AND (NOT _xErrorActive) THEN
			xShutdownDischargeAllowed := FALSE;
			xInShutdownDischargeMode := FALSE;
			_iState := 0;
		END_IF
		
		IF _xErrorActive THEN
			_iState := 1000;
		END_IF
		
	60: // Disable pumps
		_fbPosolytPumpInlet.ReqAutomaticStop();
		_fbNegolytPumpInlet.ReqAutomaticStop();
		_iState := 65;
		
	65: // Wait for pumps to be stopped
		IF _fbPosolytPumpInlet.IsStopped AND _fbNegolytPumpInlet.IsStopped THEN
			xOff := TRUE;
			_xReleaseManualMode := TRUE;
			IF NOT _xErrorActive THEN
				stHMIInterface.eStatus := E_COMPONENT_STATUS.OFF;
				_iState := 0;
			END_IF
		END_IF
		
		// Check for restart condition
		IF xEnable AND (NOT _xErrorActive) THEN
			stHMIInterface.eStatus := E_COMPONENT_STATUS.OFF;
			_iState := 0;
		END_IF
		
		// Check for error
		IF _xErrorActive THEN
			_iState := 1000;
		END_IF
		
	69: // Check for minimum voltage
		IF  (_fbVoltageSegment.rScaledValue <= GVL_CONFIG.rMinimumUnitVoltage) THEN
			_iState := 900;
		ELSE
			_rBalancingTargetVoltage := rBalancingTargetVoltage;
			_istate := 70;
		END_IF
		
	70: // Wait for segment voltage to be in the range of the target voltage
		IF (_fbVoltageSegment.rScaledValue <= _rBalancingTargetVoltage) THEN
			xBalancingDone := TRUE;
			_iState := 71;
		END_IF
		
		 // If enable signal is lost, goto shutdown
		IF (NOT xStartBalancing) THEN
			_iState := 40;
		END_IF
		
		IF _xErrorActive THEN
			_iState := 900;
		END_IF
		
	71: // Close valves
		_fbNegolytValveTankOutlet.ReqAutomaticClose();
		_fbPosolytValveTankOutlet.ReqAutomaticClose();
		xReady := FALSE;
		_iState := 72;
		
	72: // Wait for all valves to be closed
		IF _fbNegolytValveTankOutlet.IsClosed AND _fbPosolytValveTankOutlet.IsClosed THEN
			xInShutdownDischargeMode := TRUE;
			_rSetpointNegolytPumpInlet := GVL_CONFIG.rPumpNegolytDisChrgPower;
			_rSetpointPosolytPumpInlet := GVL_CONFIG.rPumpPosolytDisChrgPower;
			_iState := 73;
		END_IF
		
	73: // Wait for start balancing signal to be off to prevent automatic restart
		IF (NOT xStartBalancing) THEN
			_iState := 50;
		END_IF
		
	700: // Debug step
		IF (NOT xStartBalancing) THEN
			_iState := 40;
		END_IF
		
	900: // Soft error shutdown
		_fbNegolytValveTankOutlet.ReqAutomaticClose();
		_fbPosolytValveTankOutlet.ReqAutomaticClose();
		
		_xEnableCheckForDeltaPSegmentInlet := FALSE;
		_rSetpointNegolytPumpInlet := GVL_CONFIG.rPumpNegolytDisChrgPower;
		_rSetpointPosolytPumpInlet := GVL_CONFIG.rPumpPosolytDisChrgPower;
		
		_fbNegolytPumpInlet.ReqAutomaticStart();
		_fbPosolytPumpInlet.ReqAutomaticStart();
		
		_xEnableVoltageLimitChecks := FALSE;
		xShutdownDischargeAllowed := FALSE;
		xInShutdownDischargeMode := FALSE;
		xReady := FALSE;
		
		_iState := 910;
		
	910: // Wait for discharged or restart
		IF (_fbVoltageSegment.rScaledValue <= GVL_CONFIG.rPumpshutoffThreshold) THEN
			xShutdownDischargeAllowed := FALSE;
			xInShutdownDischargeMode := FALSE;
			_iState := 1000;
		END_IF
		
		// Check for restart condition
		IF (NOT xEnable) AND xError THEN
			xError := FALSE;
		END_IF
		
		IF (NOT xError) AND (xEnable OR xStartBalancing) THEN
			_iState := 0;
		END_IF
		
		IF _xErrorActive THEN
			_iState := 1000;
		END_IF
	
	1000: // Error shutdown
		stHMIInterface.eStatus := E_COMPONENT_STATUS.ERROR;
		_fbNegolytValveTankOutlet.ReqAutomaticClose();
		_fbPosolytValveTankOutlet.ReqAutomaticClose();
		_rSetpointNegolytPumpInlet := GVL_CONFIG.rPumpNegolytDisChrgPower;
		_rSetpointPosolytPumpInlet := GVL_CONFIG.rPumpPosolytDisChrgPower;
		_xEnableVoltageLimitChecks := FALSE;
		xShutdownDischargeAllowed := FALSE;
		xInShutdownDischargeMode := FALSE;
		xReady := FALSE;
		xOff := TRUE;
		xError := TRUE;
		_iState := 1001;
		
	1001: // Alarm active
		// Only allow reset when enable is deactivated to avoid an
		// automatic restart of the unit
		IF (NOT _xErrorActive) AND (NOT xEnable) AND (NOT xStartBalancing) AND _xPumpsReady AND xConfirmAlarms THEN
			xError := FALSE;
			IF (_fbVoltageSegment.rScaledValue >= GVL_CONFIG.rPumpshutoffThreshold) THEN
				_iState := 1002;
			ELSE
				_iState := 0;
			END_IF
		END_IF
		
	1002:
		_rSetpointNegolytPumpInlet := GVL_CONFIG.rPumpNegolytDisChrgPower;
		_rSetpointPosolytPumpInlet := GVL_CONFIG.rPumpPosolytDisChrgPower;
		
		_fbNegolytPumpInlet.ReqAutomaticStart();
		_fbPosolytPumpInlet.ReqAutomaticStart();
		_iState := 51;
END_CASE]]></ST>
      </Implementation>
    </Action>
  </POU>
</TcPlcObject>