Files
Uniper_PLC/PLC/POUs/FB_String.TcPOU
Markus.Neukirch f0e6143997 IBN changes
added sync units for cabinet temperature, changes in modbus interface to EMS (1.0.4 and 1.0.5), added error counter to modbus communication, lot of changes to kaco (faults, consecutive errors, bms error messages), isolation error ledge, allowed startbalancing when on shutdown, tower light integration
2025-09-05 14:24:37 +02:00

1143 lines
32 KiB
XML

<?xml version="1.0" encoding="utf-8"?>
<TcPlcObject Version="1.1.0.1" ProductVersion="3.1.4026.12">
<POU Name="FB_String" Id="{46501225-f446-4674-bfed-3be64273e576}" SpecialFunc="None">
<Declaration><![CDATA[FUNCTION_BLOCK FB_String
VAR_INPUT
// Enable
xEnable : BOOL;
// Release inverter power
xReleaseInverterPower : BOOL;
// String number for unit numbering starting with 0 for String 1
uiStringNumber : UINT;
// Error shutdown -> No discharge throught inverter
xErrorShutdown : BOOL;
// Start in balancing mode
xStartBalancing : BOOL;
// String in safety check mode
xInSafetyCheckMode : BOOL;
// Operating mode of string
eOperationMode : E_STRING_OPERATING_MODE;
// Requested inverter power
rPowerInverter : REAL;
// String HMI interface
stHMIInterface : REFERENCE TO ST_STRING_HMI_INTERFACE;
// Emergency stop ok
xEmergencyStopOk : BOOL;
// Reset Safety
xResetSafety : BOOL;
// Release alarms
xReleaseErrors : BOOL;
// Release analog io limit errors
xReleaseLimitErrors : BOOL;
// Release manual mode
xReleaseManualMode : BOOL;
// Input to confirm all errors
xConfirmAlarms : BOOL;
// Switch all components to manual mode
xAllToManualMode : BOOL;
// String inverter ip
sInverterIP : STRING;
xECWcState AT %I* : BOOL;
xIsolationOKL1 AT %I* : BOOL;
xIsolationOKL2 AT %I* : BOOL;
refuStringErrorsModbus : REFERENCE TO U_STRING_ERROR_REGISTER;
END_VAR
VAR_OUTPUT
// Repair switch closed
xRepairSwitchOk AT %I* : BOOL;
// Safety communication error
{attribute 'analysis' := '-33'}
xSafetyComError AT %I* : BOOL;
// All safetyinterlocks from safety plc are ok
xSafetyIntlksOk AT %I* : BOOL;
// All component safety interlocks are ok
xSafetyIntlksComponentsOk : BOOL;
// Current string voltage
rCurrentVoltage : REAL;
// Module in shutdown segment discharge mode
xInShutdownDischargeMode : BOOL;
// Module can be discharged during shutdown sequence
xShutdownDischargeAllowed : BOOL;
// String ready
xReady : BOOL;
// String completely off
xOff : BOOL;
// Signal to close dc circuit breaker
xCloseDCCB AT %Q* : BOOL;
// Signal that dc circuit breakers are closed
xDCCBOpen AT %I* : BOOL;
// Reset signal for safety dc circuit breaker
xResetSafetyDCCB AT %Q* : BOOL;
// All modules in automatic mode
xAllModulesInAutoMode : BOOL;
xError : BOOL;
xWarning : BOOL;
// Temperature control cabinet module 1 above 40 °C
xTempCabinetModule1Warning : BOOL;
// Temperature control cabinet module 2 above 40 °C
xTempCabinetModule2Warning : BOOL;
// Temperature control cabinet module 3 above 40 °C
xTempCabinetModule3Warning : BOOL;
eStatus : E_COMPONENT_STATUS;
// Inverter status data
stInverterData : ST_SUNSPEC_CURRENT_VALUES;
// Smallest segment voltage
rSmallestSegmentVoltage : REAL;
// Highest segment voltage
rHighestSegmentVoltage : REAL;
// Balancing done
xBalancingDone : BOOL;
END_VAR
VAR
_fbModule1 : FB_Module(CONCAT(Name,' - Module 1'));
_fbModule2 : FB_Module(CONCAT(Name,' - Module 2'));
_fbModule3 : FB_Module(CONCAT(Name,' - Module 3'));
// All modules are ready
_xAllModulesReady : BOOL;
// All modules in shutdown discharge mode
_xAllModulesInShutdownDischargeMode : BOOL;
// Flag for module balance checking
_xBalanceOk : BOOL;
// Modules out of balance alarm message
_fbModulesOutOfBalanceAlarm : Fb_TcAlarm;
// Safetyinterlocks pending alarm
_fbSafetyInterlocksNotOkAlarm : FB_TcAlarm;
// Inverter startup error
_fbInverterStartupTimeoutAlarm : FB_TcAlarm;
// DC Main switch not closed
_fbDCMainSwitchNotClosed : FB_TcAlarm;
// Connection to SCS lost
_fbSCSConnLost : FB_TcAlarm;
// Isolatio alarm
_fbIsolationAlarm : FB_TcAlarm;
// Safety interlock reset timeout
_fbSafetyIntlkTimeoutAlarm : FB_TcAlarm;
// Shutdown discharge stopped messages
_fbSDDCLevel : FB_TcMessage;
_fbSDUnitThreshold : FB_TcMessage;
// State for start and stop
_iState : INT := 0;
// Error timer for not closing dc relais
_tonErrorDCCBNotClosed : TON := (PT := T#5S);
// Delayed balance check signal
_fbBalanceNotOkSignal : FB_ReleaseSignal;
// String name
_sName : STRING;
// String inverter
//_fbInverter : FB_PowerSupplySunspec(Name);
_fbInverter : FB_PowerSupplyKaco(Name);
// Internal inverter power command
_rPowerInverterInternal : REAL;
// Enable inverter flag
_xEnableInverter : BOOL;
// Fault timer for inverter startup
_tonInverterStartupTimeout : TON := (PT := T#3M);
// Fault timer for inverter shutdown
_tonInverterShutdownError : TON := (PT := T#10S);
// Debug delay timer for inverter shutdown
_tonInverterShutdownDelay : TON := (PT := T#10S);
// Timer for Safety ok timeout
_tonSafetyOkTimeout : TON := (PT := T#2M);
// Analog input for string current measurement
_fbStringCurrent : FB_AnalogInput(CONCAT(Name,' - Current'));
_xErrorInverter : BOOL;
_xReleaseLimitErrorsInternal : BOOL;
_xReleaseSafetyIntlkErrors : BOOL;
// Balancing done
_xBalancingDone : BOOL;
// Enable modules internal signal
_xEnable : BOOL;
// Start balancing internal signal
_xStartBalancing : BOOL;
_xIsoErrorActive : BOOL;
_xIsoError : BOOL;
// Iso error timeout
_fbTONIsoError : TON;
// Internal SOC
_rSOC : REAL;
_fbTONDCSettlingTime : TON := (PT := T#10S);
_xErrorInternal : BOOL;
_fbSafetyResetImpulseGen : FB_Blinker := (rFrequency := 2.0);
END_VAR
VAR PERSISTENT
rCapacityWH : REAL;
rCapacityAH : REAL;
END_VAR
]]></Declaration>
<Implementation>
<ST><![CDATA[// Reset all modules in automatic mode
xAllModulesInAutoMode := TRUE;
_fbSafetyResetImpulseGen();
// ===============================
// DC current measurement
// ===============================
_fbStringCurrent(
stScalingConfig:= GVL_CONFIG.stConfigSCSCurrent,
stEWConfig:= GVL_CONFIG.stEWLSCSCurrent,
stEWDelayConfig:= GVL_CONFIG.stEWDSCSCurrent,
xReleaseErrors:= xReleaseErrors,
xReleaseLimitErrors:= FALSE,
xReleaseHardwareErrors:= xReleaseErrors,
xConfirmAlarms:= xConfirmAlarms,
xError=> ,
xWarning=> ,
rScaledValue=> ,
xErrorLow=> ,
xWarningLow=> ,
xWarningHigh=> ,
xErrorHigh=> ,
stHMIInterface=> );
// Copy scaled current value to HMI
stHMIInterface.rCurrent := _fbStringCurrent.stHMIInterface.rValue;
// ===============================
// Module 1
// ===============================
_fbModule1(
xEnable := _xEnable,
uiFirstUnitIndex := uiStringNumber * 12,
rCurrent := stHMIInterface.rCurrent,
xStartBalancing := _xStartBalancing,
eStringOperatingMode := eOperationMode,
xInverterEnabled := _fbInverter.xActive,
xInSafetyCheckMode := xInSafetyCheckMode,
xEmergencyStopOk:= xEmergencyStopOk,
refstHMIInterface:= stHMIInterface.stHMIInterfaceModule1,
xReleaseErrors:= xReleaseErrors,
xReleaseLimitErrors:= xReleaseLimitErrors AND _xReleaseLimitErrorsInternal,
xReleaseManualMode := xReleaseManualMode,
xConfirmAlarms:= xConfirmAlarms,
xAllToManualMode := xAllToManualMode,
rBalancingTargetVoltage := rSmallestSegmentVoltage,
xTempCabinetWarning => xTempCabinetModule1Warning);
// ===============================
// Module 2
// ===============================
_fbModule2(
xEnable := _xEnable,
uiFirstUnitIndex := uiStringNumber * 12 + 4,
rCurrent := stHMIInterface.rCurrent,
xStartBalancing := _xStartBalancing,
eStringOperatingMode := eOperationMode,
xInverterEnabled := _fbInverter.xActive,
xInSafetyCheckMode := xInSafetyCheckMode,
xEmergencyStopOk:= xEmergencyStopOk,
refstHMIInterface:= stHMIInterface.stHMIInterfaceModule2,
xReleaseErrors:= xReleaseErrors,
xReleaseLimitErrors:= xReleaseLimitErrors AND _xReleaseLimitErrorsInternal,
xReleaseManualMode := xReleaseManualMode,
xConfirmAlarms:= xConfirmAlarms,
xAllToManualMode := xAllToManualMode,
rBalancingTargetVoltage := rSmallestSegmentVoltage,
xTempCabinetWarning => xTempCabinetModule2Warning);
// ===============================
// Module 3
// ===============================
_fbModule3(
xEnable := _xEnable,
uiFirstUnitIndex := uiStringNumber * 12 + 8,
rCurrent := stHMIInterface.rCurrent,
xStartBalancing := _xStartBalancing,
eStringOperatingMode := eOperationMode,
xInverterEnabled := _fbInverter.xActive,
xInSafetyCheckMode := xInSafetyCheckMode,
xEmergencyStopOk:= xEmergencyStopOk,
refstHMIInterface:= stHMIInterface.stHMIInterfaceModule3,
xReleaseErrors:= xReleaseErrors,
xReleaseLimitErrors:= xReleaseLimitErrors AND _xReleaseLimitErrorsInternal,
xReleaseManualMode := xReleaseManualMode,
xConfirmAlarms:= xConfirmAlarms,
xAllToManualMode := xAllToManualMode,
rBalancingTargetVoltage := rSmallestSegmentVoltage,
xTempCabinetWarning => xTempCabinetModule3Warning);
// ===============================
// Handle modules warning state
// ===============================
xWarning := _fbModule1.xWarning OR _fbModule2.xWarning OR _fbModule3.xWarning;
// ===============================
// Handle modules in auto mode
// ===============================
xAllModulesInAutoMode := _fbModule1.xAllUnitsInAutomatic AND _fbModule2.xAllUnitsInAutomatic AND _fbModule3.xAllUnitsInAutomatic;
// ===============================
// Handle shutdown discharge mode
// ===============================
_xAllModulesInShutdownDischargeMode := _fbModule1.xInShutdownDischargeMode AND _fbModule2.xInShutdownDischargeMode AND _fbModule3.xInShutdownDischargeMode;
// ===============================
// Modules ready check
// ===============================
_xAllModulesReady := _fbModule1.xReady AND _fbModule2.xReady AND _fbModule3.xReady;
// ===============================
// Balancing done check
// ===============================
_xBalancingDone := _fbModule1.xBalancingDone AND _fbModule2.xBalancingDone AND _fbModule3.xBalancingDone;
// ===============================
// Modules in shutdown discharge mode
// ===============================
xInShutdownDischargeMode := _fbModule1.xInShutdownDischargeMode OR _fbModule2.xInShutdownDischargeMode OR _fbModule3.xInShutdownDischargeMode;
// ===============================
// Units shutdown discharge allowed
// ===============================
xShutdownDischargeAllowed := _fbModule1.xShutdownDischargeAllowed AND _fbModule2.xShutdownDischargeAllowed AND _fbModule3.xShutdownDischargeAllowed;
// ===============================
// All modules off
// ===============================
xOff := _fbModule1.xOff AND _fbModule2.xOff AND _fbModule3.xOff;
// ===============================
// Call inverter
// ===============================
_fbInverter(
sInverterIPAddr:= sInverterIP,
xEnable:= _xEnableInverter AND xEmergencyStopOk,
xReleasePower := xReleaseInverterPower,
rPower:= _rPowerInverterInternal,
xReset:= xConfirmAlarms,
rMaxBattPower:= DINT_TO_REAL(GVL_CONFIG.diMaxStringDischargePower),
stCurrentValues => stInverterData);
refuStringErrorsModbus.stBitmap.bInverterError := _fbInverter.xError;
IF (_iState >= 30) AND (_iState < 40) THEN
rCapacityAH := rCapacityAH + ((stInverterData.rActDCCurrent * 0.01) / 3600);
rCapacityWH := rCapacityWH + ((stInverterData.rActACPower * 0.01) / 3600);
END_IF
// ===============================
// String ready validation check
// ===============================
_tonErrorDCCBNotClosed();
_tonSafetyOkTimeout();
// ===============================
// Get smalles segment voltage
// of all units
// ===============================
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
HandleErrors();
CASE _iState OF
0: // Idle
// Start in normal mode
IF (xEnable OR xStartBalancing) AND xAllModulesInAutoMode AND xRepairSwitchOk AND (NOT _xErrorInternal) THEN
_xEnable := TRUE;
CASE eOperationMode OF
// Automatic mode (local or remote)
E_STRING_OPERATING_MODE.AUTOMATIC:
_iState := 5;
// Balancing mode
E_STRING_OPERATING_MODE.BALANCING:
_xStartBalancing := TRUE;
_xReleaseLimitErrorsInternal := FALSE;
_xEnable := FALSE;
_iState := 7;
// Safety check mode
E_STRING_OPERATING_MODE.SAFETY_CHECK:
_iState := 1;
// Precharge mode
E_STRING_OPERATING_MODE.PRECHARGE:
_iState := 1;
END_CASE
END_IF
IF _xErrorInternal THEN
_iState := 1000;
END_IF
1: // Wait for ready in safety check mode
IF _xAllModulesReady THEN
_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
_xReleaseLimitErrorsInternal := TRUE;
_iState := 10;
END_IF
IF (NOT xEnable) THEN
_xEnable := FALSE;
_iState := 0;
END_IF
IF _xErrorInternal THEN
_iState := 1000;
END_IF
7: // Wait for all modules to be ready in balancing mode
IF _xAllModulesReady THEN
_iState := 50;
END_IF
IF (NOT xStartBalancing) THEN
_xStartBalancing := FALSE;
//eStatus := E_COMPONENT_STATUS.OFF;
_iState := 0;
END_IF
IF _xErrorInternal THEN
_iState := 1000;
END_IF
10: // Reset safety from sensors
xResetSafety := FALSE;
_tonSafetyOkTimeout.IN := TRUE;
_iState := 15;
15: // Wait for Safety to be ok
xResetSafety := _fbSafetyResetImpulseGen.xOut;
IF xSafetyIntlksOk THEN
xResetSafety := FALSE;
_tonSafetyOkTimeout.IN := FALSE;
xCloseDCCB := TRUE;
_tonErrorDCCBNotClosed.IN := TRUE;
_iState := 20;
END_IF
IF (NOT xEnable) THEN
_tonSafetyOkTimeout.IN := FALSE;
xResetSafety := FALSE;
_xEnable := FALSE;
_iState := 40;
END_IF
IF _tonSafetyOkTimeout.Q THEN
_tonSafetyOkTimeout.IN := FALSE;
xResetSafety := FALSE;
xCloseDCCB := TRUE;
xError := TRUE;
xReady := FALSE;
IF (NOT _fbSafetyIntlkTimeoutAlarm.bRaised) THEN
_fbSafetyIntlkTimeoutAlarm.Raise(0);
END_IF
_iState := 1000;
END_IF
IF _xErrorInternal THEN
_iState := 1000;
END_IF
20: // Check if DC relais closed and safety is ok
IF (NOT xDCCBOpen) THEN
_xReleaseSafetyIntlkErrors := TRUE;
_tonErrorDCCBNotClosed.IN := FALSE;
CASE eOperationMode OF
E_STRING_OPERATING_MODE.AUTOMATIC:
_rPowerInverterInternal := rPowerInverter;
_xEnableInverter := TRUE;
_iState := 21;
E_STRING_OPERATING_MODE.SAFETY_CHECK:
_rPowerInverterInternal := 0.0;
_xEnableInverter := TRUE;
_iState := 21;
E_STRING_OPERATING_MODE.PRECHARGE:
_rPowerInverterInternal := 0.0;
_xEnableInverter := FALSE;
_iState := 29;
// Balancing mode should never reach this point!
// Its just here for testing
E_STRING_OPERATING_MODE.BALANCING:
_rPowerInverterInternal := 0.0;
_xEnableInverter := FALSE;
_iState := 50;
END_CASE
END_IF
IF _tonErrorDCCBNotClosed.Q THEN
_xEnable := FALSE;
xCloseDCCB := FALSE;
_tonErrorDCCBNotClosed.IN := FALSE;
xError := TRUE;
xReady := FALSE;
IF (NOT _fbDCMainSwitchNotClosed.bRaised) THEN
_fbDCMainSwitchNotClosed.Raise(0);
END_IF
_iState := 1000;
END_IF
IF (NOT xEnable) THEN
_tonSafetyOkTimeout.IN := FALSE;
_xEnable := FALSE;
//eStatus := E_COMPONENT_STATUS.SHUTDOWN;
_iState := 40;
END_IF
IF _xErrorInternal THEN
_iState := 1000;
END_IF
21: // Wait some time for inverter dc bus voltage settling
_fbTONDCSettlingTime(IN := TRUE);
IF _fbTONDCSettlingTime.Q THEN
_fbTONDCSettlingTime(IN := FALSE);
_xEnableInverter := TRUE;
_iState := 22;
END_IF
22: // Wait for inverter to be ready
_tonInverterStartupTimeout(IN := TRUE);
IF _fbInverter.xActive AND (NOT _fbInverter.xError) THEN
CASE eOperationMode OF
E_STRING_OPERATING_MODE.SAFETY_CHECK:
_iState := 29;
ELSE
_iState := 30;
END_CASE
xReady := TRUE;
_tonInverterStartupTimeout(IN := FALSE);
END_IF
IF (NOT xEnable) OR (NOT _xAllModulesReady) THEN
_xEnableInverter := FALSE;
_rPowerInverterInternal := 0.0;
_xEnable := FALSE;
_iState := 31;
_tonInverterStartupTimeout(IN := FALSE);
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
IF _tonInverterStartupTimeout.Q AND (NOT _fbInverterStartupTimeoutAlarm.bRaised) THEN
_fbInverterStartupTimeoutAlarm.Raise(0);
END_IF
_iState := 1000;
_xEnableInverter := FALSE;
xError := TRUE;
_xErrorInverter := TRUE;
_xEnable := FALSE;
_tonInverterStartupTimeout(IN := FALSE);
END_IF
29: // Ready in safety check mode
IF (NOT xEnable) THEN
_xEnable := FALSE;
_xReleaseLimitErrorsInternal := FALSE;
_iState := 31;
END_IF
IF _xErrorInternal THEN
_iState := 1000;
END_IF
30: // All modules ready
// !!! ATTENTION !!!
// BMS HAS TO SHUT DOWN THE INVERTER BEFORE DISSABLING THE STRING
// OTHERWISE THE DC CIRCUIT BREAKERS WILL OPEN WHILE THE INVERTER IS STILL ACTIVE
// THIS CAN DAMAGE THE INVERTER
IF rPowerInverter > DINT_TO_REAL(GVL_CONFIG.diMaxStringDischargePower) THEN
// Limit discharge power (> because discharging is a positive number)
_rPowerInverterInternal := DINT_TO_REAL(GVL_CONFIG.diMaxStringDischargePower);
ELSIF rPowerInverter < DINT_TO_REAL(GVL_CONFIG.diMaxStringChargingPower) THEN
// Limit charging power (< because charging is a negative number)
_rPowerInverterInternal := DINT_TO_REAL(GVL_CONFIG.diMaxStringChargingPower);
ELSE
// Power command within range
_rPowerInverterInternal := rPowerInverter;
END_IF
// Shutdown
IF (NOT xEnable) THEN
_xEnable := FALSE;
_xReleaseLimitErrorsInternal := FALSE;
IF GVL_CONFIG.xShutdownDischargeWithInverter THEN
_rPowerInverterInternal := GVL_CONFIG.rAbsShutdownDischargePower;
_iState := 31;
ELSE
_rPowerInverterInternal := 0.0;
_xEnableInverter := FALSE;
_iState := 40;
END_IF
ELSIF (NOT _xAllModulesReady) OR (NOT _xBalanceOk) OR (NOT xSafetyIntlksOk) OR (NOT xRepairSwitchOk) OR (_xErrorInternal) THEN
xError := TRUE;
_xReleaseLimitErrorsInternal := FALSE;
_xEnable := FALSE;
_rPowerInverterInternal := 0.0;
_xEnableInverter := FALSE;
_iState := 1000;
END_IF
31: // Wait for String to be in in shutdown discharge mode
IF _xAllModulesInShutdownDischargeMode THEN
_iState := 32;
END_IF
IF _xErrorInternal THEN
_rPowerInverterInternal := 0.0;
_xEnableInverter := FALSE;
_iState := 40;
END_IF
32: // Shutdown discharge mode
IF xShutdownDischargeAllowed AND (NOT xErrorShutdown) AND GVL_CONFIG.xShutdownDischargeWithInverter AND xSafetyIntlksOk AND (stInverterData.rActDCVoltage > 620.0) THEN
_rPowerInverterInternal := GVL_CONFIG.rAbsShutdownDischargePower;
ELSE
// Send shutdown message
IF NOT xShutdownDischargeAllowed THEN
_fbSDUnitThreshold.Send(0);
END_IF
IF (stInverterData.rActDCVoltage < 620.0) THEN
_fbSDDCLevel.Send(0);
END_IF
_rPowerInverterInternal := 0.0;
_xEnableInverter := FALSE;
_iState := 40;
END_IF
// Restart on Enable or StartBalancing
IF xEnable OR xStartBalancing THEN
_rPowerInverterInternal := 0.0;
//_xEnableInverter := FALSE;
_iState := 0;
END_IF
IF _xErrorInternal THEN
_iState := 1000;
END_IF
40: // Wait for inverter to shut down
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
50: // Wait for balancing of all units to be done
IF _xBalancingDone THEN
_xStartBalancing := FALSE;
_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;
_rPowerInverterInternal := 0.0;
xError := TRUE;
_xReleaseLimitErrorsInternal := FALSE;
_xReleaseSafetyIntlkErrors := FALSE;
// Reset timer
_tonErrorDCCBNotClosed(IN := FALSE);
//eStatus := E_COMPONENT_STATUS.ERROR;
_iState := 1005;
1005: // Wait for inverter to be off
_tonInverterShutdownError(IN := TRUE);
// If inverter is not shutting down, hard disconnect it
IF (NOT _fbInverter.xActive) OR _tonInverterShutdownError.Q THEN
_tonInverterShutdownError(IN := FALSE);
xCloseDCCB := FALSE;
_iState := 1010;
END_IF
1010: // Error idle state
// Leave error state only if modules are deactivated
IF (NOT xEnable) AND (NOT _xErrorInternal) THEN
xError := FALSE;
_xReleaseSafetyIntlkErrors := FALSE;
ClearAlarms();
//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
xReady := TRUE;
ELSE
xReady := FALSE;
END_IF
// Reset inverter startup timeout alarm
IF _fbInverterStartupTimeoutAlarm.bRaised AND xConfirmAlarms THEN
_fbInverterStartupTimeoutAlarm.Clear(0, TRUE);
END_IF
// Reset safetyinterlock timeout alarm
IF _fbSafetyIntlkTimeoutAlarm.bRaised AND xConfirmAlarms THEN
_fbSafetyIntlkTimeoutAlarm.Clear(0, TRUE);
END_IF
// Copy status to hmi interface
stHMIInterface.eStatus := eStatus;
// Reset Safety
xResetSafetyDCCB := xResetSafety;
xCloseDCCB := TRUE;]]></ST>
</Implementation>
<Action Name="ClearAlarms" Id="{541a307f-e5d8-4588-8fd3-6f4bdf6a71dd}">
<Implementation>
<ST><![CDATA[IF _fbDCMainSwitchNotClosed.bRaised THEN
_fbDCMainSwitchNotClosed.Clear(0, FALSE);
END_IF]]></ST>
</Implementation>
</Action>
<Method Name="FB_init" Id="{9e8494eb-1b40-4be9-91c8-810ecbdf7f0c}">
<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
VAR
_sTemp : STRING;
END_VAR
]]></Declaration>
<Implementation>
<ST><![CDATA[_sName := sName;
// Set names for modules
_fbModule1.Name := Concat(_sName, ' - Module 1');
_fbModule2.Name := Concat(_sName, ' - Module 2');
_fbModule3.Name := Concat(_sName, ' - Module 3');
// Create out of balance alarm
_fbModulesOutOfBalanceAlarm.CreateEx(stEventEntry := TC_EVENTS.BMSEvents.StringImbalance, bWithConfirmation := TRUE, 0);
_fbModulesOutOfBalanceAlarm.ipArguments.Clear().AddString(_sName);
// Create safetyinterlocks active alarm
_fbSafetyInterlocksNotOkAlarm.CreateEx(stEventEntry := TC_EVENTS.BMSEvents.SafetyIntlksActive, bWithConfirmation := FALSE, 0);
_fbSafetyInterlocksNotOkAlarm.ipArguments.Clear().AddString(_sName);
// Create inverter startup timeout alarm
_fbInverterStartupTimeoutAlarm.CreateEx(stEventEntry := TC_EVENTS.BMSEvents.InverterStartupTimeout, bWithConfirmation := TRUE, 0);
_fbInverterStartupTimeoutAlarm.ipArguments.Clear().AddString(_sName);
// Create DC Main Switch not closed alarm
_fbDCMainSwitchNotClosed.CreateEx(stEventEntry := TC_EVENTS.General.DCMainSwitchNotClosed, bWithConfirmation := FALSE, 0);
_fbDCMainSwitchNotClosed.ipArguments.Clear().AddString(_sName);
// EtherCAT communication lost alarm
_fbSCSConnLost.CreateEx(stEventEntry := TC_EVENTS.General.CommError, bWithConfirmation := TRUE, 0);
_sTemp := CONCAT(_sName, ' SCS');
_fbSCSConnLost.ipArguments.Clear().AddString(_sTemp);
// Safety interlock reset timeout alarm
_fbSafetyIntlkTimeoutAlarm.CreateEx(stEventEntry := TC_EVENTS.BMSEvents.SafetyIntlkTimeout, bWithConfirmation := TRUE, 0);
_fbSafetyIntlkTimeoutAlarm.ipArguments.Clear().AddString(_sName);
// Shutdown discharge messages
_fbSDDCLevel.CreateEx(TC_EVENTS.BMSEvents.SDDCVoltage, 0);
_fbSDDCLevel.ipArguments.Clear().AddString(_sName);
_fbSDUnitThreshold.CreateEx(TC_EVENTS.BMSEvents.SDUnitThreshhold, 0);
_fbSDUnitThreshold.ipArguments.Clear().AddString(_sName);
// isolation alarm creation
_fbIsolationAlarm.CreateEx(stEventEntry := TC_EVENTS.General.IsoError, bWithConfirmation := TRUE, 0);
_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;
// ===============================
// Safety ComError Handling
// ===============================
IF xSafetyComError THEN
_xErrorInternal := TRUE;
END_IF
// ===============================
// 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
_xIsoErrorActive := ((NOT xIsolationOKL1) OR (NOT xIsolationOKL2)) AND (NOT _fbInverter.xActive) AND (xDCCBOpen);
_fbTONIsoError(IN := _xIsoErrorActive, PT := GVL_CONFIG.timIsoErrorTimeout);
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
_xIsoError := TRUE;
END_IF
IF xConfirmAlarms AND (NOT _fbTONIsoError.Q) THEN
_xIsoError := FALSE;
END_IF
IF _xIsoError THEN
_xErrorInternal := TRUE;
refuStringErrorsModbus.stBitmap.bIsolationError := 1;
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}">
<Declaration><![CDATA[VAR
END_VAR
]]></Declaration>
<Implementation>
<ST><![CDATA[Name := _sName;]]></ST>
</Implementation>
</Get>
<Set Name="Set" Id="{c32997c5-bac4-4ef0-bc87-edcbbb2e542f}">
<Declaration><![CDATA[VAR
END_VAR
]]></Declaration>
<Implementation>
<ST><![CDATA[_sName := Name;
// Set modules names
_fbModule1.Name := CONCAT(_sName, ' - Module 1');
_fbModule2.Name := CONCAT(_sName, ' - Module 2');
_fbModule3.Name := CONCAT(_sName, ' - Module 3');
// Set inverter Name
_fbInverter.Name := _sName;
// Create alarm messages
_fbModulesOutOfBalanceAlarm.ipArguments.Clear().AddString(_sName);
_fbSafetyInterlocksNotOkAlarm.ipArguments.Clear().AddString(_sName);
_fbSafetyIntlkTimeoutAlarm.ipArguments.Clear().AddString(_sName);]]></ST>
</Implementation>
</Set>
</Property>
</POU>
</TcPlcObject>