- Fixed bug that SafetyIntlkTimeout could not be acknowledged - Pumps now dont shut off during error in unit (except over- or underpressure), setpoint is now set to discharge power - Fixed bug not able to restart safety-check when not all strings where off - Adjusted Safetyparameters to match safetycheck protocol
1145 lines
36 KiB
XML
1145 lines
36 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;
|
|
_xReleaseErrors : BOOL := TRUE;
|
|
_xReleaseLimitsErrors : BOOL := TRUE;
|
|
_xConfirmAlarms : BOOL;
|
|
_xEnableString : BOOL;
|
|
_xStartBalancing : BOOL;
|
|
|
|
_xCanChangeMode : BOOL := TRUE;
|
|
|
|
{attribute 'OPC.UA.DA' := '0'}
|
|
_afbStrings : ARRAY[0..1] OF FB_String[('String 1'), ('String 2')];
|
|
|
|
// Variable to detect charge status change
|
|
//_eLastChargeStatus : E_CHARGE_STATUS;
|
|
// Variable to detect battery status change
|
|
//_eLastBatteryStatus : E_BATTERY_STATUS;
|
|
|
|
// Battery shutdown due to error
|
|
_xErrorShutdown : BOOL := FALSE;
|
|
|
|
// State machine state
|
|
_iState : INT;
|
|
_iStateSafetyCheck : INT;
|
|
_iStateBalancing : INT;
|
|
|
|
// Start safety check mode
|
|
_xStartSafetyCheck : 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#30S);
|
|
|
|
// 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;
|
|
|
|
|
|
|
|
// First cycle tag
|
|
_xFirstCycle : BOOL := TRUE;
|
|
|
|
// ADS reader for modbus server data
|
|
_fbADSReader : ADSREAD;
|
|
|
|
// Timer for ADS read
|
|
_timADSReadTimer : TON;
|
|
|
|
// 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;
|
|
|
|
// 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;
|
|
|
|
// DEBUG
|
|
_ModbusDebugTest : ST_MODBUS_REG_11;
|
|
|
|
_fbStringReadyTimeout : TON;
|
|
|
|
// Sum of voltage of all active strings
|
|
_rStringsSumVoltage : REAL;
|
|
_rDeltaUm : REAL;
|
|
_arPowerString : ARRAY[0..(GVL_CONFIG.uiNumberOfStrings-1)] OF REAL;
|
|
|
|
_ui : UINT := 0;
|
|
_xStringsReady : BOOL;
|
|
_xStringsErrorActive : BOOL;
|
|
_xStringsInSchutdownDischargeMode : BOOL;
|
|
_xStringsShutdownDischargeAllowed : BOOL;
|
|
_xStringsAllInAutomaticMode : BOOL;
|
|
_xStringsOff : BOOL;
|
|
_xStringsBalancingDone : BOOL;
|
|
_xStringsInAutoMode : BOOL;
|
|
|
|
_rMaxCurrentInverterDCVoltage : REAL;
|
|
_rMinCurrentInverterDCVoltage : REAL;
|
|
|
|
_fbModbusRead : FB_MBReadRegs;
|
|
//_wLength : WORD := 49;
|
|
xDebugTest : BOOL;
|
|
_wDebug1 : WORD;
|
|
_wDebug2 : WORD;
|
|
|
|
_fbPowerMeterPower : FB_PowerMeter;
|
|
_fbPowerMeter24V : FB_PowerMeter;
|
|
|
|
// Number of activated strings (from configuration)
|
|
_uiNumberOfActiveStrings : UINT;
|
|
|
|
_xGetPowerMeterData : BOOL;
|
|
|
|
//_fbKaco : FB_PowerSupplyKaco('Kaco');
|
|
//_xEnableKaco : BOOL;
|
|
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';
|
|
END_IF
|
|
|
|
// Reset error flag
|
|
_xErrorActive := 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.stBMSErrorReg.wConfirmAlarms > 0 THEN
|
|
GVL_MODBUS.stBMSErrorReg.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;
|
|
|
|
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);
|
|
|
|
// ===============================
|
|
// 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);
|
|
|
|
// Call string 1
|
|
_afbStrings[0](
|
|
xEnable := _xEnableString AND GVL_CONFIG.axStringEnabled[0],
|
|
uiStringNumber := 0,
|
|
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);
|
|
|
|
IF _afbStrings[0].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.wBMSErrorActive.stBitmap.bDCSwitchS1 := _afbStrings[0].xRepairSwitchOk;
|
|
GVL_MODBUS.stBMSErrorReg.wBMSWarningActive.stBitmap.bSafetyIntlkString1 := (NOT _afbStrings[0].xSafetyIntlksOk);
|
|
|
|
|
|
// Call string 2
|
|
_afbStrings[1](
|
|
xEnable := _xEnableString AND GVL_CONFIG.axStringEnabled[1],
|
|
uiStringNumber := 1,
|
|
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);
|
|
|
|
IF _afbStrings[1].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.wBMSErrorActive.stBitmap.bDCSwitchS2 := _afbStrings[1].xRepairSwitchOk;
|
|
GVL_MODBUS.stBMSErrorReg.wBMSWarningActive.stBitmap.bSafetyIntlkString2 := (NOT _afbStrings[1].xSafetyIntlksOk);
|
|
|
|
// 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;
|
|
|
|
FOR _ui := 0 TO (GVL_CONFIG.uiNumberOfStrings-1) DO
|
|
// 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=> );
|
|
|
|
_fbModbusRead(
|
|
sIPAddr:= '127.0.0.1',
|
|
nTCPPort:= 502,
|
|
nUnitID:= 16#FF,
|
|
nQuantity:= 48,
|
|
nMBAddr:= 11000,
|
|
cbLength:= SIZEOF(_ModbusDebugTest),
|
|
pDestAddr:= ADR(_ModbusDebugTest),
|
|
bExecute:= xDebugTest,
|
|
tTimeout:= T#5S,
|
|
bBusy=> ,
|
|
bError=> ,
|
|
nErrId=> ,
|
|
cbRead=> );
|
|
|
|
|
|
// ===============================
|
|
// Copy data to modbus registers
|
|
// ===============================
|
|
// 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:
|
|
_xAllComponentsToManualMode := FALSE;
|
|
_xInSafetyCheckMode := FALSE;
|
|
_xReleaseManualMode := FALSE;
|
|
_rAutoPowerRequest := DINT_TO_REAL(GVL_MODBUS.stModbusEMSComm.stModbusReg12.diSetpointActivePower);
|
|
IF (GVL_SCADA.eRequestedControlMode <> _eBMSControlMode) AND (GVL_SCADA.xCanChangeControlMode) THEN
|
|
_eBMSControlMode := GVL_SCADA.eRequestedControlMode;
|
|
END_IF
|
|
SM_AUTO();
|
|
|
|
E_BMS_CONTROL_MODE.AUTO_LOCAL:
|
|
_xAllComponentsToManualMode := FALSE;
|
|
_xInSafetyCheckMode := FALSE;
|
|
_xReleaseManualMode := 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:
|
|
_xAllComponentsToManualMode := TRUE;
|
|
_xInSafetyCheckMode := FALSE;
|
|
_xReleaseManualMode := TRUE;
|
|
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:
|
|
_xAllComponentsToManualMode := FALSE;
|
|
_xInSafetyCheckMode := TRUE;
|
|
_xReleaseManualMode := 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:
|
|
_xAllComponentsToManualMode := FALSE;
|
|
_xInSafetyCheckMode := FALSE;
|
|
_xReleaseManualMode := 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:
|
|
_xAllComponentsToManualMode := FALSE;
|
|
_xInSafetyCheckMode := FALSE;
|
|
_xReleaseManualMode := 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:
|
|
_xAllComponentsToManualMode := FALSE;
|
|
_xInSafetyCheckMode := FALSE;
|
|
_xReleaseManualMode := 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);
|
|
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();
|
|
|
|
// 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;
|
|
_xConfirmAlarms := FALSE;
|
|
END_IF
|
|
|
|
_fbPowerMeter24V();
|
|
|
|
// _fbKaco(
|
|
// sInverterIPAddr:= '192.168.42.14',
|
|
// xEnable:= _xEnableKaco,
|
|
// rPower:= 0.0,
|
|
// xReset:= _xConfirmAlarms,
|
|
// rMaxBattPower:= 10_000,
|
|
// uiMinDisVoltage := ,
|
|
// uiMaxChaVoltage := ,
|
|
// uiMaxDisCurrent := ,
|
|
// uiMaxChaCurrent := ,
|
|
// xActive=> ,
|
|
// xError=> ,
|
|
// xHeartbeatOk=> ,
|
|
// stCurrentValues=> ,
|
|
// );]]></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)) AND (NOT _xStringsErrorActive) AND _xStringsAllInAutomaticMode THEN
|
|
_iState := 5;
|
|
_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)) 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;//DINT_TO_REAL(GVL_MODBUS.stModbusEMSComm.stModbusReg12.diSetpointActivePower);
|
|
|
|
// Check if the battery should still be active
|
|
IF (_rAutoPowerRequest = 0.0) 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 THEN
|
|
_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
|
|
_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 THEN
|
|
_rPowerInverter := GVL_CONFIG.rAbsShutdownDischargePower;
|
|
ELSE
|
|
_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;
|
|
_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
|
|
_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;]]></ST>
|
|
</Implementation>
|
|
</Action>
|
|
<Action Name="SM_PRECHARGE" Id="{b84aedc8-0039-40a2-8abe-a166eca7bebc}">
|
|
<Implementation>
|
|
<ST><![CDATA[]]></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
|
|
|
|
// Sto pif 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;
|
|
_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> |