, xWarning=> , rScaledValue=> , xErrorLow=> , xWarningLow=> , xWarningHigh=> , xErrorHigh=> , stHMIInterface=> stHMIInterface.stStringCurrent); // =============================== // Module 1 // =============================== _fbModule1( xEnable := _xEnable, xStartBalancing := _xStartBalancing, xInverterEnabled := _fbInverter.xActive, stModuleVoltageConfig := stStringModuleVoltageConfig.stModule1VoltConfig, xInSafetyCheckMode := xInSafetyCheckMode, xEmergencyStopOk:= xEmergencyStopOk, stHMIInterface:= stHMIInterface.stHMIInterfaceModule1, xReleaseErrors:= xReleaseErrors, xReleaseLimitErrors:= xReleaseLimitErrors AND _xReleaseLimitErrorsInternal, xReleaseManualMode := xReleaseManualMode, xConfirmAlarms:= xConfirmAlarms, rBalancingTargetVoltage := rSmallestSegmentVoltage); // =============================== // Module 2 // =============================== _fbModule2( xEnable := _xEnable, xStartBalancing := _xStartBalancing, xInverterEnabled := _fbInverter.xActive, stModuleVoltageConfig := stStringModuleVoltageConfig.stModule2VoltConfig, xInSafetyCheckMode := xInSafetyCheckMode, xEmergencyStopOk:= xEmergencyStopOk, stHMIInterface:= stHMIInterface.stHMIInterfaceModule2, xReleaseErrors:= xReleaseErrors, xReleaseLimitErrors:= xReleaseLimitErrors AND _xReleaseLimitErrorsInternal, xReleaseManualMode := xReleaseManualMode, xConfirmAlarms:= xConfirmAlarms, rBalancingTargetVoltage := rSmallestSegmentVoltage); // =============================== // Module 3 // =============================== _fbModule3( xEnable := _xEnable, xStartBalancing := _xStartBalancing, xInverterEnabled := _fbInverter.xActive, stModuleVoltageConfig := stStringModuleVoltageConfig.stModule3VoltConfig, xInSafetyCheckMode := xInSafetyCheckMode, xEmergencyStopOk:= xEmergencyStopOk, stHMIInterface:= stHMIInterface.stHMIInterfaceModule3, xReleaseErrors:= xReleaseErrors, xReleaseLimitErrors:= xReleaseLimitErrors AND _xReleaseLimitErrorsInternal, xReleaseManualMode := xReleaseManualMode, xConfirmAlarms:= xConfirmAlarms, rBalancingTargetVoltage := rSmallestSegmentVoltage); // =============================== // Handle modules error state // =============================== _xModuleError := _fbModule1.xError OR _fbModule2.xError OR _fbModule3.xError; // =============================== // 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; // =============================== // 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 // =============================== _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 AND _fbModule2.xInShutdownDischargeMode AND _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; // =============================== // Calculate string voltage // =============================== rCurrentVoltage := _fbModule1.rCurrentVoltage + _fbModule2.rCurrentVoltage + _fbModule3.rCurrentVoltage; stHMIInterface.rVoltage := rCurrentVoltage; // =============================== // 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); END_IF // =============================== // String ready validation check // =============================== _tonResetPulseLength(); _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 // 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 CASE _iState OF 0: // Idle // Start in normal mode IF xEnable AND (NOT xStartBalancing) AND xAllModulesInAutoMode AND xRepairSwitchOk THEN _xEnable := TRUE; _iState := 5; END_IF // Start in balancing mode IF (NOT xEnable) AND xStartBalancing AND xAllModulesInAutoMode THEN _xStartBalancing := TRUE; _xReleaseLimitErrorsInternal := FALSE; _iState := 7; END_IF 5: // Wait for all modules to be ready in normal mode IF _xAllModulesReady AND _xBalanceOk THEN xResetSafetyDCCB := TRUE; IF (NOT xInSafetyCheckMode) THEN _xReleaseLimitErrorsInternal := TRUE; END_IF _tonResetPulseLength.IN := TRUE; _iState := 10; END_IF IF (NOT xEnable) THEN _xEnable := FALSE; _iState := 0; END_IF IF _xModuleError THEN _xEnable := FALSE; _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; _iState := 0; END_IF IF xError THEN _xEnable := FALSE; _iState := 1000; END_IF 10: // Reset safety from sensors IF _tonResetPulseLength.Q THEN _tonResetPulseLength.IN := FALSE; xResetSafetyDCCB := FALSE; _tonSafetyOkTimeout.IN := TRUE; _iState := 15; END_IF 15: // Wait for Safety to be ok IF xSafetyIntlksOk THEN _tonSafetyOkTimeout.IN := FALSE; xCloseDCCB := TRUE; _tonErrorDCCBNotClosed.IN := TRUE; _iState := 20; END_IF IF NOT xEnable THEN _tonSafetyOkTimeout.IN := FALSE; _xEnable := FALSE; _iState := 40; END_IF IF _tonSafetyOkTimeout.Q THEN _tonSafetyOkTimeout.IN := FALSE; xCloseDCCB := TRUE; xError := TRUE; xReady := FALSE; _iState := 1000; END_IF 20: // Check if DC relais closed and safety is ok IF NOT xDCCBOpen THEN _xEnableInverter := TRUE; _rPowerInverterInternal := rPowerInverter; //_rPowerInverterInternal := 0.0; _iState := 21; END_IF IF _tonErrorDCCBNotClosed.Q THEN _xEnable := FALSE; xCloseDCCB := FALSE; _tonErrorDCCBNotClosed.IN := FALSE; xError := TRUE; xReady := FALSE; _iState := 1000; END_IF IF NOT xEnable THEN _tonSafetyOkTimeout.IN := FALSE; _xEnable := FALSE; _iState := 40; END_IF 21: // Wait for inverter to be ready _tonInverterStartupTimeout(IN := TRUE); IF _fbInverter.xActive AND (NOT _fbInverter.xError) THEN _iState := 30; xReady := TRUE; _tonInverterStartupTimeout(IN := FALSE); END_IF IF (NOT xEnable) OR (NOT _xAllModulesReady) THEN _xEnableInverter := FALSE; _rPowerInverterInternal := 0.0; _xEnable := FALSE; _iState := 31; END_IF // Inverter error or timeout for startup IF _fbInverter.xError OR (NOT xRepairSwitchOk) THEN // _tonInverterStartupTimeout.Q 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 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) 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 xError THEN _rPowerInverterInternal := 0.0; _xEnableInverter := FALSE; _iState := 40; END_IF 32: // Shutdown discharge mode IF xShutdownDischargeAllowed 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 := 40; END_IF 40: // Wait for inverter to shut down IF NOT _fbInverter.xActive THEN _iState := 41; END_IF 41: // Debug delay time for inverter shutdown _tonInverterShutdownDelay(IN := TRUE); IF _tonInverterShutdownDelay.Q THEN _tonInverterShutdownDelay(IN := FALSE); xCloseDCCB := FALSE; _iState := 0; END_IF 50: // Wait for balancing of all units to be done IF _xBalancingDone THEN _xStartBalancing := FALSE; _iState := 51; END_IF 51: // Check if start balancing has been releases to avoid a restart IF (NOT xStartBalancing) THEN _iState := 0; END_IF 1000: // Error state _xEnable := FALSE; _xEnableInverter := FALSE; _rPowerInverterInternal := 0.0; xError := TRUE; _xReleaseLimitErrorsInternal := FALSE; // Reset timer _tonResetPulseLength(IN := FALSE); _tonErrorDCCBNotClosed(IN := FALSE); _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 _xModuleError) THEN xError := FALSE; _iState := 0; END_IF END_CASE // Copy inverter data to SCADA interface stHMIInterface.stInverterData := stInverterData; IF _xAllModulesReady AND _xBalanceOk AND (_iState = 30) 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 // =============================== // String status sum // =============================== IF xOff THEN eStatus := E_COMPONENT_STATUS.OFF; END_IF IF xReady THEN eStatus := E_COMPONENT_STATUS.ON; END_IF IF xError THEN eStatus := E_COMPONENT_STATUS.ERROR; END_IF]]>