changes during start of safety check

- Refactoring of string fb
- Added pump safety interlocks for pressure segment inlet too low and too high
This commit is contained in:
Matthias Heisig
2025-06-02 19:51:57 +02:00
parent ac0a8d160b
commit 2f9f4df261
10 changed files with 636 additions and 514 deletions

View File

@@ -129,14 +129,20 @@ VAR
_xECModuleInOP : BOOL;
xDebug1 : BOOL;
xDebug2 : BOOL;
xDebug3 : BOOL;
xDebug4 : BOOL;
_xFirstCycle : BOOL := TRUE;
END_VAR
]]></Declaration>
<Implementation>
<ST><![CDATA[// Reset error and warning flag
<ST><![CDATA[IF _xFirstCycle THEN
_xFirstCycle := FALSE;
_fbUnit1.Name := CONCAT(Name, ' - Unit 1');
_fbUnit2.Name := CONCAT(Name, ' - Unit 2');
_fbUnit3.Name := CONCAT(Name, ' - Unit 3');
_fbUnit4.Name := CONCAT(Name, ' - Unit 4');
END_IF
// Reset error and warning flag
xError := FALSE;
xWarning := FALSE;
@@ -243,7 +249,6 @@ END_IF
IF _fbUnit1.xError THEN
xError := TRUE;
xDebug1 := TRUE;
END_IF
IF NOT _fbUnit1.xSafetyIntlksOk THEN
@@ -283,7 +288,6 @@ END_IF
IF _fbUnit2.xError THEN
xError := TRUE;
xDebug2 := TRUE;
END_IF
IF NOT _fbUnit2.xSafetyIntlksOk THEN
@@ -323,7 +327,6 @@ END_IF
IF _fbUnit3.xError THEN
xError := TRUE;
xDebug3 := TRUE;
END_IF
IF NOT _fbUnit3.xSafetyIntlksOk THEN
@@ -363,7 +366,6 @@ END_IF
IF _fbUnit4.xError THEN
xError := TRUE;
xDebug4 := TRUE;
END_IF
IF NOT _fbUnit4.xSafetyIntlksOk THEN

View File

@@ -52,7 +52,7 @@ VAR_INPUT
// String inverter ip
sInverterIP : STRING;
xECStateSCS AT %I* : UINT;
xECWcState AT %I* : BOOL;
xIsoErrorL1 AT %I* : BOOL;
xIsoErrorL2 AT %I* : BOOL;
@@ -116,9 +116,6 @@ VAR
_fbModule2 : FB_Module(CONCAT(Name,' - Module 2'));
_fbModule3 : FB_Module(CONCAT(Name,' - Module 3'));
// Summed status of all module errors
_xModuleError : BOOL;
// All modules are ready
_xAllModulesReady : BOOL;
@@ -170,8 +167,8 @@ VAR
_sName : STRING;
// String inverter
//_fbInverter : FB_PowerSupplySunspec(Name);
_fbInverter : FB_PowerSupplyKaco(Name);
_fbInverter : FB_PowerSupplySunspec(Name);
//_fbInverter : FB_PowerSupplyKaco(Name);
// Internal inverter power command
_rPowerInverterInternal : REAL;
@@ -194,10 +191,12 @@ VAR
// Analog input for string current measurement
_fbStringCurrent : FB_AnalogInput(CONCAT(Name,' - Current'));
xErrorInverter : BOOL;
_xErrorInverter : BOOL;
_xReleaseLimitErrorsInternal : BOOL;
_xReleaseSafetyIntlkErrors : BOOL;
// Balancing done
_xBalancingDone : BOOL;
@@ -217,6 +216,10 @@ VAR
_fbTONDCSettlingTime : TON := (PT := T#10S);
_xErrorInternal : BOOL;
_fbSafetyResetImpulseGen : FB_Blinker := (rFrequency := 1.0);
END_VAR
VAR PERSISTENT
@@ -234,58 +237,6 @@ xAllModulesInAutoMode := TRUE;
// Reset Safety
xResetSafetyDCCB := xResetSafety;
// ===============================
// EtherCAT connection lost error handling
// ===============================
IF (xECStateSCS <> 8) AND (NOT _fbSCSConnLost.bRaised) AND xReleaseErrors THEN
_fbSCSConnLost.Raise(0);
END_IF
IF (xECStateSCS = 8) AND _fbSCSConnLost.bRaised THEN
_fbSCSConnLost.Clear(0, FALSE);
END_IF
IF _fbSCSConnLost.eConfirmationState = TcEventConfirmationState.WaitForConfirmation AND xConfirmAlarms THEN
_fbSCSConnLost.Confirm(0);
END_IF
// ===============================
// DC Main switch error handling
// ===============================
IF (NOT xRepairSwitchOk) AND (NOT _fbDCMainSwitchNotClosed.bRaised) THEN
_fbDCMainSwitchNotClosed.Raise(0);
END_IF
IF xRepairSwitchOk AND _fbDCMainSwitchNotClosed.bRaised THEN
_fbDCMainSwitchNotClosed.Clear(0, FALSE);
END_IF
// ===============================
// ISO Error handling
// ===============================
// Mute iso error when inverter is enabled
_xIsoError := ((NOT xIsoErrorL1) OR (NOT xIsoErrorL2)) AND (NOT _fbInverter.xActive) AND (xDCCBOpen);
_fbTONIsoError(IN := _xIsoError, PT := GVL_CONFIG.timIsoErrorTimeout);
// _fbInverter
IF _fbTONIsoError.Q AND (NOT _fbIsolationAlarm.bRaised) AND xReleaseErrors THEN
_fbIsolationAlarm.Raise(0);
END_IF
IF (NOT _fbTONIsoError.Q) AND _fbIsolationAlarm.bRaised THEN
_fbIsolationAlarm.Clear(0, FALSE);
END_IF
IF _fbIsolationAlarm.eConfirmationState = TcEventConfirmationState.WaitForConfirmation AND xConfirmAlarms THEN
_fbIsolationAlarm.Confirm(0);
END_IF
IF _fbTONIsoError.Q THEN
xError := TRUE;
END_IF
// ===============================
// DC current measurement
// ===============================
@@ -370,19 +321,6 @@ _fbModule3(
rBalancingTargetVoltage := rSmallestSegmentVoltage);
// ===============================
// Handle component safety interlocks ok
// ===============================
xSafetyIntlksComponentsOk := _fbModule1.xSafetyIntlksOk AND _fbModule2.xSafetyIntlksOk AND _fbModule3.xSafetyIntlksOk;
// ===============================
// Handle modules error state
// ===============================
_xModuleError := _fbModule1.xError OR _fbModule2.xError OR _fbModule3.xError OR (NOT xSafetyIntlksComponentsOk);
IF _xModuleError THEN
xError := TRUE;
END_IF
// ===============================
// Handle modules warning state
// ===============================
@@ -398,19 +336,6 @@ xAllModulesInAutoMode := _fbModule1.xAllUnitsInAutomatic AND _fbModule2.xAllUnit
// ===============================
_xAllModulesInShutdownDischargeMode := _fbModule1.xInShutdownDischargeMode AND _fbModule2.xInShutdownDischargeMode AND _fbModule3.xInShutdownDischargeMode;
// ===============================
// Handle safety interlock alarm
// ===============================
IF (NOT xSafetyIntlksOk) AND NOT _fbSafetyInterlocksNotOkAlarm.bRaised THEN
_fbSafetyInterlocksNotOkAlarm.Raise(0);
END_IF
IF xSafetyIntlksOk AND _fbSafetyInterlocksNotOkAlarm.bRaised THEN
_fbSafetyInterlocksNotOkAlarm.Clear(0, TRUE);
END_IF
// ===============================
// Modules ready check
// ===============================
@@ -431,101 +356,25 @@ xInShutdownDischargeMode := _fbModule1.xInShutdownDischargeMode OR _fbModule2.xI
// ===============================
xShutdownDischargeAllowed := _fbModule1.xShutdownDischargeAllowed AND _fbModule2.xShutdownDischargeAllowed AND _fbModule3.xShutdownDischargeAllowed;
// ===============================
// All modules off
// ===============================
xOff := _fbModule1.xOff AND _fbModule2.xOff AND _fbModule3.xOff;
// ===============================
// Handle status
// Call inverter
// ===============================
// String off
IF xOff AND (NOT xError) THEN
eStatus := E_COMPONENT_STATUS.OFF;
END_IF
// String starting
IF _xEnable AND (NOT _xAllModulesReady) AND (NOT xError) THEN
eStatus := E_COMPONENT_STATUS.STARTING;
END_IF
// String on
IF _xAllModulesReady AND _fbInverter.xActive AND (NOT xError) THEN
IF _rPowerInverterInternal < 0.0 THEN
eStatus := E_COMPONENT_STATUS.CHARGING;
ELSIF _rPowerInverterInternal > 0.0 THEN
eStatus := E_COMPONENT_STATUS.DISCHARGING;
ELSE
eStatus := E_COMPONENT_STATUS.ON;
END_IF
END_IF
// String in shutdown
IF xInShutdownDischargeMode AND (NOT xError) THEN
eStatus := E_COMPONENT_STATUS.SHUTDOWN;
END_IF
// String in error state
IF xError THEN
eStatus := E_COMPONENT_STATUS.ERROR;
END_IF
// ===============================
// Calculate string voltage
// ===============================
rCurrentVoltage := _fbModule1.rCurrentVoltage + _fbModule2.rCurrentVoltage + _fbModule3.rCurrentVoltage;
stHMIInterface.rVoltage := stHMIInterface.rVoltage* 0.95 + rCurrentVoltage * 0.05;
// ===============================
// String balance check
// ===============================
// Reset balance ok flag
_xBalanceOk := TRUE;
// Test module 1 with module 2
IF ABS(_fbModule1.rCurrentVoltage - _fbModule2.rCurrentVoltage) > GVL_CONFIG.rMaxAbsDiffVoltageModulesInString THEN
_xBalanceOk := FALSE;
END_IF
// Test module 1 with module 3
IF ABS(_fbModule1.rCurrentVoltage - _fbModule3.rCurrentVoltage) > GVL_CONFIG.rMaxAbsDiffVoltageModulesInString THEN
_xBalanceOk := FALSE;
END_IF
// Test module 2 with module 3
IF ABS(_fbModule2.rCurrentVoltage - _fbModule3.rCurrentVoltage) > GVL_CONFIG.rMaxAbsDiffVoltageModulesInString THEN
_xBalanceOk := FALSE;
END_IF
// Release signal for balance not ok
_fbBalanceNotOkSignal(
xSignal:= NOT _xBalanceOk,
xRelease:= xEnable AND _xAllModulesReady,
timOnDelay:= T#10S,
timOffDelay:= T#10S,
xReleaseSignal=> );
// Signal an error if all units are ready and module is out of balance
IF _fbBalanceNotOkSignal.xReleaseSignal THEN
xError := TRUE;
END_IF
// Raise error
IF _fbBalanceNotOkSignal.xReleaseSignal AND (NOT _fbModulesOutOfBalanceAlarm.bRaised) THEN
_fbModulesOutOfBalanceAlarm.Raise(0);
END_IF
// Clear error
IF (NOT _fbBalanceNotOkSignal.xReleaseSignal) AND _fbModulesOutOfBalanceAlarm.bRaised AND xConfirmAlarms THEN
_fbModulesOutOfBalanceAlarm.Clear(0, FALSE);
END_IF
// Confirm error
IF _fbModulesOutOfBalanceAlarm.eConfirmationState = TcEventConfirmationState.WaitForConfirmation AND xConfirmAlarms THEN
_fbModulesOutOfBalanceAlarm.Confirm(0);
_fbInverter(
sInverterIPAddr:= sInverterIP,
xEnable:= _xEnableInverter AND xEmergencyStopOk,
rPower:= _rPowerInverterInternal,
xReset:= xConfirmAlarms,
rMaxBattPower:= DINT_TO_REAL(GVL_CONFIG.diMaxStringDischargePower),
stCurrentValues => stInverterData);
IF (_iState >= 30) AND (_iState < 40) THEN
rCapacityAH := rCapacityAH + ((stInverterData.rActDCCurrent * 0.01) / 3600);
rCapacityWH := rCapacityWH + ((stInverterData.rActACPower * 0.01) / 3600);
END_IF
// ===============================
@@ -543,31 +392,17 @@ _tonSafetyOkTimeout();
rSmallestSegmentVoltage := MIN(_fbModule1.rSmallestSegmentVoltage, _fbModule2.rSmallestSegmentVoltage, _fbModule3.rSmallestSegmentVoltage);
rHighestSegmentVoltage := MAX(_fbModule1.rHighestSegmentVoltage, _fbModule2.rHighestSegmentVoltage, _fbModule3.rHighestSegmentVoltage);
// Only recalculate SOC if all modules are ready
IF _xAllModulesReady THEN
_rSOC := ((100.0 * rSmallestSegmentVoltage ) / 24.0) - 229.17;
END_IF
// Call inverter
_fbInverter(
sInverterIPAddr:= sInverterIP,
xEnable:= _xEnableInverter AND xEmergencyStopOk,
rPower:= _rPowerInverterInternal,
xReset:= xConfirmAlarms,
rMaxBattPower:= DINT_TO_REAL(GVL_CONFIG.diMaxStringDischargePower),
stCurrentValues => stInverterData);
IF (_iState >= 30) AND (_iState < 40) THEN
rCapacityAH := rCapacityAH + ((stInverterData.rActDCCurrent * 0.01) / 3600);
rCapacityWH := rCapacityWH + ((stInverterData.rActACPower * 0.01) / 3600);
END_IF
HandleErrors();
CASE _iState OF
0: // Idle
// Start in normal mode
IF xEnable AND (NOT xStartBalancing) AND xAllModulesInAutoMode AND xRepairSwitchOk AND (NOT _xModuleError) THEN
IF xEnable AND (NOT xStartBalancing) AND xAllModulesInAutoMode AND xRepairSwitchOk AND (NOT _xErrorInternal) THEN
_xEnable := TRUE;
//eStatus := E_COMPONENT_STATUS.STARTING;
IF xInSafetyCheckMode THEN
@@ -578,7 +413,7 @@ CASE _iState OF
END_IF
// Start in balancing mode
IF (NOT xEnable) AND xStartBalancing AND xAllModulesInAutoMode AND (NOT _xModuleError) THEN
IF (NOT xEnable) AND xStartBalancing AND xAllModulesInAutoMode AND (NOT _xErrorInternal) THEN
_xStartBalancing := TRUE;
_xReleaseLimitErrorsInternal := FALSE;
//eStatus := E_COMPONENT_STATUS.STARTING;
@@ -590,9 +425,19 @@ CASE _iState OF
IF _xAllModulesReady THEN
xResetSafetyDCCB := TRUE;
_tonResetPulseLength.IN := TRUE;
_xReleaseLimitErrorsInternal := TRUE;
_iState := 10;
END_IF
IF (NOT xEnable) THEN
_xEnable := FALSE;
_iState := 0;
END_IF
IF _xErrorInternal THEN
_iState := 1000;
END_IF
5: // Wait for all modules to be ready in normal mode
IF _xAllModulesReady AND _xBalanceOk THEN
xResetSafetyDCCB := TRUE;
@@ -609,8 +454,7 @@ CASE _iState OF
_iState := 0;
END_IF
IF _xModuleError THEN
_xEnable := FALSE;
IF _xErrorInternal THEN
_iState := 1000;
END_IF
@@ -625,8 +469,7 @@ CASE _iState OF
_iState := 0;
END_IF
IF _xModuleError THEN
_xEnable := FALSE;
IF _xErrorInternal THEN
_iState := 1000;
END_IF
@@ -640,7 +483,9 @@ CASE _iState OF
END_IF
15: // Wait for Safety to be ok
xResetSafetyDCCB := _fbSafetyResetImpulseGen.xOut;
IF xSafetyIntlksOk THEN
xResetSafetyDCCB := FALSE;
_tonSafetyOkTimeout.IN := FALSE;
xCloseDCCB := TRUE;
_tonErrorDCCBNotClosed.IN := TRUE;
@@ -649,6 +494,7 @@ CASE _iState OF
IF (NOT xEnable) THEN
_tonSafetyOkTimeout.IN := FALSE;
xResetSafetyDCCB := FALSE;
_xEnable := FALSE;
//eStatus := E_COMPONENT_STATUS.SHUTDOWN;
_iState := 40;
@@ -656,6 +502,7 @@ CASE _iState OF
IF _tonSafetyOkTimeout.Q THEN
_tonSafetyOkTimeout.IN := FALSE;
xResetSafetyDCCB := FALSE;
xCloseDCCB := TRUE;
xError := TRUE;
xReady := FALSE;
@@ -665,18 +512,21 @@ CASE _iState OF
_iState := 1000;
END_IF
IF _xModuleError THEN
_xEnable := FALSE;
IF _xErrorInternal THEN
_iState := 1000;
END_IF
20: // Check if DC relais closed and safety is ok
IF NOT xDCCBOpen THEN
IF (NOT xDCCBOpen) THEN
_xReleaseSafetyIntlkErrors := TRUE;
_tonErrorDCCBNotClosed.IN := FALSE;
_rPowerInverterInternal := rPowerInverter;
//_rPowerInverterInternal := 0.0;
IF xInSafetyCheckMode THEN
_iState := 29;
_rPowerInverterInternal := 0.0;
_xEnableInverter := TRUE;
// _iState := 29;
_iState := 21;
//eStatus := E_COMPONENT_STATUS.ON;
ELSE
_iState := 21;
@@ -698,8 +548,7 @@ CASE _iState OF
_iState := 40;
END_IF
IF _xModuleError THEN
_xEnable := FALSE;
IF _xErrorInternal THEN
_iState := 1000;
END_IF
@@ -714,7 +563,11 @@ CASE _iState OF
22: // Wait for inverter to be ready
_tonInverterStartupTimeout(IN := TRUE);
IF _fbInverter.xActive AND (NOT _fbInverter.xError) THEN
_iState := 30;
IF xInSafetyCheckMode THEN
_iState := 29;
ELSE
_iState := 30;
END_IF
//eStatus := E_COMPONENT_STATUS.ON;
xReady := TRUE;
_tonInverterStartupTimeout(IN := FALSE);
@@ -730,7 +583,6 @@ CASE _iState OF
END_IF
// Inverter error or timeout for startup
IF _fbInverter.xError OR (NOT xRepairSwitchOk) OR _tonInverterStartupTimeout.Q THEN // _tonInverterStartupTimeout.Q
// Shutdown beacause of inverter startup timeout
@@ -741,7 +593,7 @@ CASE _iState OF
_iState := 1000;
_xEnableInverter := FALSE;
xError := TRUE;
xErrorInverter := TRUE;
_xErrorInverter := TRUE;
_xEnable := FALSE;
_tonInverterStartupTimeout(IN := FALSE);
END_IF
@@ -753,6 +605,10 @@ CASE _iState OF
//eStatus := E_COMPONENT_STATUS.SHUTDOWN;
_iState := 31;
END_IF
IF _xErrorInternal THEN
_iState := 1000;
END_IF
30: // All modules ready
// !!! ATTENTION !!!
@@ -795,7 +651,7 @@ CASE _iState OF
_iState := 40;
END_IF
ELSIF (NOT _xAllModulesReady) OR (NOT _xBalanceOk) OR (NOT xSafetyIntlksOk) OR (NOT xRepairSwitchOk) OR (_fbInverter.xError) THEN
ELSIF (NOT _xAllModulesReady) OR (NOT _xBalanceOk) OR (NOT xSafetyIntlksOk) OR (NOT xRepairSwitchOk) OR (_xErrorInternal) THEN
xError := TRUE;
_xReleaseLimitErrorsInternal := FALSE;
_xEnable := FALSE;
@@ -809,7 +665,7 @@ CASE _iState OF
_iState := 32;
END_IF
IF xError THEN
IF _xErrorInternal THEN
_rPowerInverterInternal := 0.0;
_xEnableInverter := FALSE;
_iState := 40;
@@ -840,17 +696,26 @@ CASE _iState OF
_iState := 0;
END_IF
IF _xErrorInternal THEN
_iState := 1000;
END_IF
40: // Wait for inverter to shut down
IF NOT _fbInverter.xActive THEN
IF (NOT _fbInverter.xActive) THEN
_iState := 41;
END_IF
IF _xErrorInternal THEN
_iState := 1000;
END_IF
41: // Debug delay time for inverter shutdown
_tonInverterShutdownDelay(IN := TRUE);
IF _tonInverterShutdownDelay.Q THEN
_tonInverterShutdownDelay(IN := FALSE);
xCloseDCCB := FALSE;
_xReleaseSafetyIntlkErrors := FALSE;
//eStatus := E_COMPONENT_STATUS.OFF;
_iState := 0;
END_IF
@@ -861,12 +726,20 @@ CASE _iState OF
_iState := 51;
END_IF
IF _xErrorInternal THEN
_iState := 1000;
END_IF
51: // Check if start balancing has been releases to avoid a restart
IF (NOT xStartBalancing) THEN
//eStatus := E_COMPONENT_STATUS.OFF;
_iState := 0;
END_IF
IF _xErrorInternal THEN
_iState := 1000;
END_IF
1000: // Error state
_xEnable := FALSE;
_xEnableInverter := FALSE;
@@ -874,6 +747,7 @@ CASE _iState OF
xError := TRUE;
_xReleaseLimitErrorsInternal := FALSE;
_xReleaseSafetyIntlkErrors := FALSE;
// Reset timer
_tonResetPulseLength(IN := FALSE);
@@ -897,14 +771,58 @@ CASE _iState OF
1010: // Error idle state
// Leave error state only if modules are deactivated
IF (NOT xEnable) AND (NOT _xModuleError) THEN
IF (NOT xEnable) AND (NOT _xErrorInternal) THEN
xError := FALSE;
_xReleaseSafetyIntlkErrors := FALSE;
//eStatus := E_COMPONENT_STATUS.OFF;
_iState := 0;
END_IF
END_CASE
// ===============================
// Handle status
// ===============================
// String off
IF xOff AND (NOT xError) THEN
eStatus := E_COMPONENT_STATUS.OFF;
END_IF
// String starting
IF _xEnable AND (NOT _xAllModulesReady) AND (NOT xError) THEN
eStatus := E_COMPONENT_STATUS.STARTING;
END_IF
// String on
IF _xAllModulesReady AND _fbInverter.xActive AND (NOT xError) THEN
IF _rPowerInverterInternal < 0.0 THEN
eStatus := E_COMPONENT_STATUS.CHARGING;
ELSIF _rPowerInverterInternal > 0.0 THEN
eStatus := E_COMPONENT_STATUS.DISCHARGING;
ELSE
eStatus := E_COMPONENT_STATUS.ON;
END_IF
END_IF
// String in shutdown
IF xInShutdownDischargeMode AND (NOT xError) THEN
eStatus := E_COMPONENT_STATUS.SHUTDOWN;
END_IF
// String in error state
IF xError THEN
eStatus := E_COMPONENT_STATUS.ERROR;
END_IF
// ===============================
// Calculate string voltage
// ===============================
rCurrentVoltage := _fbModule1.rCurrentVoltage + _fbModule2.rCurrentVoltage + _fbModule3.rCurrentVoltage;
stHMIInterface.rVoltage := stHMIInterface.rVoltage* 0.95 + rCurrentVoltage * 0.05;
// ===============================
// Copy inverter data to SCADA interface
// ===============================
stHMIInterface.stInverterData := stInverterData;
IF _xAllModulesReady AND _xBalanceOk AND ((_iState = 30) OR (_iState = 29)) THEN
@@ -983,6 +901,164 @@ _fbIsolationAlarm.CreateEx(stEventEntry := TC_EVENTS.General.IsoError, bWithConf
_fbIsolationAlarm.ipArguments.Clear().AddString(_sName);]]></ST>
</Implementation>
</Method>
<Action Name="HandleErrors" Id="{97c046d3-715e-4533-b060-2e11c6ac9c9b}">
<Implementation>
<ST><![CDATA[// Reset internal error flag
_xErrorInternal := FALSE;
// ===============================
// EtherCAT connection lost error handling
// ===============================
IF (xECWcState <> 0) AND (NOT _fbSCSConnLost.bRaised) AND xReleaseErrors THEN
_fbSCSConnLost.Raise(0);
END_IF
IF (xECWcState = 0) AND _fbSCSConnLost.bRaised THEN
_fbSCSConnLost.Clear(0, FALSE);
END_IF
IF _fbSCSConnLost.eConfirmationState = TcEventConfirmationState.WaitForConfirmation AND xConfirmAlarms THEN
_fbSCSConnLost.Confirm(0);
END_IF
IF (xECWcState <> 0) THEN
_xErrorInternal := TRUE;
END_IF
// ===============================
// DC Main switch error handling
// ===============================
IF (NOT xRepairSwitchOk) AND (NOT _fbDCMainSwitchNotClosed.bRaised) AND xReleaseErrors THEN
_fbDCMainSwitchNotClosed.Raise(0);
END_IF
IF xRepairSwitchOk AND _fbDCMainSwitchNotClosed.bRaised THEN
_fbDCMainSwitchNotClosed.Clear(0, FALSE);
END_IF
IF (NOT xRepairSwitchOk) THEN
_xErrorInternal := TRUE;
END_IF
// ===============================
// ISO Error handling
// ===============================
// Mute iso error when inverter is enabled
_xIsoError := ((NOT xIsoErrorL1) OR (NOT xIsoErrorL2)) AND (NOT _fbInverter.xActive) AND (xDCCBOpen);
_fbTONIsoError(IN := _xIsoError, PT := GVL_CONFIG.timIsoErrorTimeout);
// _fbInverter
IF _fbTONIsoError.Q AND (NOT _fbIsolationAlarm.bRaised) AND xReleaseErrors THEN
_fbIsolationAlarm.Raise(0);
END_IF
IF (NOT _fbTONIsoError.Q) AND _fbIsolationAlarm.bRaised THEN
_fbIsolationAlarm.Clear(0, FALSE);
END_IF
IF _fbIsolationAlarm.eConfirmationState = TcEventConfirmationState.WaitForConfirmation AND xConfirmAlarms THEN
_fbIsolationAlarm.Confirm(0);
END_IF
IF _fbTONIsoError.Q THEN
_xErrorInternal := TRUE;
END_IF
// ===============================
// Handle modules error state
// ===============================
IF _fbModule1.xError OR _fbModule2.xError OR _fbModule3.xError THEN
_xErrorInternal := TRUE;
END_IF
// ===============================
// Handle component safety interlocks ok
// ===============================
xSafetyIntlksComponentsOk := _fbModule1.xSafetyIntlksOk AND _fbModule2.xSafetyIntlksOk AND _fbModule3.xSafetyIntlksOk;
IF (NOT xSafetyIntlksComponentsOk) THEN
_xErrorInternal := TRUE;
END_IF
// ===============================
// Handle safety interlock alarm
// ===============================
IF (NOT xSafetyIntlksOk) AND (NOT _fbSafetyInterlocksNotOkAlarm.bRaised) AND xReleaseErrors AND _xReleaseSafetyIntlkErrors THEN
_fbSafetyInterlocksNotOkAlarm.Raise(0);
END_IF
IF (xSafetyIntlksOk OR (NOT xReleaseErrors) OR (NOT _xReleaseSafetyIntlkErrors)) AND _fbSafetyInterlocksNotOkAlarm.bRaised THEN
_fbSafetyInterlocksNotOkAlarm.Clear(0, TRUE);
END_IF
IF (NOT xSafetyIntlksOk) AND _xReleaseSafetyIntlkErrors THEN
_xErrorInternal := TRUE;
END_IF
// ===============================
// String balance check
// ===============================
// Reset balance ok flag
_xBalanceOk := TRUE;
// Test module 1 with module 2
IF ABS(_fbModule1.rCurrentVoltage - _fbModule2.rCurrentVoltage) > GVL_CONFIG.rMaxAbsDiffVoltageModulesInString THEN
_xBalanceOk := FALSE;
END_IF
// Test module 1 with module 3
IF ABS(_fbModule1.rCurrentVoltage - _fbModule3.rCurrentVoltage) > GVL_CONFIG.rMaxAbsDiffVoltageModulesInString THEN
_xBalanceOk := FALSE;
END_IF
// Test module 2 with module 3
IF ABS(_fbModule2.rCurrentVoltage - _fbModule3.rCurrentVoltage) > GVL_CONFIG.rMaxAbsDiffVoltageModulesInString THEN
_xBalanceOk := FALSE;
END_IF
// Release signal for balance not ok
_fbBalanceNotOkSignal(
xSignal:= NOT _xBalanceOk,
xRelease:= xEnable AND _xAllModulesReady,
timOnDelay:= T#10S,
timOffDelay:= T#10S,
xReleaseSignal=> );
// Signal an error if all units are ready and module is out of balance
IF _fbBalanceNotOkSignal.xReleaseSignal THEN
_xErrorInternal := TRUE;
END_IF
// Raise error
IF _fbBalanceNotOkSignal.xReleaseSignal AND (NOT _fbModulesOutOfBalanceAlarm.bRaised) AND xReleaseErrors THEN
_fbModulesOutOfBalanceAlarm.Raise(0);
END_IF
// Clear error
IF (NOT _fbBalanceNotOkSignal.xReleaseSignal) AND _fbModulesOutOfBalanceAlarm.bRaised AND xConfirmAlarms THEN
_fbModulesOutOfBalanceAlarm.Clear(0, FALSE);
END_IF
// Confirm error
IF _fbModulesOutOfBalanceAlarm.eConfirmationState = TcEventConfirmationState.WaitForConfirmation AND xConfirmAlarms THEN
_fbModulesOutOfBalanceAlarm.Confirm(0);
END_IF
// ===============================
// Handle inverter error
// ===============================
IF _fbInverter.xError THEN
_xErrorInternal := TRUE;
END_IF]]></ST>
</Implementation>
</Action>
<Property Name="Name" Id="{19fcb6d4-fd4b-49f9-9791-1e4c931b9e69}">
<Declaration><![CDATA[PROPERTY Name : String]]></Declaration>
<Get Name="Get" Id="{a4b6ba34-8ad9-46b1-939c-45cef957fd9a}">

View File

@@ -140,13 +140,13 @@ VAR
_stPosolytPumpInletPIntlk : T_INTERLOCK;
_stPosolytPumpInletPIntlkUsed : T_INTERLOCK := 2#0000_0000_0000_0001;
_stPosolytPumpInletSIntlk : T_INTERLOCK;
_stPosolytPumpInletSIntlkUsed : T_INTERLOCK := 2#0000_0000_0000_0001;
_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_0001;
_stNegolytPumpInletSIntlkUsed : T_INTERLOCK := 2#0000_0000_0000_0111;
// Error active
_xErrorActive : BOOL;
@@ -372,6 +372,10 @@ END_IF
// 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';
@@ -417,6 +421,10 @@ END_IF
// 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';
@@ -464,7 +472,7 @@ _fbPressurePosolytSegmentInlet(
stEWConfig:= stUnitConfig.stEWLPosolytPressureSegmentInlet,
stEWDelayConfig:= stUnitConfig.stEWDPosolytPressureSegmentInlet,
xReleaseErrors:= xReleaseErrors,
xReleaseLimitErrors:= xReleaseLimitErrors AND _fbPosolytPumpInlet.IsRunning AND _fbPosolytPumpInlet.xInTarget AND (NOT xStartBalancing) AND (_fbPosolytPumpInlet.rSPautomatic > 40),
xReleaseLimitErrors:= xReleaseLimitErrors AND _fbPosolytPumpInlet.IsRunning AND _fbPosolytPumpInlet.xInTarget AND (NOT xStartBalancing), // AND (_fbPosolytPumpInlet.rSPautomatic > 40),
xReleaseHardwareErrors:= xReleaseErrors,
xConfirmAlarms:= xConfirmAlarms,
stHMIInterface => stHMIInterface.stP11);
@@ -512,7 +520,7 @@ _fbPressureNegolytSegmentInlet(
stEWConfig:= stUnitConfig.stEWLNegolytPressureSegmentInlet,
stEWDelayConfig:= stUnitConfig.stEWDNegolytPressureSegmentInlet,
xReleaseErrors:= xReleaseErrors,
xReleaseLimitErrors:= xReleaseLimitErrors AND _fbNegolytPumpInlet.IsRunning AND _fbNegolytPumpInlet.xInTarget AND (NOT xStartBalancing) AND (_fbNegolytPumpInlet.rSPautomatic > 40),
xReleaseLimitErrors:= xReleaseLimitErrors AND _fbNegolytPumpInlet.IsRunning AND _fbNegolytPumpInlet.xInTarget AND (NOT xStartBalancing),// AND (_fbNegolytPumpInlet.rSPautomatic > 40),
xReleaseHardwareErrors:= xReleaseErrors,
xConfirmAlarms:= xConfirmAlarms,
stHMIInterface => stHMIInterface.stP21);
@@ -923,7 +931,9 @@ END_VAR
<ST><![CDATA[_sName := Name;
// Create alarm message
_fbNotAllAutomaticAlarm.ipArguments.Clear().AddString(_sName);]]></ST>
_fbNotAllAutomaticAlarm.ipArguments.Clear().AddString(_sName);
_fbPosolytPumpInlet.Name :=CONCAT(_sName, ' - Posolyt segment inlet');
_fbNegolytPumpInlet.Name := CONCAT(_sName, ' - Negolyt segment inlet');]]></ST>
</Implementation>
</Set>
</Property>
@@ -954,16 +964,23 @@ CASE _iState OF
_iState := 1000;
END_IF
10: // Open all valves
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
IF _fbPosolytValveTankOutlet.IsOpen AND _fbNegolytValveTankOutlet.IsOpen THEN
IF (NOT _fbPosolytValveTankOutlet.xError) AND (NOT _fbNegolytValveTankOutlet.xError) THEN
_iState := 20;
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
@@ -983,30 +1000,6 @@ CASE _iState OF
_iState := 51;
END_IF
20: // Start pumps
_fbPosolytPumpInlet.ReqAutomaticStart();
_fbNegolytPumpInlet.ReqAutomaticStart();
_xEnableCheckForDeltaPSegmentInlet := TRUE;
_rSetpointNegolytPumpInlet := GVL_CONFIG.rPumpNegolytOnPower;
_rSetpointPosolytPumpInlet := GVL_CONFIG.rPumpPosolytOnPower;
_iState := 25;
25: // Wait for all pumps to run
IF _fbPosolytPumpInlet.xInTarget AND _fbNegolytPumpInlet.xInTarget THEN
_iState := 30;
END_IF
// If enable signal is lost, goto shutdown
IF ((NOT xEnable) AND (NOT xStartBalancing)) THEN
stHMIInterface.eStatus := E_COMPONENT_STATUS.SHUTDOWN;
_iState := 40; // 40
END_IF
IF _xErrorActive THEN
_iState := 900;
END_IF
30: // Wait some time
_tonStartupCheck(In := TRUE, PT := _timUnitStartupWaitTime);

View File

@@ -321,7 +321,7 @@ _afbStrings[0](
xInSafetyCheckMode := _xInSafetyCheckMode,
stHMIInterface:= GVL_SCADA.stHMIInterface[0],
xEmergencyStopOk:= _xEmergencyStopOk,
xReleaseErrors:= _xReleaseErrors AND _tonStartupDelay.Q AND _xEtherCatString1Ok,
xReleaseErrors:= _xReleaseErrors AND _tonStartupDelay.Q AND _xEtherCatString1Ok AND GVL_CONFIG.axStringEnabled[0],
xReleaseLimitErrors:= _xReleaseLimitsErrors AND _tonStartupDelay.Q AND _xEtherCatString1Ok,
xReleaseManualMode := _xReleaseManualMode,
xConfirmAlarms:= _xConfirmAlarms,
@@ -350,7 +350,7 @@ _afbStrings[1](
xInSafetyCheckMode := _xInSafetyCheckMode,
stHMIInterface:= GVL_SCADA.stHMIInterface[1],
xEmergencyStopOk:= _xEmergencyStopOk,
xReleaseErrors:= _xReleaseErrors AND _tonStartupDelay.Q AND _xEtherCatString2Ok,
xReleaseErrors:= _xReleaseErrors AND _tonStartupDelay.Q AND _xEtherCatString2Ok AND GVL_CONFIG.axStringEnabled[1],
xReleaseLimitErrors:= _xReleaseLimitsErrors AND _tonStartupDelay.Q AND _xEtherCatString2Ok,
xReleaseManualMode := _xReleaseManualMode,
xConfirmAlarms:= _xConfirmAlarms,
@@ -1124,6 +1124,7 @@ CASE _iStateSafetyCheck OF
_xEnableString := FALSE;
_rPowerInverter := 0.0;
_xCanChangeMode := TRUE;
_xStartSafetyCheck := FALSE;
_iStateSafetyCheck := 1010;
1010: // Wait for reset from error state

View File

@@ -109,6 +109,9 @@ VAR
// Current state of the inverters internal statemachine
_uiInverterState : UINT;
// Polling in enable state
_xEnableInternal : BOOL;
// Inverter name for alarm message
_sName : STRING;
END_VAR
@@ -595,7 +598,7 @@ CASE _iState OF
END_IF
ELSE
// No error and not enabled
IF (NOT xEnable) THEN
IF (NOT _xEnableInternal) THEN
// Goto polling state
_iState := 110;
ELSE
@@ -843,6 +846,7 @@ CASE _iState OF
1000: // Error state
xActive := FALSE;
xError := TRUE;
_xEnableInternal := TRUE;
_iState := 1001;
1001: // Error state, wait for reset

View File

@@ -35,6 +35,9 @@ VAR
// FB for writing Modbus holding registers
_fbWriteRegister : FB_MBWriteRegs;
// FB for writing Modbus holding registers for inverter heartbeat
_fbWriteHeartbeatRegister : FB_MBWriteRegs;
// Timer for checking if the inverter started in a reasonable amount of time
//_tonInverterStartup : TON;
@@ -228,6 +231,24 @@ _tonHearbeatIncTimer(IN := TRUE);
IF _tonHearbeatIncTimer.Q THEN
_tonHearbeatIncTimer(IN := FALSE);
_uiPLCToInverterCounter := _uiPLCToInverterCounter + 1;
_fbWriteHeartbeatRegister.bExecute := TRUE;
END_IF
_fbWriteHeartbeatRegister(
sIPAddr:= sInverterIPAddr,
nTCPPort:= 502,
nUnitID:= 16#FF, // 16#FF for Modbus TCP
nQuantity:= 1,
nMBAddr:= CONTROLLER_HB,
cbLength:= SIZEOF(_uiPLCToInverterCounter),
pSrcAddr:= ADR(_uiPLCToInverterCounter),
tTimeout:= T#5S,
bBusy=> ,
bError=> ,
nErrId=> );
// If writing modbus register is done
IF NOT _fbWriteHeartbeatRegister.bBusy THEN
// And there is no error, then continue
_fbWriteHeartbeatRegister(bExecute := FALSE);
END_IF
// State machine
@@ -277,8 +298,10 @@ CASE _iState OF
2: // IF inverter is not in STANDYB(8) STATE, send command to shutdown inverter
IF (_uiInverterState = 8) OR (_uiInverterState = 1) OR (_uiInverterState = 7) THEN
IF (_uiInverterState = 8) OR (_uiInverterState = 1) THEN
_iState := 3;
ELSIF _uiInverterState = 7 THEN
_iState := 990;
ELSE
_uiPCSSetOperation := 3;
_iState := 200;
@@ -339,7 +362,7 @@ CASE _iState OF
// If there was no error and the converter has no error continue
IF NOT _fbReadRegister.bError THEN
// Go back to polling state
_iState := 5;
_iState := 6;
stCurrentValues.rActACCurrent := LREAL_TO_REAL(WORD_TO_INT(_awCurrentACValues[0]) * EXPT(10,WORD_TO_INT(_awCurrentACValues[4])));
stCurrentValues.rActtACPhaseACurrent := LREAL_TO_REAL(WORD_TO_INT(_awCurrentACValues[1]) * EXPT(10,WORD_TO_INT(_awCurrentACValues[4])));
stCurrentValues.rActtACPhaseBCurrent := LREAL_TO_REAL(WORD_TO_INT(_awCurrentACValues[2]) * EXPT(10,WORD_TO_INT(_awCurrentACValues[4])));
@@ -356,36 +379,6 @@ CASE _iState OF
END_IF
_fbReadRegister(bExecute := FALSE);
END_IF
5: // Send heartbeat signal
_fbWriteRegister(
sIPAddr:= sInverterIPAddr,
nTCPPort:= 502,
nUnitID:= 16#FF, // 16#FF for Modbus TCP
nQuantity:= 1,
nMBAddr:= CONTROLLER_HB,
cbLength:= SIZEOF(_uiPLCToInverterCounter),
pSrcAddr:= ADR(_uiPLCToInverterCounter),
bExecute:= TRUE,
tTimeout:= T#5S,
bBusy=> ,
bError=> ,
nErrId=> );
// If writing modbus register is done
IF NOT _fbWriteRegister.bBusy THEN
// And there is no error, then continue
IF NOT _fbWriteRegister.bError THEN
_iState := 6;
ELSE
xError := TRUE;
_iErrorInState := _iState;
// Goto error state
_iState := 1000;
END_IF
_fbWriteRegister(bExecute := FALSE);
END_IF
6: // Check heartbeat signal
@@ -828,13 +821,19 @@ CASE _iState OF
IF _tonPollingTimer.Q THEN
_tonPollingTimer(IN := FALSE);
_iState := 70;
ELSIF (ABS(rPower - _rPowerInternal) > 0.1) AND _tonSetPowerTimer.Q AND xEnable THEN
ELSIF _tonSetPowerTimer.Q AND xEnable THEN // (ABS(rPower - _rPowerInternal) > 0.1) AND
_tonSetPowerTimer(IN := FALSE);
_rPowerInternal := rPower;
// Add power when derating is active
IF (stCurrentValues.rActDCVoltage > 900) AND (rPower < 0) THEN
_rPowerInternal := rPower - ((stCurrentValues.rActDCVoltage - 900.0) * 35.0);
END_IF
// Calculate power to write to register
_iWMaxLimPct := LREAL_TO_INT((_rPowerInternal*100)/(_rWMax * EXPT(10,_iWMaxLimPctSF)));
// If power has been changed, goto set power limit mode
_iState := 40;
END_IF
@@ -1224,6 +1223,7 @@ CASE _iState OF
1000: // Write error state to log
ADSLOGDINT(msgCtrlMask:= ADSLOG_MSGTYPE_ERROR, msgFmtStr:= 'Fehler im state: %s', dintArg:= INT_TO_DINT(_iErrorInState));
xActive := FALSE;
_iState := 1001;
1001: // Error state, wait for reset