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
1535 lines
49 KiB
XML
1535 lines
49 KiB
XML
<?xml version="1.0" encoding="utf-8"?>
|
|
<TcPlcObject Version="1.1.0.1" ProductVersion="3.1.4026.12">
|
|
<POU Name="MAIN" Id="{bbd7302c-91ce-4697-9f4b-743f57ca5819}" SpecialFunc="None">
|
|
<Declaration><![CDATA[PROGRAM MAIN
|
|
VAR
|
|
_xEmergencyStopOk AT %I* : BOOL;
|
|
_xShowAckEmergencyStop AT %Q* : BOOL;
|
|
xNAProtectionOK AT %I* : BOOL;
|
|
_xNAProtectionTripped : BOOL := FALSE;
|
|
_xReleaseErrors : BOOL := TRUE;
|
|
_xReleaseLimitsErrors : BOOL := TRUE;
|
|
_xConfirmAlarms : BOOL;
|
|
_xEnableString : BOOL;
|
|
_xReleaseInverterPower : BOOL;
|
|
_xStartBalancing : BOOL;
|
|
|
|
_xCanChangeMode : BOOL := TRUE;
|
|
|
|
{attribute 'OPC.UA.DA' := '0'}
|
|
_afbStrings : ARRAY[0..1] OF FB_String[('String 1'), ('String 2')];
|
|
|
|
// Battery shutdown due to error
|
|
_xErrorShutdown : BOOL := FALSE;
|
|
|
|
// State machine state
|
|
_iState : INT;
|
|
_iStateSafetyCheck : INT;
|
|
_iStateBalancing : INT;
|
|
_iStatePrecharge : INT;
|
|
_iStateDH : INT;
|
|
|
|
// Start safety check mode
|
|
_xStartSafetyCheck : BOOL;
|
|
|
|
// Start precharge mode
|
|
_xStartPrecharge : BOOL;
|
|
|
|
// Auto remote and auto local power request
|
|
_rAutoPowerRequest : REAL;
|
|
|
|
// Internal inverter power
|
|
_rPowerInverter : REAL;
|
|
|
|
// Flag for zero power indication
|
|
_xNoPowerRequested : BOOL;
|
|
|
|
// Startup delay for error release during plc startup
|
|
_tonStartupDelay : TON := (PT := T#10S);
|
|
|
|
// Small delay for inverter shutdown
|
|
_tonBeginShutdown : TON := (PT := T#10S);
|
|
|
|
// Not all strings in automatic mode
|
|
_fbNoAutomaticModeAlarm : FB_TcAlarm;
|
|
|
|
// Emergency stop not ok alarm
|
|
_fbEStopNotOk : FB_TcAlarm;
|
|
|
|
// String 1 Error Mssage
|
|
_fbEtherCATErrorString1 : FB_TcAlarm;
|
|
_stECString1ErrSI : FB_TcSourceInfo;
|
|
|
|
// String 2 Error Mssage
|
|
_fbEtherCATErrorString2 : FB_TcAlarm;
|
|
_stECString2ErrSI : FB_TcSourceInfo;
|
|
|
|
// EMS heartbeat alarm
|
|
_fbEMSHeartbeatAlarm : FB_TcAlarm;
|
|
|
|
// NA Protection Alarm
|
|
_fbNAProtectionAlarm : FB_TcAlarm;
|
|
|
|
// First cycle tag
|
|
_xFirstCycle : BOOL := TRUE;
|
|
|
|
// ADS reader for modbus server data
|
|
_fbADSReader : ADSREAD;
|
|
|
|
// Timer for ADS read
|
|
_timADSReadTimer : TON;
|
|
|
|
// Old EMS lifecount message
|
|
_udiLastEMSLifeMessage : UDINT;
|
|
|
|
// No change in life counter from EMS detected
|
|
_xNoEMSLifeMessageChange : BOOL;
|
|
|
|
// EMS heartbeat not ok signal
|
|
_xEMSHeartbeatNotOK : BOOL;
|
|
_xEMSHeartbeatNotOKLedge : BOOL;
|
|
_rtrigEMSHeartbeakNotOK : R_TRIG;
|
|
|
|
// Error signal for no EMS Heartbeat
|
|
_fbEMSHeartbeatTimeout : FB_ReleaseSignal;
|
|
|
|
// Release EMS heartbeat timeout signal
|
|
_xReleaseEMSHeartbeatError : BOOL;
|
|
|
|
// Release manual mode
|
|
_xReleaseManualMode : BOOL;
|
|
|
|
// Current BMS control mode (Auto local, Auto remote, etc...)
|
|
// On restart star in manual mode (so the ems can not directly start the bms)
|
|
_eBMSControlMode : E_BMS_CONTROL_MODE := E_BMS_CONTROL_MODE.AUTO_LOCAL;
|
|
|
|
// UPS
|
|
_fbUPS : FB_S_UPS_BAPI;
|
|
|
|
// Safety
|
|
{attribute 'analysis' := '-33'}
|
|
xSafetyRun AT %Q* : BOOL := TRUE;
|
|
xSafetyErrAck AT %Q* : BOOL;
|
|
xSafetyResterTaster AT %I* : BOOL;
|
|
|
|
// Hardware reset button
|
|
_xHarwareResetButton AT %I* : BOOL;
|
|
_xShowErrorOnButton AT %Q* : BOOL;
|
|
_tonHardwareResetButton : TON := (PT := T#1S);
|
|
_rtHardwareResetButton : R_TRIG;
|
|
|
|
_xErrorActive : BOOL;
|
|
_xWarningActive : BOOL;
|
|
|
|
// Battery in safety check mode
|
|
_xInSafetyCheckMode : BOOL;
|
|
|
|
// Battery full message
|
|
_fbBatteryFullMessage : FB_TcMessage;
|
|
_fbBatteryEmptyMessage : FB_TcMessage;
|
|
|
|
// Smallest segment voltage
|
|
_rSmallestSegmentVoltage : REAL;
|
|
|
|
// Highest segment voltage
|
|
_rHighestSegmentVoltage : REAL;
|
|
|
|
// Safety
|
|
_fbSafety : FB_Safety;
|
|
|
|
// String EtherCAT state
|
|
_uiEtherCATState AT%I* : UINT;
|
|
_wEtherCATState : WORD;
|
|
_xEtherCatString1Ok : BOOL;
|
|
_xEtherCatString2Ok : BOOL;
|
|
|
|
// Flag to set all components in manual mode
|
|
_xAllComponentsToManualMode : BOOL;
|
|
|
|
// Hardware reset button rising edge trigger
|
|
_fbRTrigHardwareAck : R_TRIG;
|
|
|
|
_fbStringReadyTimeout : TON;
|
|
|
|
// Sum of voltage of all active strings
|
|
_rStringsSumVoltage : REAL;
|
|
_rDeltaUm : REAL;
|
|
_arPowerString : ARRAY[0..(GVL_CONFIG.uiNumberOfStrings-1)] OF REAL;
|
|
|
|
// Temperature sensor SCS String 1
|
|
_fbTempCabinetSCSString1 : FB_AnalogInput('String 1 - SCS - T1_Cabinet');
|
|
|
|
// Temperature sensor SCS String 1
|
|
_fbTempCabinetSCSString2 : FB_AnalogInput('String 2 - SCS - T1_Cabinet');
|
|
|
|
// Temperature sensor BMS cabinet
|
|
_fbTempCabinetBMS : FB_AnalogInput('BMS - T1_Cabinet');
|
|
|
|
_ui : UINT := 0;
|
|
_xStringsReady : BOOL;
|
|
_xStringsErrorActive : BOOL;
|
|
_xStringsInSchutdownDischargeMode : BOOL;
|
|
_xStringsShutdownDischargeAllowed : BOOL;
|
|
_xStringsAllInAutomaticMode : BOOL;
|
|
_xStringsOff : BOOL;
|
|
_xStringsBalancingDone : BOOL;
|
|
_xStringsInAutoMode : BOOL;
|
|
_xStringSafetyComError : BOOL;
|
|
|
|
_eStringOpMode : E_STRING_OPERATING_MODE;
|
|
|
|
_rMaxCurrentInverterDCVoltage : REAL;
|
|
_rMinCurrentInverterDCVoltage : REAL;
|
|
|
|
_wDebug1 : WORD;
|
|
_wDebug2 : WORD;
|
|
|
|
_fbPowerMeterPower : FB_PowerMeter;
|
|
_fbPowerMeter24V : FB_PowerMeter;
|
|
|
|
// Number of activated strings (from configuration)
|
|
_uiNumberOfActiveStrings : UINT;
|
|
|
|
_xGetPowerMeterData : BOOL;
|
|
|
|
_rPowerDH : REAL;
|
|
_fbTONDHCycleTime : TON := (PT := T#15M);
|
|
|
|
// tower light
|
|
_fbTowerLight : FB_TowerLight;
|
|
END_VAR
|
|
]]></Declaration>
|
|
<Implementation>
|
|
<ST><![CDATA[IF _xFirstCycle THEN
|
|
_xFirstCycle := FALSE;
|
|
_xGetPowerMeterData := TRUE;
|
|
|
|
_fbBatteryFullMessage.CreateEx(stEventEntry := TC_EVENTS.BMSEvents.BatteryFull, 0);
|
|
_fbBatteryEmptyMessage.CreateEx(stEventEntry := TC_EVENTS.BMSEvents.BatteryEmpty, 0);
|
|
|
|
_fbEStopNotOk.CreateEx(stEventEntry := TC_EVENTS.BMSEvents.EmergencyStopNotOk, TRUE, 0);
|
|
|
|
_stECString1ErrSI.Clear();
|
|
_stECString1ErrSI.nId := 1;
|
|
_stECString1ErrSI.sName := 'MAIN';
|
|
_fbEtherCATErrorString1.CreateEx(stEventEntry := TC_EVENTS.BMSEvents.EthercatStringError, TRUE, _stECString1ErrSI);
|
|
_fbEtherCATErrorString1.ipArguments.Clear().AddString('1');
|
|
|
|
_stECString2ErrSI.Clear();
|
|
_stECString2ErrSI.nId := 2;
|
|
_stECString2ErrSI.sName := 'MAIN';
|
|
_fbEtherCATErrorString2.CreateEx(stEventEntry := TC_EVENTS.BMSEvents.EthercatStringError, TRUE, _stECString2ErrSI);
|
|
_fbEtherCATErrorString2.ipArguments.Clear().AddString('2');
|
|
|
|
_afbStrings[0].Name := 'String 1';
|
|
_afbStrings[1].Name := 'String 2';
|
|
|
|
_fbEMSHeartbeatAlarm.CreateEx(stEventEntry := TC_EVENTS.BMSEvents.EMSHeartbeatTimeout, TRUE, 0);
|
|
|
|
_fbNAProtectionAlarm.CreateEx(stEventEntry := TC_EVENTS.BMSEvents.NAProtectionTripped, TRUE, 0);
|
|
END_IF
|
|
|
|
// Reset error flag
|
|
_xErrorActive := FALSE;
|
|
|
|
// Reset warning flag
|
|
_xWarningActive := FALSE;
|
|
|
|
// Ack alarms from HMI
|
|
_xConfirmAlarms := GVL_SCADA.stAckAlarmsButton.xRequest;
|
|
IF GVL_SCADA.stAckAlarmsButton.xRequest THEN
|
|
GVL_SCADA.stAckAlarmsButton.xRequest := FALSE;
|
|
END_IF
|
|
|
|
// Ack alarms through modbus
|
|
IF GVL_MODBUS.wConfirmAlarms > 0 THEN
|
|
GVL_MODBUS.wConfirmAlarms := 0;
|
|
_xConfirmAlarms := TRUE;
|
|
END_IF
|
|
|
|
|
|
// Ack alarms from hardware button
|
|
_fbRTrigHardwareAck(CLK := _xHarwareResetButton);
|
|
IF _fbRTrigHardwareAck.Q THEN
|
|
_xConfirmAlarms := TRUE;
|
|
END_IF
|
|
|
|
// ===============================
|
|
// EtherCAT communication error
|
|
// ===============================
|
|
_wEtherCATState := UINT_TO_WORD(_uiEtherCATState);
|
|
_xEtherCatString1Ok := (_wEtherCATState AND 16#8000) = 0;
|
|
_wDebug1 := (_wEtherCATState AND 16#8000);
|
|
_xEtherCatString2Ok := (_wEtherCATState AND 16#2000) = 0;
|
|
_wDebug2 := (_wEtherCATState AND 16#2000);
|
|
|
|
// String 1 on X1 = Port D = 0x8000
|
|
IF (NOT _xEtherCatString1Ok) AND (NOT _fbEtherCATErrorString1.bRaised) THEN
|
|
_fbEtherCATErrorString1.Raise(0);
|
|
GVL_MODBUS.stBMSErrorReg.wBMSErrorActive.stBitmap.bEthercat := 1;
|
|
END_IF
|
|
|
|
IF _fbEtherCATErrorString1.bRaised AND (_xEtherCatString1Ok) THEN
|
|
_fbEtherCATErrorString1.Clear(0, FALSE);
|
|
END_IF
|
|
|
|
IF _fbEtherCATErrorString1.eConfirmationState = TcEventConfirmationState.WaitForConfirmation AND _xConfirmAlarms THEN
|
|
_fbEtherCATErrorString1.Confirm(0);
|
|
END_IF
|
|
|
|
// String 2 on X2 = Port B = 0x2000
|
|
IF (NOT _xEtherCatString2Ok) AND (NOT _fbEtherCATErrorString2.bRaised) THEN
|
|
_fbEtherCATErrorString2.Raise(0);
|
|
GVL_MODBUS.stBMSErrorReg.wBMSErrorActive.stBitmap.bEthercat := 1;
|
|
END_IF
|
|
|
|
IF _fbEtherCATErrorString2.bRaised AND _xEtherCatString2Ok THEN
|
|
_fbEtherCATErrorString2.Clear(0, FALSE);
|
|
END_IF
|
|
|
|
IF _fbEtherCATErrorString2.eConfirmationState = TcEventConfirmationState.WaitForConfirmation AND _xConfirmAlarms THEN
|
|
_fbEtherCATErrorString2.Confirm(0);
|
|
END_IF
|
|
|
|
|
|
// ===============================
|
|
// Safety
|
|
// ===============================
|
|
xSafetyErrAck := xSafetyResterTaster;
|
|
_xShowAckEmergencyStop := (NOT _xEmergencyStopOk) OR _xStringSafetyComError;
|
|
|
|
IF (NOT _xEmergencyStopOk) AND (NOT _fbEStopNotOk.bRaised) THEN
|
|
_fbEStopNotOk.Raise(0);
|
|
END_IF
|
|
|
|
IF _xEmergencyStopOk AND _fbEStopNotOk.bRaised THEN
|
|
_fbEStopNotOk.Clear(0, FALSE);
|
|
END_IF
|
|
|
|
IF _fbEStopNotOk.eConfirmationState = TcEventConfirmationState.WaitForConfirmation AND _xConfirmAlarms THEN
|
|
_fbEStopNotOk.Confirm(0);
|
|
END_IF
|
|
|
|
GVL_MODBUS.stBMSErrorReg.wBMSErrorActive.stBitmap.bEStop := (NOT _xEmergencyStopOk) OR _fbEStopNotOk.bRaised OR (_fbEStopNotOk.eConfirmationState <> TcEventConfirmationState.Confirmed);
|
|
|
|
// ===============================
|
|
// handle NA Protection
|
|
// ===============================
|
|
IF _xConfirmAlarms THEN
|
|
_xNAProtectionTripped := FALSE;
|
|
END_IF
|
|
|
|
IF NOT xNAProtectionOK THEN
|
|
_xNAProtectionTripped := TRUE;
|
|
IF NOT _fbNAProtectionAlarm.bRaised THEN
|
|
_fbNAProtectionAlarm.Raise();
|
|
END_IF
|
|
END_IF
|
|
|
|
IF _xNAProtectionTripped THEN
|
|
_xErrorActive := TRUE;
|
|
END_IF
|
|
|
|
IF xNAProtectionOK AND _fbNAProtectionAlarm.bRaised THEN
|
|
_fbNAProtectionAlarm.Clear(0, FALSE);
|
|
END_IF
|
|
|
|
IF _fbNAProtectionAlarm.eConfirmationState = TcEventConfirmationState.WaitForConfirmation AND _xConfirmAlarms THEN
|
|
_fbNAProtectionAlarm.Confirm(0);
|
|
END_IF
|
|
|
|
GVL_MODBUS.stBMSErrorReg.wBMSErrorActive.stBitmap.bNAProtectionTripped := _xNAProtectionTripped;
|
|
|
|
// ===============================
|
|
// Hardware reset button part 1
|
|
// ===============================
|
|
_tonHardwareResetButton(IN := _xHarwareResetButton);
|
|
_rtHardwareResetButton(CLK := _tonHardwareResetButton.Q);
|
|
// _xConfirmAlarms := TRUE;
|
|
|
|
// ===============================
|
|
// Handle Manual mode release
|
|
// ===============================
|
|
//IF _iState = 0 THEN
|
|
// _xReleaseManualMode := TRUE;
|
|
//ELSE
|
|
// _xReleaseManualMode := FALSE;
|
|
//END_IF
|
|
|
|
|
|
// ===============================
|
|
// Handle UPS events
|
|
// ===============================
|
|
_fbUPS(
|
|
sNetID:= '',
|
|
iPLCPort:= 851,
|
|
tTimeout:= DEFAULT_ADS_TIMEOUT,
|
|
eUpsMode:= eSUPS_WrPersistData_Shutdown,
|
|
ePersistentMode:= SPDM_2PASS,
|
|
tRecoverTime:= T#10S,
|
|
bPowerFailDetect=> ,
|
|
eState=> );
|
|
|
|
|
|
IF _xFirstCycle THEN
|
|
_xFirstCycle := FALSE;
|
|
_afbStrings[0].Name := 'String 1';
|
|
_afbStrings[1].Name := 'String 2';
|
|
END_IF
|
|
|
|
// Dely release of errors during PLC startup phase
|
|
_tonStartupDelay(IN := TRUE);
|
|
|
|
// ===============================
|
|
// Temperature sensor control cabinet BMS
|
|
// ===============================
|
|
_fbTempCabinetBMS(
|
|
stScalingConfig:= GVL_CONFIG.stConfigCabinetTemp,
|
|
stEWConfig:= GVL_CONFIG.stEWLCabinetTemp,
|
|
stEWDelayConfig:= GVL_CONFIG.stEWDCabinetTemp,
|
|
xReleaseErrors:= _xReleaseErrors,
|
|
xReleaseLimitErrors:= _xReleaseLimitsErrors,
|
|
xReleaseHardwareErrors:= _xReleaseErrors,
|
|
xConfirmAlarms:= _xConfirmAlarms,
|
|
stHMIInterface=> GVL_SCADA.stTempCabinetBMS);
|
|
|
|
// Set modbus error register bit
|
|
IF _fbTempCabinetBMS.xWarningHigh THEN
|
|
GVL_MODBUS.stBMSErrorReg.wBMSWarningActive.stBitmap.bTCabinetBMSHigh := TRUE;
|
|
END_IF
|
|
|
|
IF _fbTempCabinetBMS.xWarning THEN
|
|
_xWarningActive := TRUE;
|
|
END_IF
|
|
|
|
IF _fbTempCabinetBMS.xError THEN
|
|
_xErrorActive := TRUE;
|
|
END_IF
|
|
|
|
// Call string 1
|
|
_afbStrings[0](
|
|
xEnable := _xEnableString AND GVL_CONFIG.axStringEnabled[0] AND xNAProtectionOK,
|
|
xReleaseInverterPower := _xReleaseInverterPower,
|
|
uiStringNumber := 0,
|
|
eOperationMode := _eStringOpMode,
|
|
xErrorShutdown := _xErrorShutdown,
|
|
xStartBalancing := _xStartBalancing,
|
|
sInverterIP := GVL_CONFIG.sInverterIpString1,
|
|
rPowerInverter := _arPowerString[0],
|
|
xInSafetyCheckMode := _xInSafetyCheckMode,
|
|
stHMIInterface:= GVL_SCADA.stHMIInterface[0],
|
|
xEmergencyStopOk:= _xEmergencyStopOk,
|
|
xReleaseErrors:= _xReleaseErrors AND _tonStartupDelay.Q AND _xEtherCatString1Ok AND GVL_CONFIG.axStringEnabled[0],
|
|
xReleaseLimitErrors:= _xReleaseLimitsErrors AND _tonStartupDelay.Q AND _xEtherCatString1Ok,
|
|
xReleaseManualMode := _xReleaseManualMode,
|
|
xConfirmAlarms:= _xConfirmAlarms,
|
|
xAllToManualMode := _xAllComponentsToManualMode,
|
|
xResetSafety := xSafetyResterTaster,
|
|
refuStringErrorsModbus := GVL_MODBUS.stBMSErrorReg.wString1ErrorActive);
|
|
|
|
IF _afbStrings[0].xWarning THEN
|
|
_xWarningActive := TRUE;
|
|
END_IF
|
|
|
|
IF _afbStrings[0].xError THEN
|
|
_xErrorActive := TRUE;
|
|
END_IF
|
|
|
|
// ===============================
|
|
// Temperature sensor control cabinet SCS string 1
|
|
// ===============================
|
|
_fbTempCabinetSCSString1(
|
|
stScalingConfig:= GVL_CONFIG.stConfigCabinetTemp,
|
|
stEWConfig:= GVL_CONFIG.stEWLCabinetTemp,
|
|
stEWDelayConfig:= GVL_CONFIG.stEWDCabinetTemp,
|
|
xReleaseErrors:= _xReleaseErrors,
|
|
xReleaseLimitErrors:= _xReleaseLimitsErrors,
|
|
xReleaseHardwareErrors:= _xReleaseErrors,
|
|
xConfirmAlarms:= _xConfirmAlarms,
|
|
stHMIInterface=> GVL_SCADA.stHMIInterface[0].stTempCabinetSCS);
|
|
|
|
IF _fbTempCabinetSCSString1.xWarning THEN
|
|
_xWarningActive := TRUE;
|
|
END_IF
|
|
|
|
IF _fbTempCabinetSCSString1.xError THEN
|
|
_xErrorActive := TRUE;
|
|
END_IF
|
|
|
|
|
|
// Handle string 1 modbus error and warning
|
|
GVL_MODBUS.stBMSErrorReg.wStringErrorActive.0 := _afbStrings[0].xError;
|
|
GVL_MODBUS.stBMSErrorReg.wStringWarningActive.0 := _afbStrings[0].xWarning;
|
|
GVL_MODBUS.stBMSErrorReg.wString1ErrorActive.stBitmap.bDCSwitch := (NOT _afbStrings[0].xRepairSwitchOk);
|
|
GVL_MODBUS.stBMSErrorReg.wBMSWarningActive.stBitmap.bSafetyIntlkString1 := (NOT _afbStrings[0].xSafetyIntlksOk);
|
|
GVL_MODBUS.stBMSErrorReg.wBMSWarningActive.stBitmap.bTCabinetString1Module1High := _afbStrings[0].xTempCabinetModule1Warning;
|
|
GVL_MODBUS.stBMSErrorReg.wBMSWarningActive.stBitmap.bTCabinetString1Module2High := _afbStrings[0].xTempCabinetModule2Warning;
|
|
GVL_MODBUS.stBMSErrorReg.wBMSWarningActive.stBitmap.bTCabinetString1Module3High := _afbStrings[0].xTempCabinetModule3Warning;
|
|
GVL_MODBUS.stBMSErrorReg.wBMSWarningActive.stBitmap.bTCabinetSCSString1High := _fbTempCabinetSCSString1.xWarningHigh;
|
|
|
|
|
|
// Call string 2
|
|
_afbStrings[1](
|
|
xEnable := _xEnableString AND GVL_CONFIG.axStringEnabled[1] AND xNAProtectionOK,
|
|
xReleaseInverterPower := _xReleaseInverterPower,
|
|
uiStringNumber := 1,
|
|
eOperationMode := _eStringOpMode,
|
|
xErrorShutdown := _xErrorShutdown,
|
|
xStartBalancing := _xStartBalancing,
|
|
sInverterIP := GVL_CONFIG.sInverterIpString2,
|
|
rPowerInverter := _arPowerString[1],
|
|
xInSafetyCheckMode := _xInSafetyCheckMode,
|
|
stHMIInterface:= GVL_SCADA.stHMIInterface[1],
|
|
xEmergencyStopOk:= _xEmergencyStopOk,
|
|
xReleaseErrors:= _xReleaseErrors AND _tonStartupDelay.Q AND _xEtherCatString2Ok AND GVL_CONFIG.axStringEnabled[1],
|
|
xReleaseLimitErrors:= _xReleaseLimitsErrors AND _tonStartupDelay.Q AND _xEtherCatString2Ok,
|
|
xReleaseManualMode := _xReleaseManualMode,
|
|
xConfirmAlarms:= _xConfirmAlarms,
|
|
xAllToManualMode := _xAllComponentsToManualMode,
|
|
xResetSafety := xSafetyResterTaster,
|
|
refuStringErrorsModbus := GVL_MODBUS.stBMSErrorReg.wString2ErrorActive);
|
|
|
|
IF _afbStrings[1].xWarning THEN
|
|
_xWarningActive := TRUE;
|
|
END_IF
|
|
|
|
IF _afbStrings[1].xError THEN
|
|
_xErrorActive := TRUE;
|
|
END_IF
|
|
|
|
// ===============================
|
|
// Temperature sensor control cabinet SCS string 2
|
|
// ===============================
|
|
_fbTempCabinetSCSString2(
|
|
stScalingConfig:= GVL_CONFIG.stConfigCabinetTemp,
|
|
stEWConfig:= GVL_CONFIG.stEWLCabinetTemp,
|
|
stEWDelayConfig:= GVL_CONFIG.stEWDCabinetTemp,
|
|
xReleaseErrors:= _xReleaseErrors,
|
|
xReleaseLimitErrors:= _xReleaseLimitsErrors,
|
|
xReleaseHardwareErrors:= _xReleaseErrors,
|
|
xConfirmAlarms:= _xConfirmAlarms,
|
|
stHMIInterface=> GVL_SCADA.stHMIInterface[1].stTempCabinetSCS);
|
|
|
|
IF _fbTempCabinetSCSString2.xWarning THEN
|
|
_xWarningActive := TRUE;
|
|
END_IF
|
|
|
|
IF _fbTempCabinetSCSString2.xError THEN
|
|
_xErrorActive := TRUE;
|
|
END_IF
|
|
|
|
// Handle string 1 modbus error and warning
|
|
GVL_MODBUS.stBMSErrorReg.wStringErrorActive.1 := _afbStrings[1].xError;
|
|
GVL_MODBUS.stBMSErrorReg.wStringWarningActive.1 := _afbStrings[1].xWarning;
|
|
GVL_MODBUS.stBMSErrorReg.wString2ErrorActive.stBitmap.bDCSwitch := (NOT _afbStrings[1].xRepairSwitchOk);
|
|
GVL_MODBUS.stBMSErrorReg.wBMSWarningActive.stBitmap.bSafetyIntlkString2 := (NOT _afbStrings[1].xSafetyIntlksOk);
|
|
GVL_MODBUS.stBMSErrorReg.wBMSWarningActive.stBitmap.bTCabinetString2Module1High := _afbStrings[1].xTempCabinetModule1Warning;
|
|
GVL_MODBUS.stBMSErrorReg.wBMSWarningActive.stBitmap.bTCabinetString2Module2High := _afbStrings[1].xTempCabinetModule2Warning;
|
|
GVL_MODBUS.stBMSErrorReg.wBMSWarningActive.stBitmap.bTCabinetString2Module3High := _afbStrings[1].xTempCabinetModule3Warning;
|
|
GVL_MODBUS.stBMSErrorReg.wBMSWarningActive.stBitmap.bTCabinetSCSString2High := _fbTempCabinetSCSString2.xWarningHigh;
|
|
|
|
// Copy general error to modbus error register
|
|
GVL_MODBUS.stBMSErrorReg.wBMSErrorActive.stBitmap.bError := _xErrorActive;
|
|
|
|
|
|
// ===============================
|
|
// Get global string status information
|
|
// ===============================
|
|
_xStringsReady := TRUE;
|
|
_xStringsErrorActive := FALSE;
|
|
_xStringsInSchutdownDischargeMode := FALSE;
|
|
_xStringsShutdownDischargeAllowed := TRUE;
|
|
_xStringsAllInAutomaticMode := TRUE;
|
|
_xStringsOff := TRUE;
|
|
_xStringsBalancingDone := TRUE;
|
|
_rMaxCurrentInverterDCVoltage := 0.0;
|
|
_rMinCurrentInverterDCVoltage := 10_000;
|
|
_rHighestSegmentVoltage := 0.0;
|
|
_rSmallestSegmentVoltage := 1_000.0;
|
|
_xStringsInAutoMode := TRUE;
|
|
_uiNumberOfActiveStrings := 0;
|
|
_rStringsSumVoltage := 0;
|
|
_xStringSafetyComError := FALSE;
|
|
|
|
FOR _ui := 0 TO (GVL_CONFIG.uiNumberOfStrings-1) DO
|
|
// Check for safety com error
|
|
IF _afbStrings[_ui].xSafetyComError THEN
|
|
_xStringSafetyComError := TRUE;
|
|
END_IF
|
|
|
|
|
|
// Ignore deactivated strings
|
|
IF (NOT GVL_CONFIG.axStringEnabled[_ui]) THEN
|
|
CONTINUE;
|
|
END_IF
|
|
|
|
// Count number of active strings
|
|
_uiNumberOfActiveStrings := _uiNumberOfActiveStrings + 1;
|
|
|
|
// Check ready state
|
|
IF (NOT _afbStrings[_ui].xReady) THEN
|
|
_xStringsReady := FALSE;
|
|
END_IF
|
|
|
|
// Check error state
|
|
IF _afbStrings[_ui].xError THEN
|
|
_xStringsErrorActive := TRUE;
|
|
END_IF
|
|
|
|
// Check for shutdown discharge mode
|
|
IF _afbStrings[_ui].xInShutdownDischargeMode THEN
|
|
_xStringsInSchutdownDischargeMode := TRUE;
|
|
END_IF
|
|
|
|
// Check for shutdown discharge allowed
|
|
IF (NOT _afbStrings[_ui].xShutdownDischargeAllowed) THEN
|
|
_xStringsShutdownDischargeAllowed := FALSE;
|
|
END_IF
|
|
|
|
// Check for all in automatic mode
|
|
IF (NOT _afbStrings[_ui].xAllModulesInAutoMode) THEN
|
|
_xStringsAllInAutomaticMode := FALSE;
|
|
END_IF
|
|
|
|
// Check for all strings off
|
|
IF (NOT _afbStrings[_ui].xOff) THEN
|
|
_xStringsOff := FALSE;
|
|
END_IF
|
|
|
|
// check balancing done
|
|
IF (NOT _afbStrings[_ui].xBalancingDone) THEN
|
|
_xStringsBalancingDone := FALSE;
|
|
END_IF
|
|
|
|
// Check for max dc voltage
|
|
IF _rMaxCurrentInverterDCVoltage < _afbStrings[_ui].stInverterData.rActDCVoltage THEN
|
|
_rMaxCurrentInverterDCVoltage := _afbStrings[_ui].stInverterData.rActDCVoltage;
|
|
END_IF
|
|
|
|
// Check for min DC voltage
|
|
IF _rMinCurrentInverterDCVoltage > _afbStrings[_ui].stInverterData.rActDCVoltage THEN
|
|
_rMinCurrentInverterDCVoltage := _afbStrings[_ui].stInverterData.rActDCVoltage;
|
|
END_IF
|
|
|
|
// Calculate highest segment voltage
|
|
IF _rSmallestSegmentVoltage > _afbStrings[_ui].rSmallestSegmentVoltage THEN
|
|
_rSmallestSegmentVoltage := _afbStrings[_ui].rSmallestSegmentVoltage;
|
|
END_IF
|
|
|
|
// Calculate lowest segment voltage
|
|
IF _rHighestSegmentVoltage < _afbStrings[_ui].rHighestSegmentVoltage THEN
|
|
_rHighestSegmentVoltage := _afbStrings[_ui].rHighestSegmentVoltage;
|
|
END_IF
|
|
|
|
// Calculate sum voltage
|
|
_rStringsSumVoltage := _rStringsSumVoltage + _afbStrings[_ui].rCurrentVoltage;
|
|
|
|
// Get auto mode status
|
|
IF NOT _afbStrings[_ui].xAllModulesInAutoMode THEN
|
|
_xStringsInAutoMode := FALSE;
|
|
END_IF
|
|
END_FOR
|
|
|
|
// ===============================
|
|
// Calculate sum power for string balancing
|
|
// ===============================
|
|
// _rStringsSumVoltage := _afbStrings[0].rCurrentVoltage + _afbStrings[1].rCurrentVoltage;
|
|
|
|
|
|
// ===============================
|
|
// Hardware reset button part 2
|
|
// ===============================
|
|
_xShowErrorOnButton := _xErrorActive;
|
|
|
|
// HMI Feedback
|
|
(*
|
|
GVL_SCADA.stHMIInterface[0].rVoltage := _afbStrings[0].rCurrentVoltage;
|
|
IF _afbStrings[0].eStatus = E_COMPONENT_STATUS.ON THEN
|
|
IF _iState = 30 AND _rPowerInverter > 0 THEN
|
|
GVL_SCADA.stHMIInterface[0].eStatus := E_COMPONENT_STATUS.DISCHARGING;
|
|
ELSIF _iState = 30 AND _rPowerInverter < 0 THEN
|
|
GVL_SCADA.stHMIInterface[0].eStatus := E_COMPONENT_STATUS.CHARGING;
|
|
ELSE
|
|
GVL_SCADA.stHMIInterface[0].eStatus :=_afbStrings[0].eStatus;
|
|
END_IF
|
|
ELSE
|
|
GVL_SCADA.stHMIInterface[0].eStatus :=_afbStrings[0].eStatus;
|
|
END_IF
|
|
*)
|
|
|
|
// ===============================
|
|
// Read modbus request count
|
|
// ===============================
|
|
_timADSReadTimer(IN := NOT _fbADSReader.BUSY, PT := T#200MS);
|
|
_fbADSReader(
|
|
NETID:= '',
|
|
PORT:= 10500,
|
|
IDXGRP:= 16#2000,
|
|
IDXOFFS:= 1,
|
|
LEN:= 4,
|
|
DESTADDR:= ADR(GVL_MODBUS.stModbusEMSComm.stModbusReg11.udiLifeMessage),
|
|
READ:= _timADSReadTimer.Q,
|
|
TMOUT:= T#1S,
|
|
BUSY=> ,
|
|
ERR=> ,
|
|
ERRID=> );
|
|
|
|
// Check for change in life count messages
|
|
// If changed, we have a valid communication with the EMS
|
|
IF ABS(GVL_MODBUS.stModbusEMSComm.stModbusReg11.udiLifeMessage - _udiLastEMSLifeMessage) > 0 THEN
|
|
_xNoEMSLifeMessageChange := FALSE;
|
|
ELSE
|
|
_xNoEMSLifeMessageChange := TRUE;
|
|
END_IF
|
|
_udiLastEMSLifeMessage := GVL_MODBUS.stModbusEMSComm.stModbusReg11.udiLifeMessage;
|
|
|
|
// Create Heartbeat timeout signal with delay
|
|
_fbEMSHeartbeatTimeout(
|
|
xSignal:= _xNoEMSLifeMessageChange,
|
|
xRelease:= _xReleaseEMSHeartbeatError,
|
|
timOnDelay:= GVL_CONFIG.timEMSHeartbeatTimeout,
|
|
timOffDelay:= T#0S,
|
|
xReleaseSignal=> _xEMSHeartbeatNotOK);
|
|
|
|
// EMS Heartbeat timeout error message handling
|
|
IF _xEMSHeartbeatNotOK AND (NOT _fbEMSHeartbeatAlarm.bRaised) THEN
|
|
_fbEMSHeartbeatAlarm.Raise(0);
|
|
END_IF
|
|
|
|
IF (NOT _xEMSHeartbeatNotOK) AND _fbEMSHeartbeatAlarm.bRaised THEN
|
|
_fbEMSHeartbeatAlarm.Clear(0, FALSE);
|
|
END_IF
|
|
|
|
IF _xConfirmAlarms AND _fbEMSHeartbeatAlarm.eConfirmationState = TcEventConfirmationState.WaitForConfirmation THEN
|
|
_fbEMSHeartbeatAlarm.Confirm(0);
|
|
END_IF
|
|
|
|
// Ledge heartbeatNOK
|
|
_rtrigEMSHeartbeakNotOK(CLK := _xEMSHeartbeatNotOK);
|
|
IF _rtrigEMSHeartbeakNotOK.Q THEN
|
|
_xEMSHeartbeatNotOKLedge := TRUE;
|
|
END_IF
|
|
|
|
IF NOT _xEMSHeartbeatNotOK AND _xConfirmAlarms THEN
|
|
_xEMSHeartbeatNotOKLedge := FALSE;
|
|
END_IF
|
|
|
|
// ===============================
|
|
// Copy data to modbus registers
|
|
// ===============================
|
|
// heartbeat
|
|
GVL_MODBUS.stBMSErrorReg.wBMSErrorActive.stBitmap.bEMSHeartbeatError := _xEMSHeartbeatNotOKLedge;
|
|
// Modbus current inverter values
|
|
GVL_MODBUS.stModbusEMSComm.stModbusReg11.diCurrentActivePower := 0;
|
|
GVL_MODBUS.stModbusEMSComm.stModbusReg11.diCurrentReactivePower := 0;
|
|
GVL_MODBUS.stModbusEMSComm.stModbusReg11.diTotalACCurrentPhase1 := 0;
|
|
GVL_MODBUS.stModbusEMSComm.stModbusReg11.diTotalACCurrentPhase2 := 0;
|
|
GVL_MODBUS.stModbusEMSComm.stModbusReg11.diTotalACCurrentPhase3 := 0;
|
|
FOR _ui := 0 TO (GVL_CONFIG.uiNumberOfStrings-1) DO
|
|
GVL_MODBUS.stModbusEMSComm.stModbusReg11.diCurrentActivePower := GVL_MODBUS.stModbusEMSComm.stModbusReg11.diCurrentActivePower + REAL_TO_DINT(_afbStrings[_ui].stInverterData.rActACPower);
|
|
GVL_MODBUS.stModbusEMSComm.stModbusReg11.diCurrentReactivePower := GVL_MODBUS.stModbusEMSComm.stModbusReg11.diCurrentReactivePower + REAL_TO_DINT(_afbStrings[_ui].stInverterData.rActReactivePower);
|
|
GVL_MODBUS.stModbusEMSComm.stModbusReg11.diTotalACCurrentPhase1 := GVL_MODBUS.stModbusEMSComm.stModbusReg11.diTotalACCurrentPhase1 + REAL_TO_DINT(_afbStrings[_ui].stInverterData.rActtACPhaseACurrent);
|
|
GVL_MODBUS.stModbusEMSComm.stModbusReg11.diTotalACCurrentPhase2 := GVL_MODBUS.stModbusEMSComm.stModbusReg11.diTotalACCurrentPhase2 + REAL_TO_DINT(_afbStrings[_ui].stInverterData.rActtACPhaseBCurrent);
|
|
GVL_MODBUS.stModbusEMSComm.stModbusReg11.diTotalACCurrentPhase3 := GVL_MODBUS.stModbusEMSComm.stModbusReg11.diTotalACCurrentPhase3 + REAL_TO_DINT(_afbStrings[_ui].stInverterData.rActtACPhaseCCurrent);
|
|
END_FOR
|
|
|
|
// Set Modbus mirror values
|
|
GVL_MODBUS.stModbusEMSComm.stModbusReg11.diSetpointActivePowerMirror := GVL_MODBUS.stModbusEMSComm.stModbusReg12.diSetpointActivePower;
|
|
GVL_MODBUS.stModbusEMSComm.stModbusReg11.rSetpointCosPhiMirror := GVL_MODBUS.stModbusEMSComm.stModbusReg12.rSetpointCosPhi;
|
|
|
|
// ===============================
|
|
// State machine
|
|
// ===============================
|
|
CASE _eBMSControlMode OF
|
|
E_BMS_CONTROL_MODE.AUTO_REMOTE:
|
|
_eStringOpMode := E_STRING_OPERATING_MODE.AUTOMATIC;
|
|
_xAllComponentsToManualMode := FALSE;
|
|
_xInSafetyCheckMode := FALSE;
|
|
_xReleaseManualMode := FALSE;
|
|
_xReleaseEMSHeartbeatError := TRUE;
|
|
|
|
// Only set power to EMS requested power if the EMS heartbeat is ok
|
|
// Otherwise shutdown battery
|
|
IF (NOT _xEMSHeartbeatNotOKLedge) AND _iState <> 1010 THEN
|
|
_rAutoPowerRequest := DINT_TO_REAL(GVL_MODBUS.stModbusEMSComm.stModbusReg12.diSetpointActivePower);
|
|
ELSIF _iState = 1010 THEN
|
|
_rAutoPowerRequest := 0;
|
|
ELSE
|
|
_rAutoPowerRequest := 0;
|
|
_xErrorShutdown := TRUE;
|
|
_iState := 1000;
|
|
END_IF
|
|
|
|
IF (GVL_SCADA.eRequestedControlMode <> _eBMSControlMode) AND (GVL_SCADA.xCanChangeControlMode) THEN
|
|
_eBMSControlMode := GVL_SCADA.eRequestedControlMode;
|
|
END_IF
|
|
SM_AUTO();
|
|
|
|
E_BMS_CONTROL_MODE.AUTO_LOCAL:
|
|
_eStringOpMode := E_STRING_OPERATING_MODE.AUTOMATIC;
|
|
_xAllComponentsToManualMode := FALSE;
|
|
_xInSafetyCheckMode := FALSE;
|
|
_xReleaseManualMode := FALSE;
|
|
_xReleaseEMSHeartbeatError := FALSE;
|
|
GVL_MODBUS.stModbusEMSComm.stModbusReg11.eBatteryStatus := E_BATTERY_STATUS.TESTING;
|
|
_rAutoPowerRequest := DINT_TO_REAL(GVL_SCADA.stAutomaticModeHMI.diSetpointAutomatic);
|
|
IF (GVL_SCADA.eRequestedControlMode <> _eBMSControlMode) AND (GVL_SCADA.xCanChangeControlMode) THEN
|
|
_eBMSControlMode := GVL_SCADA.eRequestedControlMode;
|
|
END_IF
|
|
SM_AUTO();
|
|
|
|
E_BMS_CONTROL_MODE.MANUAL:
|
|
_eStringOpMode := E_STRING_OPERATING_MODE.AUTOMATIC;
|
|
_xAllComponentsToManualMode := TRUE;
|
|
_xInSafetyCheckMode := FALSE;
|
|
_xReleaseManualMode := TRUE;
|
|
_xReleaseEMSHeartbeatError := FALSE;
|
|
GVL_MODBUS.stModbusEMSComm.stModbusReg11.eBatteryStatus := E_BATTERY_STATUS.MAINTENANCE;
|
|
IF (GVL_SCADA.eRequestedControlMode <> _eBMSControlMode) AND (GVL_SCADA.xCanChangeControlMode) THEN
|
|
_eBMSControlMode := GVL_SCADA.eRequestedControlMode;
|
|
END_IF
|
|
SM_MANUAL();
|
|
|
|
E_BMS_CONTROL_MODE.SAFETY_CHECK:
|
|
_eStringOpMode := E_STRING_OPERATING_MODE.SAFETY_CHECK;
|
|
_xAllComponentsToManualMode := FALSE;
|
|
_xInSafetyCheckMode := TRUE;
|
|
_xReleaseManualMode := FALSE;
|
|
_xReleaseEMSHeartbeatError := FALSE;
|
|
GVL_MODBUS.stModbusEMSComm.stModbusReg11.eBatteryStatus := E_BATTERY_STATUS.MAINTENANCE;
|
|
IF (GVL_SCADA.eRequestedControlMode <> _eBMSControlMode) AND (GVL_SCADA.xCanChangeControlMode) THEN
|
|
_eBMSControlMode := GVL_SCADA.eRequestedControlMode;
|
|
END_IF
|
|
SM_SAFETY_CHECK();
|
|
|
|
E_BMS_CONTROL_MODE.CAPACITY_TEST:
|
|
_eStringOpMode := E_STRING_OPERATING_MODE.AUTOMATIC;
|
|
_xAllComponentsToManualMode := FALSE;
|
|
_xInSafetyCheckMode := FALSE;
|
|
_xReleaseManualMode := FALSE;
|
|
_xReleaseEMSHeartbeatError := FALSE;
|
|
GVL_MODBUS.stModbusEMSComm.stModbusReg11.eBatteryStatus := E_BATTERY_STATUS.TESTING;
|
|
IF (GVL_SCADA.eRequestedControlMode <> _eBMSControlMode) AND (GVL_SCADA.xCanChangeControlMode) THEN
|
|
_eBMSControlMode := GVL_SCADA.eRequestedControlMode;
|
|
END_IF
|
|
SM_CAPACITY_TEST();
|
|
|
|
E_BMS_CONTROL_MODE.BALANCING:
|
|
_eStringOpMode := E_STRING_OPERATING_MODE.BALANCING;
|
|
_xAllComponentsToManualMode := FALSE;
|
|
_xInSafetyCheckMode := FALSE;
|
|
_xReleaseManualMode := FALSE;
|
|
_xReleaseEMSHeartbeatError := FALSE;
|
|
IF (GVL_SCADA.eRequestedControlMode <> _eBMSControlMode) AND (GVL_SCADA.xCanChangeControlMode) THEN
|
|
_xStartBalancing := FALSE;
|
|
_eBMSControlMode := GVL_SCADA.eRequestedControlMode;
|
|
END_IF
|
|
SM_BALANCING();
|
|
|
|
E_BMS_CONTROL_MODE.CYCLING:
|
|
_eStringOpMode := E_STRING_OPERATING_MODE.AUTOMATIC;
|
|
_xAllComponentsToManualMode := FALSE;
|
|
_xInSafetyCheckMode := FALSE;
|
|
_xReleaseManualMode := FALSE;
|
|
_xReleaseEMSHeartbeatError := FALSE;
|
|
//IF (_iState <> 30) OR GVL_SCADA.stAutomaticModeHMI.diSetpointAutomatic = 0 THEN
|
|
_rAutoPowerRequest := DINT_TO_REAL(GVL_SCADA.stAutomaticModeHMI.diSetpointAutomatic);
|
|
//END_IF
|
|
IF (GVL_SCADA.eRequestedControlMode <> _eBMSControlMode) AND (GVL_SCADA.xCanChangeControlMode) THEN
|
|
_eBMSControlMode := GVL_SCADA.eRequestedControlMode;
|
|
END_IF
|
|
SM_AUTO();
|
|
GVL_SCADA.stAutomaticModeHMI.diSetpointAutomatic := REAL_TO_DINT(_rAutoPowerRequest);
|
|
|
|
E_BMS_CONTROL_MODE.PRECHARGE:
|
|
_eStringOpMode := E_STRING_OPERATING_MODE.PRECHARGE;
|
|
_xAllComponentsToManualMode := FALSE;
|
|
_xInSafetyCheckMode := FALSE;
|
|
_xReleaseManualMode := FALSE;
|
|
_xReleaseEMSHeartbeatError := FALSE;
|
|
GVL_MODBUS.stModbusEMSComm.stModbusReg11.eBatteryStatus := E_BATTERY_STATUS.MAINTENANCE;
|
|
IF (GVL_SCADA.eRequestedControlMode <> _eBMSControlMode) AND (GVL_SCADA.xCanChangeControlMode) THEN
|
|
_eBMSControlMode := GVL_SCADA.eRequestedControlMode;
|
|
END_IF
|
|
SM_PRECHARGE();
|
|
|
|
E_BMS_CONTROL_MODE.DH:
|
|
_eStringOpMode := E_STRING_OPERATING_MODE.AUTOMATIC;
|
|
_xAllComponentsToManualMode := FALSE;
|
|
_xInSafetyCheckMode := FALSE;
|
|
_xReleaseManualMode := FALSE;
|
|
_xReleaseEMSHeartbeatError := FALSE;
|
|
|
|
// Goto error state if a string has an error
|
|
IF _xStringsErrorActive THEN
|
|
_iStateDH := 1000;
|
|
END_IF
|
|
|
|
CASE _iStateDH OF
|
|
0: // Idle, wait for command
|
|
_rAutoPowerRequest := DINT_TO_REAL(GVL_SCADA.stAutomaticModeHMI.diSetpointAutomatic);
|
|
IF (ABS(_rAutoPowerRequest) > 100.0) THEN
|
|
_rPowerDH := _rAutoPowerRequest;
|
|
_fbTONDHCycleTime(IN := TRUE);
|
|
_iStateDH := 10;
|
|
END_IF
|
|
|
|
10: // First power cycle
|
|
_fbTONDHCycleTime(IN := TRUE);
|
|
IF _fbTONDHCycleTime.Q THEN
|
|
_rAutoPowerRequest := 0;
|
|
GVL_SCADA.stAutomaticModeHMI.diSetpointAutomatic := 0;
|
|
_fbTONDHCycleTime(IN := FALSE);
|
|
_iStateDH := 20;
|
|
END_IF
|
|
|
|
20: // First pause
|
|
_fbTONDHCycleTime(IN := TRUE);
|
|
IF _fbTONDHCycleTime.Q THEN
|
|
_rAutoPowerRequest := _rPowerDH;
|
|
GVL_SCADA.stAutomaticModeHMI.diSetpointAutomatic := REAL_TO_DINT(_rAutoPowerRequest);
|
|
_fbTONDHCycleTime(IN := FALSE);
|
|
_iStateDH := 30;
|
|
END_IF
|
|
|
|
30: // Second power phase
|
|
_fbTONDHCycleTime(IN := TRUE);
|
|
IF _fbTONDHCycleTime.Q THEN
|
|
_rAutoPowerRequest := 0;
|
|
GVL_SCADA.stAutomaticModeHMI.diSetpointAutomatic := 0;
|
|
_fbTONDHCycleTime(IN := FALSE);
|
|
_iStateDH := 0;
|
|
END_IF
|
|
|
|
1000: // Error state
|
|
_rAutoPowerRequest := 0;
|
|
_rPowerDH := 0;
|
|
GVL_SCADA.stAutomaticModeHMI.diSetpointAutomatic := 0;
|
|
_fbTONDHCycleTime(IN := FALSE);
|
|
_iStateDH := 1010;
|
|
|
|
1010: // Wait for reset
|
|
IF _xConfirmAlarms THEN
|
|
_iStateDH := 0;
|
|
END_IF
|
|
|
|
END_CASE
|
|
IF (GVL_SCADA.eRequestedControlMode <> _eBMSControlMode) AND (GVL_SCADA.xCanChangeControlMode) THEN
|
|
_eBMSControlMode := GVL_SCADA.eRequestedControlMode;
|
|
END_IF
|
|
SM_AUTO();
|
|
GVL_SCADA.stAutomaticModeHMI.diSetpointAutomatic := REAL_TO_DINT(_rAutoPowerRequest);
|
|
END_CASE
|
|
|
|
GVL_SCADA.xCanChangeControlMode := _xCanChangeMode;
|
|
GVL_SCADA.eCurrentControlMode := _eBMSControlMode;
|
|
|
|
// Calculate current battery dc power
|
|
GVL_SCADA.diCurrentBatteryPower := REAL_TO_DINT(_afbStrings[0].stInverterData.rActACPower + _afbStrings[1].stInverterData.rActACPower);
|
|
|
|
// Read power values if commanded
|
|
_fbPowerMeterPower(
|
|
xGetEnergyCounters:= _xGetPowerMeterData,
|
|
sIpAddress:= '192.168.42.75',
|
|
lrEnergyFromGrid=> GVL_SCADA.lrChargedPowerValueWH,
|
|
lrEnergyIntoGrid=> GVL_SCADA.lrDischargedPowerValueWH,
|
|
xBusy=> ,
|
|
xError=> );
|
|
|
|
_fbPowerMeter24V(
|
|
xGetEnergyCounters:= _xGetPowerMeterData,
|
|
sIpAddress:= '192.168.42.80',
|
|
lrEnergyFromGrid=> GVL_SCADA.lrLastCycleUtilityPowerValueWh,
|
|
lrEnergyIntoGrid=> ,
|
|
xBusy=> ,
|
|
xError=> );
|
|
|
|
IF _xGetPowerMeterData THEN
|
|
_xGetPowerMeterData := FALSE;
|
|
END_IF
|
|
|
|
|
|
// Call safety fb
|
|
_fbSafety(refuStringErrorsModbus := GVL_MODBUS.stBMSErrorReg.wBMSErrorActive);
|
|
|
|
// Check if all modules are in auto mode
|
|
IF _xStringsInAutoMode AND _fbNoAutomaticModeAlarm.bRaised THEN
|
|
_fbNoAutomaticModeAlarm.Clear(0, TRUE);
|
|
END_IF
|
|
|
|
// Copy auto mode warning message to modbus register
|
|
GVL_MODBUS.stBMSErrorReg.wBMSWarningActive.stBitmap.bNotAllInAuto := (NOT _xStringsInAutoMode);
|
|
|
|
// Reset automatic buttons
|
|
IF GVL_SCADA.stAutomaticModeHMI.stStartAutoButton.xRequest THEN
|
|
GVL_SCADA.stAutomaticModeHMI.stStartAutoButton.xRequest := FALSE;
|
|
END_IF
|
|
IF GVL_SCADA.stAutomaticModeHMI.stStopAutoButton.xRequest THEN
|
|
GVL_SCADA.stAutomaticModeHMI.stStopAutoButton.xRequest := FALSE;
|
|
END_IF
|
|
|
|
// Reset alarm confirmation
|
|
IF _xConfirmAlarms OR _rtHardwareResetButton.Q THEN
|
|
GVL_MODBUS.stBMSErrorReg.wBMSErrorActive.wRegister := 0;
|
|
GVL_MODBUS.stBMSErrorReg.wBMSWarningActive.wRegister := 0;
|
|
GVL_MODBUS.stBMSErrorReg.wString1ErrorActive.wRegister := 0;
|
|
GVL_MODBUS.stBMSErrorReg.wString2ErrorActive.wRegister := 0;
|
|
_xConfirmAlarms := FALSE;
|
|
END_IF
|
|
|
|
_fbPowerMeter24V();
|
|
|
|
_fbTowerLight(
|
|
xAutoInStop := FALSE,
|
|
xWarningActive := FALSE,
|
|
xWarningConfirmPending := FALSE,
|
|
xErrorActive := _xErrorActive,
|
|
xErrorConfirmPending := FALSE);]]></ST>
|
|
</Implementation>
|
|
<Action Name="SM_AUTO" Id="{b5166e16-4fea-442b-9560-02c156f9a9ad}">
|
|
<Implementation>
|
|
<ST><![CDATA[CASE _iState OF
|
|
0: // Idle
|
|
// Check for all in auto mode
|
|
IF (NOT _xStringsInAutoMode) AND (NOT _fbNoAutomaticModeAlarm.bRaised) THEN
|
|
_fbNoAutomaticModeAlarm.Raise(0);
|
|
END_IF
|
|
|
|
// Wait for power command
|
|
IF ((ABS(_rAutoPowerRequest) > DINT_TO_REAL(GVL_CONFIG.diMinimumAbsPowerForEnable)) OR GVL_MODBUS.stModbusEMSComm.stModbusReg12.wBMSControlsRegister.stBitmap.bScheduleActive)
|
|
AND (NOT _xStringsErrorActive) AND _xStringsAllInAutomaticMode AND (NOT _xEMSHeartbeatNotOK) THEN
|
|
_iState := 5;
|
|
_xReleaseInverterPower := FALSE;
|
|
_xCanChangeMode := FALSE;
|
|
_xErrorShutdown := FALSE;
|
|
END_IF
|
|
|
|
5: // Check if power command is within limits
|
|
IF _rAutoPowerRequest <= DINT_TO_REAL(GVL_CONFIG.diMaxStringDischargePower)
|
|
AND _rAutoPowerRequest >= DINT_TO_REAL(GVL_CONFIG.diMaxStringChargingPower) THEN
|
|
_xEnableString := TRUE;
|
|
_xStartBalancing := FALSE;
|
|
_iState := 10;
|
|
ELSE
|
|
// Set error bitmap flag
|
|
GVL_MODBUS.stModbusEMSComm.stModbusReg11.lwErrorBitmap.0 := 1;
|
|
|
|
// Goto error state
|
|
_iState := 1000;
|
|
END_IF
|
|
|
|
10: // Wait for string to be ready
|
|
_fbStringReadyTimeout(IN := TRUE, PT := GVL_CONFIG.timStringReadyTimeout);
|
|
|
|
IF _xStringsReady AND (NOT _xStringsErrorActive) THEN
|
|
_xGetPowerMeterData := TRUE;
|
|
_fbStringReadyTimeout(IN := FALSE);
|
|
_rPowerInverter := 0.0;
|
|
// Set active parallel members (modbus)
|
|
GVL_MODBUS.stModbusEMSComm.stModbusReg10.uiActiveParallelMembers := GVL_CONFIG.uiNumberOfStrings;
|
|
GVL_MODBUS.stModbusEMSComm.stModbusReg11.eBatteryStatus := E_BATTERY_STATUS.ACTIVE;
|
|
_iState := 30;
|
|
END_IF
|
|
|
|
IF _xStringsErrorActive OR _fbStringReadyTimeout.Q THEN
|
|
_fbStringReadyTimeout(IN := FALSE);
|
|
_xEnableString := FALSE;
|
|
GVL_SCADA.stAutomaticModeHMI.diSetpointAutomatic := 0;
|
|
_xCanChangeMode := TRUE;
|
|
_iState := 45;
|
|
END_IF
|
|
|
|
|
|
IF (ABS(_rAutoPowerRequest) < DINT_TO_REAL(GVL_CONFIG.diMinimumAbsPowerForEnable)) OR _xEMSHeartbeatNotOK THEN
|
|
_fbStringReadyTimeout(IN := FALSE);
|
|
_xEnableString := FALSE;
|
|
_xCanChangeMode := TRUE;
|
|
_iState := 45;
|
|
END_IF
|
|
|
|
|
|
30: // String and inverter enabled
|
|
// Set inverter power to modbus requested power
|
|
_rPowerInverter := _rAutoPowerRequest;
|
|
_xReleaseInverterPower := TRUE;
|
|
|
|
// Check if the battery should still be active
|
|
IF (_rAutoPowerRequest = 0.0) AND NOT GVL_MODBUS.stModbusEMSComm.stModbusReg12.wBMSControlsRegister.stBitmap.bScheduleActive THEN
|
|
_xNoPowerRequested := TRUE;
|
|
ELSE
|
|
_xNoPowerRequested := FALSE;
|
|
END_IF
|
|
|
|
// Set battery status
|
|
IF _rAutoPowerRequest > 0 THEN
|
|
GVL_SCADA.eCycleStatus := E_CYCLE_STATUS.DISCHARGE_STARTED;
|
|
GVL_MODBUS.stModbusEMSComm.stModbusReg11.eChargeStatus := E_CHARGE_STATUS.DISCHARGING;
|
|
ELSIF _rAutoPowerRequest < 0 THEN
|
|
GVL_SCADA.eCycleStatus := E_CYCLE_STATUS.CHARGE_STARTED;
|
|
GVL_MODBUS.stModbusEMSComm.stModbusReg11.eChargeStatus := E_CHARGE_STATUS.CHARGING;
|
|
ELSE
|
|
GVL_SCADA.eCycleStatus := E_CYCLE_STATUS.OFF;
|
|
GVL_MODBUS.stModbusEMSComm.stModbusReg11.eChargeStatus := E_CHARGE_STATUS.UNDEFINED;
|
|
END_IF
|
|
|
|
// Add small delay before shutdown by EMS is detected
|
|
_tonBeginShutdown(IN := _xNoPowerRequested);
|
|
|
|
// shutdown triggered from EMS
|
|
IF _tonBeginShutdown.Q THEN
|
|
_tonBeginShutdown(In := FALSE);
|
|
|
|
IF _rPowerInverter < 0 THEN
|
|
GVL_SCADA.eCycleStatus := E_CYCLE_STATUS.CHARGE_ENDED;
|
|
ELSIF _rPowerInverter > 0 THEN
|
|
GVL_SCADA.eCycleStatus := E_CYCLE_STATUS.DISCHARGE_ENDED;
|
|
END_IF
|
|
|
|
// Set inverter to zero power
|
|
_rPowerInverter := 0.0;
|
|
|
|
// Start string shutdown
|
|
_xEnableString := FALSE;
|
|
GVL_MODBUS.stModbusEMSComm.stModbusReg11.eChargeStatus := E_CHARGE_STATUS.DISCHARGING;
|
|
GVL_MODBUS.stModbusEMSComm.stModbusReg10.uiActiveParallelMembers := 0;
|
|
_iState := 35;
|
|
END_IF
|
|
|
|
// Shutdown triggered by battery fully charged
|
|
IF GVL_MODBUS.stModbusEMSComm.stModbusReg11.eChargeStatus = E_CHARGE_STATUS.CHARGING AND ((_rMaxCurrentInverterDCVoltage >= GVL_CONFIG.rStringFullyChargedVoltage) OR _rHighestSegmentVoltage >= GVL_CONFIG.rMaximumUnitVoltage) THEN
|
|
_xGetPowerMeterData := TRUE;
|
|
IF (_eBMSControlMode = E_BMS_CONTROL_MODE.CYCLING) THEN
|
|
GVL_SCADA.eCycleStatus := E_CYCLE_STATUS.DISCHARGE_STARTED;
|
|
_rAutoPowerRequest := _rAutoPowerRequest * -1;
|
|
GVL_SCADA.stAutomaticModeHMI.diSetpointAutomatic := REAL_TO_DINT(_rAutoPowerRequest);
|
|
ELSE
|
|
_tonBeginShutdown(In := FALSE);
|
|
|
|
// Send message
|
|
_fbBatteryFullMessage.Send(0);
|
|
|
|
// Set inverter to zero power
|
|
_rPowerInverter := 0.0;
|
|
|
|
// Set local remote to zero power
|
|
GVL_SCADA.stAutomaticModeHMI.diSetpointAutomatic := 0;
|
|
|
|
// Start string shutdown
|
|
_xEnableString := FALSE;
|
|
|
|
// Change battery status
|
|
GVL_MODBUS.stModbusEMSComm.stModbusReg11.eChargeStatus := E_CHARGE_STATUS.FULL;
|
|
GVL_MODBUS.stModbusEMSComm.stModbusReg10.uiActiveParallelMembers := 0;
|
|
_iState := 35;
|
|
END_IF
|
|
|
|
END_IF
|
|
|
|
// Shutdown triggered by battery empty
|
|
IF GVL_MODBUS.stModbusEMSComm.stModbusReg11.eChargeStatus = E_CHARGE_STATUS.DISCHARGING AND ((_rMinCurrentInverterDCVoltage <= GVL_CONFIG.rStringEmptyVoltage) OR _rSmallestSegmentVoltage <= GVL_CONFIG.rMinimumUnitVoltage) THEN
|
|
_xGetPowerMeterData := TRUE;
|
|
IF (_eBMSControlMode = E_BMS_CONTROL_MODE.CYCLING) THEN
|
|
GVL_SCADA.eCycleStatus := E_CYCLE_STATUS.CHARGE_STARTED;
|
|
_rAutoPowerRequest := _rAutoPowerRequest * -1;
|
|
GVL_SCADA.stAutomaticModeHMI.diSetpointAutomatic := REAL_TO_DINT(_rAutoPowerRequest);
|
|
ELSE
|
|
_tonBeginShutdown(In := FALSE);
|
|
|
|
// Send Message
|
|
_fbBatteryEmptyMessage.Send(0);
|
|
|
|
// Set local remote to zero power
|
|
GVL_SCADA.stAutomaticModeHMI.diSetpointAutomatic := 0;
|
|
|
|
// Set inverter to zero power
|
|
_rPowerInverter := 0.0;
|
|
|
|
// Start string shutdown
|
|
_xEnableString := FALSE;
|
|
|
|
// Change battery status
|
|
GVL_MODBUS.stModbusEMSComm.stModbusReg11.eChargeStatus := E_CHARGE_STATUS.EMPTY;
|
|
GVL_MODBUS.stModbusEMSComm.stModbusReg10.uiActiveParallelMembers := 0;
|
|
_iState := 35;
|
|
END_IF
|
|
END_IF
|
|
|
|
// Check for errors
|
|
IF _xStringsErrorActive OR _xEMSHeartbeatNotOK THEN
|
|
_xReleaseInverterPower := FALSE;
|
|
_xEnableString := FALSE;
|
|
_xErrorShutdown := TRUE;
|
|
_tonBeginShutdown(In := FALSE);
|
|
GVL_SCADA.stAutomaticModeHMI.diSetpointAutomatic := 0;
|
|
_xCanChangeMode := TRUE;
|
|
_iState := 45;
|
|
END_IF
|
|
|
|
35: // Wait for string to be in shutdown discharge mode
|
|
IF _xStringsInSchutdownDischargeMode THEN
|
|
// Check if we are allowed to discharge during shutdown with inverter
|
|
IF GVL_CONFIG.xShutdownDischargeWithInverter THEN
|
|
_iState := 40;
|
|
ELSE
|
|
_xReleaseInverterPower := FALSE;
|
|
_rPowerInverter := 0.0;
|
|
_xEnableString := FALSE;
|
|
GVL_SCADA.stAutomaticModeHMI.diSetpointAutomatic := 0;
|
|
_xCanChangeMode := TRUE;
|
|
GVL_MODBUS.stModbusEMSComm.stModbusReg10.uiActiveParallelMembers := 0;
|
|
GVL_MODBUS.stModbusEMSComm.stModbusReg11.eBatteryStatus := E_BATTERY_STATUS.OFF;
|
|
_iState := 45;
|
|
END_IF
|
|
END_IF
|
|
|
|
// Check for errors
|
|
IF _xStringsErrorActive THEN
|
|
_xErrorShutdown := TRUE;
|
|
_iState := 1000;
|
|
END_IF
|
|
|
|
40: // Wait for inverter discharge done
|
|
IF _xStringsShutdownDischargeAllowed OR _xEMSHeartbeatNotOK THEN
|
|
_rPowerInverter := GVL_CONFIG.rAbsShutdownDischargePower;
|
|
ELSE
|
|
_xReleaseInverterPower := FALSE;
|
|
_xGetPowerMeterData := TRUE;
|
|
GVL_MODBUS.stModbusEMSComm.stModbusReg10.uiActiveParallelMembers := 0;
|
|
_rPowerInverter := 0.0;
|
|
GVL_SCADA.stAutomaticModeHMI.diSetpointAutomatic := 0;
|
|
_xEnableString := FALSE;
|
|
_xCanChangeMode := TRUE;
|
|
_iState := 45;
|
|
END_IF
|
|
|
|
// Check for errors
|
|
IF _xStringsErrorActive THEN
|
|
_xErrorShutdown := TRUE;
|
|
_iState := 1000;
|
|
END_IF
|
|
|
|
// Restart if possible
|
|
IF (ABS(_rAutoPowerRequest) > DINT_TO_REAL(GVL_CONFIG.diMinimumAbsPowerForEnable)) AND (NOT _xStringsErrorActive) AND _xStringsAllInAutomaticMode THEN
|
|
_iState := 5;
|
|
END_IF
|
|
|
|
45: // Wait for shutdown of string to be done
|
|
IF (NOT _xStringsInSchutdownDischargeMode) AND _xStringsOff THEN
|
|
GVL_MODBUS.stModbusEMSComm.stModbusReg11.eBatteryStatus := E_BATTERY_STATUS.OFF;
|
|
GVL_MODBUS.stModbusEMSComm.stModbusReg11.eChargeStatus := E_CHARGE_STATUS.UNDEFINED;
|
|
GVL_SCADA.eCycleStatus := E_CYCLE_STATUS.OFF;
|
|
_iState := 0;
|
|
END_IF
|
|
|
|
// Restart if possible
|
|
IF (ABS(_rAutoPowerRequest) > DINT_TO_REAL(GVL_CONFIG.diMinimumAbsPowerForEnable)) AND (NOT _xStringsErrorActive) AND _xStringsAllInAutomaticMode THEN
|
|
_xCanChangeMode := FALSE;
|
|
_iState := 5;
|
|
END_IF
|
|
|
|
// Check for errors
|
|
IF _xStringsErrorActive THEN
|
|
_xErrorShutdown := TRUE;
|
|
_iState := 1000;
|
|
END_IF
|
|
|
|
|
|
1000: // Error state
|
|
GVL_MODBUS.stModbusEMSComm.stModbusReg10.uiActiveParallelMembers := 0;
|
|
_xReleaseInverterPower := FALSE;
|
|
_xEnableString := FALSE;
|
|
_rPowerInverter := 0.0;
|
|
GVL_MODBUS.stModbusEMSComm.stModbusReg11.eBatteryStatus := E_BATTERY_STATUS.ERROR;
|
|
_iState := 1010;
|
|
|
|
1010: // Wait for reset from error state
|
|
IF (_rAutoPowerRequest = 0.0) AND (NOT _xStringsErrorActive) AND _xConfirmAlarms THEN
|
|
// Reset modbus error register
|
|
GVL_MODBUS.stModbusEMSComm.stModbusReg11.lwErrorBitmap := 0;
|
|
|
|
// Reset modbus error flag
|
|
GVL_MODBUS.stModbusEMSComm.stModbusReg11.eBatteryStatus := E_BATTERY_STATUS.OFF;
|
|
|
|
// Reset error shutdown flag
|
|
_xErrorShutdown := FALSE;
|
|
|
|
// Goto init state
|
|
_iState := 0;
|
|
|
|
_xCanChangeMode := TRUE;
|
|
END_IF
|
|
END_CASE
|
|
|
|
// Calculate string power balancing
|
|
IF _rStringsSumVoltage <> 0 AND (_uiNumberOfActiveStrings <> 0) THEN
|
|
FOR _ui := 0 TO (GVL_CONFIG.uiNumberOfStrings-1) DO
|
|
IF GVL_CONFIG.axStringEnabled[_ui] THEN
|
|
// Calculate delta u to middle voltage
|
|
_rDeltaUm := (_afbStrings[_ui].rCurrentVoltage * _uiNumberOfActiveStrings - _rStringsSumVoltage) / _rStringsSumVoltage;
|
|
|
|
// Discharging
|
|
IF _rPowerInverter > 0 THEN
|
|
_arPowerString[_ui] := (_rPowerInverter / _uiNumberOfActiveStrings) * ( 1 + (_rDeltaUm * GVL_CONFIG.rBalancingFactor));
|
|
// Charging
|
|
ELSIF _rPowerInverter < 0 THEN
|
|
_arPowerString[_ui] := (_rPowerInverter / _uiNumberOfActiveStrings) * ( 1 - (_rDeltaUm * GVL_CONFIG.rBalancingFactor));
|
|
// Nothing
|
|
ELSE
|
|
_arPowerString[_ui] := 0.0;
|
|
END_IF
|
|
END_IF
|
|
END_FOR
|
|
ELSE
|
|
FOR _ui := 0 TO (GVL_CONFIG.uiNumberOfStrings-1) DO
|
|
_arPowerString[_ui] := 0.0;
|
|
END_FOR
|
|
END_IF
|
|
]]></ST>
|
|
</Implementation>
|
|
</Action>
|
|
<Action Name="SM_BALANCING" Id="{f1f90032-de29-468d-899c-50bfb54e48e0}">
|
|
<Implementation>
|
|
<ST><![CDATA[CASE _iStateBalancing OF
|
|
0: // Test
|
|
IF GVL_SCADA.stAutomaticModeHMI.stStartAutoButton.xRequest THEN
|
|
_xReleaseInverterPower := FALSE;
|
|
_iStateBalancing := 5;
|
|
END_IF
|
|
|
|
// GVL_SCADA.stAutomaticModeHMI.stStartAutoButton.xRelease := (NOT _xStringsErrorActive) AND _afbStrings[_uiDebugCurrentString].xAllModulesInAutoMode;
|
|
|
|
5: // Check for start conditions
|
|
IF (NOT _xStringsErrorActive) AND _xStringsAllInAutomaticMode THEN
|
|
_xCanChangeMode := FALSE;
|
|
_xEnableString := FALSE;
|
|
_xStartBalancing := TRUE;
|
|
_iStateBalancing := 10;
|
|
END_IF
|
|
|
|
10: // Wait for balancing to be done
|
|
IF _xStringsBalancingDone THEN
|
|
_xCanChangeMode := TRUE;
|
|
_xStartBalancing := FALSE;
|
|
_iStateBalancing := 0;
|
|
END_IF
|
|
|
|
IF _xStringsErrorActive THEN
|
|
_iStateBalancing := 900;
|
|
_xStartBalancing := FALSE;
|
|
END_IF
|
|
|
|
IF GVL_SCADA.stAutomaticModeHMI.stStopAutoButton.xRequest THEN
|
|
_xStartBalancing := FALSE;
|
|
_xCanChangeMode := TRUE;
|
|
_iStateBalancing := 0;
|
|
END_IF
|
|
|
|
900: // Error state
|
|
IF _xConfirmAlarms AND (NOT _xStringsErrorActive) THEN
|
|
_xCanChangeMode := TRUE;
|
|
_iStateBalancing := 0;
|
|
END_IF
|
|
END_CASE]]></ST>
|
|
</Implementation>
|
|
</Action>
|
|
<Action Name="SM_CAPACITY_TEST" Id="{705978cf-2798-4a38-8f24-148e2ec1d46e}">
|
|
<Implementation>
|
|
<ST><![CDATA[]]></ST>
|
|
</Implementation>
|
|
</Action>
|
|
<Action Name="SM_MANUAL" Id="{ddef276e-9f4f-4258-b863-d254dd94b701}">
|
|
<Implementation>
|
|
<ST><![CDATA[_xCanChangeMode := TRUE;
|
|
_xReleaseInverterPower := FALSE;]]></ST>
|
|
</Implementation>
|
|
</Action>
|
|
<Action Name="SM_PRECHARGE" Id="{b84aedc8-0039-40a2-8abe-a166eca7bebc}">
|
|
<Implementation>
|
|
<ST><![CDATA[// Start on start button pressed
|
|
IF GVL_SCADA.stAutomaticModeHMI.stStartAutoButton.xRequest THEN
|
|
// Only start if everything is ok
|
|
IF _xStringsAllInAutomaticMode AND (NOT _xStringsErrorActive) THEN
|
|
_xStartPrecharge := TRUE;
|
|
END_IF
|
|
END_IF
|
|
|
|
// Sto pif stop button pressed
|
|
IF GVL_SCADA.stAutomaticModeHMI.stStopAutoButton.xRequest THEN
|
|
_xStartPrecharge := FALSE;
|
|
END_IF
|
|
|
|
|
|
// State machine
|
|
CASE _iStatePrecharge OF
|
|
0: // Idle
|
|
// Wait for start command
|
|
IF _xStartPrecharge AND _xStringsAllInAutomaticMode THEN
|
|
_xEnableString := TRUE;
|
|
_iStatePrecharge := 10;
|
|
_xReleaseInverterPower := FALSE;
|
|
_rPowerInverter := 0.0;
|
|
_xCanChangeMode := FALSE;
|
|
END_IF
|
|
|
|
10: // Wait for string to be ready
|
|
IF _xStringsReady AND (NOT _xStringsErrorActive) THEN
|
|
_iStatePrecharge := 30;
|
|
END_IF
|
|
|
|
// Shutdown
|
|
IF NOT _xStartPrecharge THEN
|
|
_xEnableString := FALSE;
|
|
_iStatePrecharge := 0;
|
|
_xCanChangeMode := TRUE;
|
|
END_IF
|
|
|
|
// Check for errors
|
|
IF _xStringsErrorActive THEN
|
|
_iStatePrecharge := 1000;
|
|
END_IF
|
|
|
|
30: // String enabled and dc circuit breaker closed
|
|
// Check if the battery should still be active
|
|
IF (NOT _xStartPrecharge) THEN
|
|
// Start string shutdown
|
|
_xEnableString := FALSE;
|
|
GVL_MODBUS.stModbusEMSComm.stModbusReg11.eChargeStatus := E_CHARGE_STATUS.UNDEFINED;
|
|
GVL_MODBUS.stModbusEMSComm.stModbusReg10.uiActiveParallelMembers := 0;
|
|
_iStatePrecharge := 45;
|
|
_xCanChangeMode := TRUE;
|
|
END_IF
|
|
|
|
// Check for errors
|
|
IF _xStringsErrorActive THEN
|
|
_xEnableString := FALSE;
|
|
_iStatePrecharge := 1000;
|
|
END_IF
|
|
|
|
45: // Wait for shutdown of string to be done
|
|
IF _xStringsOff THEN
|
|
_iStatePrecharge := 0;
|
|
END_IF
|
|
|
|
IF _xStartPrecharge THEN
|
|
_iStatePrecharge := 0;
|
|
END_IF
|
|
|
|
// Check for errors
|
|
IF _xStringsErrorActive THEN
|
|
_xEnableString := FALSE;
|
|
_iStatePrecharge := 1000;
|
|
END_IF
|
|
|
|
1000: // Error state
|
|
_xEnableString := FALSE;
|
|
_rPowerInverter := 0.0;
|
|
_xCanChangeMode := TRUE;
|
|
_xStartPrecharge := FALSE;
|
|
_iStatePrecharge := 1010;
|
|
|
|
1010: // Wait for reset from error state
|
|
IF (NOT _xStringsErrorActive) AND (NOT _xStartPrecharge) THEN
|
|
// Goto init state
|
|
_iStatePrecharge := 0;
|
|
|
|
_xCanChangeMode := TRUE;
|
|
END_IF
|
|
END_CASE]]></ST>
|
|
</Implementation>
|
|
</Action>
|
|
<Action Name="SM_SAFETY_CHECK" Id="{6d8e5993-cf32-4980-9ea3-c1fbfa4b8601}">
|
|
<Implementation>
|
|
<ST><![CDATA[// Start on start button pressed
|
|
IF GVL_SCADA.stAutomaticModeHMI.stStartAutoButton.xRequest THEN
|
|
// Only start if everything is ok
|
|
IF _xStringsAllInAutomaticMode AND (NOT _xStringsErrorActive) THEN
|
|
_xStartSafetyCheck := TRUE;
|
|
END_IF
|
|
END_IF
|
|
|
|
// Stop if stop button pressed
|
|
IF GVL_SCADA.stAutomaticModeHMI.stStopAutoButton.xRequest THEN
|
|
_xStartSafetyCheck := FALSE;
|
|
END_IF
|
|
|
|
|
|
// State machine
|
|
CASE _iStateSafetyCheck OF
|
|
0: // Idle
|
|
// Wait for start command
|
|
IF _xStartSafetyCheck AND _xStringsAllInAutomaticMode THEN
|
|
_xEnableString := TRUE;
|
|
_xReleaseInverterPower := FALSE;
|
|
_iStateSafetyCheck := 10;
|
|
_rPowerInverter := 0.0;
|
|
_xCanChangeMode := FALSE;
|
|
END_IF
|
|
|
|
10: // Wait for string to be ready
|
|
IF _xStringsReady AND (NOT _xStringsErrorActive) THEN
|
|
_iStateSafetyCheck := 30;
|
|
END_IF
|
|
|
|
// Shutdown
|
|
IF NOT _xStartSafetyCheck THEN
|
|
_xEnableString := FALSE;
|
|
_iStateSafetyCheck := 0;
|
|
_xCanChangeMode := TRUE;
|
|
END_IF
|
|
|
|
// Check for errors
|
|
IF _xStringsErrorActive THEN
|
|
_iStateSafetyCheck := 1000;
|
|
END_IF
|
|
|
|
30: // String enabled and dc circuit breaker closed
|
|
// Check if the battery should still be active
|
|
IF (NOT _xStartSafetyCheck) THEN
|
|
// Start string shutdown
|
|
_xEnableString := FALSE;
|
|
GVL_MODBUS.stModbusEMSComm.stModbusReg11.eChargeStatus := E_CHARGE_STATUS.UNDEFINED;
|
|
GVL_MODBUS.stModbusEMSComm.stModbusReg10.uiActiveParallelMembers := 0;
|
|
_iStateSafetyCheck := 45;
|
|
_xCanChangeMode := TRUE;
|
|
END_IF
|
|
|
|
// Check for errors
|
|
IF _xStringsErrorActive THEN
|
|
_xEnableString := FALSE;
|
|
_iStateSafetyCheck := 1000;
|
|
END_IF
|
|
|
|
45: // Wait for shutdown of string to be done
|
|
IF _xStringsOff THEN
|
|
_iStateSafetyCheck := 0;
|
|
END_IF
|
|
|
|
IF _xStartSafetyCheck THEN
|
|
_iStateSafetyCheck := 0;
|
|
END_IF
|
|
|
|
// Check for errors
|
|
IF _xStringsErrorActive THEN
|
|
_xEnableString := FALSE;
|
|
_iStateSafetyCheck := 1000;
|
|
END_IF
|
|
|
|
1000: // Error state
|
|
_xEnableString := FALSE;
|
|
_rPowerInverter := 0.0;
|
|
_xCanChangeMode := TRUE;
|
|
_xStartSafetyCheck := FALSE;
|
|
_iStateSafetyCheck := 1010;
|
|
|
|
1010: // Wait for reset from error state
|
|
IF (NOT _xStringsErrorActive) AND (NOT _xStartSafetyCheck) THEN
|
|
// Goto init state
|
|
_iStateSafetyCheck := 0;
|
|
|
|
_xCanChangeMode := TRUE;
|
|
END_IF
|
|
END_CASE]]></ST>
|
|
</Implementation>
|
|
</Action>
|
|
</POU>
|
|
</TcPlcObject> |