- Iso error in string will now only be active if the dc circuit breaker is not closed - Added some delay between closing dc circuit breaker and enabling inverter so that the dc bus voltage can stabilize itself
1124 lines
29 KiB
XML
1124 lines
29 KiB
XML
<?xml version="1.0" encoding="utf-8"?>
|
|
<TcPlcObject Version="1.1.0.1" ProductVersion="3.1.4026.12">
|
|
<POU Name="FB_PowerSupplyKaco" Id="{43c28077-20d6-4076-bde1-bc92c785654f}" SpecialFunc="None">
|
|
<Declaration><![CDATA[FUNCTION_BLOCK FB_PowerSupplyKaco
|
|
VAR_INPUT
|
|
sInverterIPAddr : STRING;
|
|
xEnable : BOOL;
|
|
rPower : REAL;
|
|
xReset : BOOL;
|
|
rMaxBattPower : REAL := 40_000; // 24kW
|
|
// Discharge min DC voltage (V)
|
|
uiMinDisVoltage : UINT := 6000;
|
|
|
|
// Charge max voltage (V)
|
|
uiMaxChaVoltage : UINT := 9600;
|
|
|
|
// Discharge max current (A)
|
|
uiMaxDisCurrent : UINT := 300;
|
|
|
|
// Charge max current (A)
|
|
uiMaxChaCurrent : UINT := 300;
|
|
END_VAR
|
|
VAR_OUTPUT
|
|
// Inverter active
|
|
xActive : BOOL;
|
|
|
|
// FB error
|
|
xError : BOOL;
|
|
|
|
// Heartbeat ok signal
|
|
xHeartbeatOk : BOOL := TRUE;
|
|
|
|
// Current inverter values
|
|
stCurrentValues : ST_SUNSPEC_CURRENT_VALUES;
|
|
END_VAR
|
|
VAR
|
|
// Current state
|
|
_iState : INT := 0;
|
|
|
|
// State for startup state machine
|
|
_iStateStartup : INT := 0;
|
|
|
|
// Startup busy flag
|
|
_xStartupBusy : BOOL;
|
|
|
|
// Internal power command
|
|
_rPowerInternal : REAL := 0;
|
|
|
|
// Value for commanding the target state of the inverter
|
|
_uiPCSSetOperation : UINT;
|
|
|
|
// FB for reading Modbus holding registers
|
|
_fbReadRegister : FB_MBReadRegs;
|
|
|
|
// FB for writing Modbus holding registers
|
|
_fbWriteRegister : FB_MBWriteRegs;
|
|
|
|
// Time for polling for current dc values and check for inverter error
|
|
_timPollingDelay : TIME := T#500MS;
|
|
|
|
// Time for setting the current power
|
|
_timSetPowerDelay : TIME := T#250MS;
|
|
|
|
// Timer for polling of current values
|
|
_tonPollingTimer : TON;
|
|
|
|
// Timer for setting the inverter power
|
|
_tonSetPowerTimer : TON;
|
|
|
|
// Inverter alarm
|
|
_fbErrorInverterAlarm : FB_TcAlarm;
|
|
|
|
// Flag if battery limits have been set
|
|
_xBatteryLimitsSet : BOOL := FALSE;
|
|
|
|
// Flag to start setting the battery limits
|
|
_xSetBatteryLimits : BOOL := FALSE;
|
|
|
|
// Flag to see if an error occured during setting the battery limits
|
|
_xErrorSetBatteryLimits : BOOL := FALSE;
|
|
|
|
// Battery limit scaling factors
|
|
_arBattScalingFactors : ARRAY[0..1] OF INT;
|
|
|
|
// Helper variable for writing a 1 to a register
|
|
_uiEnableLimit : UINT := 1;
|
|
|
|
// Retry timer to set battery limits
|
|
_fbTONSetBatteryLimits : TON := (PT := T#1S);
|
|
|
|
// Inverter power output
|
|
_iWSetPct : INT := 0;
|
|
|
|
// Converter max power scaling factor
|
|
_iWMaxSF : INT;
|
|
|
|
// Scaled converter max power
|
|
_rWMax : REAL;
|
|
|
|
// Unscaled converter max power
|
|
_uiWMax : UINT;
|
|
|
|
// Current DC values (DCA, DCA_SF, DCV, DCV_SF, DCW, DCW_SF) in word array for efficient modbus reading
|
|
_awCurrentDCValues : ARRAY[0..5] OF WORD;
|
|
|
|
// Current AC values (W, W_SF, Hz, Hz_SF, VA, VA_SF, VAr, VAr_SF, PF, PF_SF) in word array for efficient modbus reading
|
|
_awCurrentACValues : ARRAY[0..21] OF WORD;
|
|
|
|
// Current state of the inverters internal statemachine
|
|
_uiInverterState : UINT;
|
|
|
|
// Inverter name for alarm message
|
|
_sName : STRING;
|
|
END_VAR
|
|
VAR CONSTANT
|
|
// Battery limits registers (Model 64202)
|
|
BATTERY_LIMIT_SF : WORD := 41120;
|
|
DIS_MIN_V : WORD := 41122;
|
|
CHA_MAX_V : WORD := 41125;
|
|
CHA_MAX_A : WORD := 41126;
|
|
DIS_MAX_A : WORD := 41123;
|
|
EN_LIMIT : WORD := 41129;
|
|
|
|
// Power registers (Model 64201)
|
|
W_SET_PCT : WORD := 41069;
|
|
|
|
// Basic settings registers (Model 121)
|
|
W_MAX : WORD := 40214;
|
|
W_MAX_SF : WORD := 40234;
|
|
|
|
// Start of register with the current dc values
|
|
// Size 4
|
|
DC_VALUES_START_REGISTER : WORD := 40097;
|
|
|
|
// Start of register with the current ac values
|
|
// SIZE 10
|
|
AC_VALUES_START_REGISTER : WORD := 40072;
|
|
|
|
// Inverter statemachine status register
|
|
// Size 1, enum16 (Range = 0 .. 65534, Not implemented = 0xFFFF)
|
|
STATUS_REGISTER : WORD := 40108;
|
|
|
|
// Control register to set the target state of the inverters state machine
|
|
// Size 1, enum16 (Range = 0 .. 65534, Not implemented = 0xFFFF)
|
|
PCS_SET_OPERATION_REGISTER : WORD := 41064;
|
|
END_VAR
|
|
|
|
]]></Declaration>
|
|
<Implementation>
|
|
<ST><![CDATA[_rPowerInternal := rPower;
|
|
|
|
// Clamp rPower to maximum allowed power
|
|
IF (rPower > rMaxBattPower) THEN
|
|
_rPowerInternal := rMaxBattPower;
|
|
END_IF
|
|
|
|
IF (rPower < -rMaxBattPower) THEN
|
|
_rPowerInternal := -rMaxBattPower;
|
|
END_IF
|
|
|
|
CASE _iState OF
|
|
0: // Pre-init phase (no battery limits set)
|
|
_fbTONSetBatteryLimits(IN := TRUE);
|
|
IF _fbTONSetBatteryLimits.Q THEN
|
|
_fbTONSetBatteryLimits(IN := FALSE);
|
|
_iState := 10;
|
|
END_IF
|
|
|
|
10: // Read scaling factors
|
|
_fbReadRegister(
|
|
sIPAddr:= sInverterIPAddr,
|
|
nTCPPort:= 502,
|
|
nUnitID:= 16#01, // 16#FF for Modbus TCP
|
|
nQuantity:= 2,
|
|
nMBAddr:= BATTERY_LIMIT_SF,
|
|
cbLength:= SIZEOF(_arBattScalingFactors),
|
|
pDestAddr:= ADR(_arBattScalingFactors),
|
|
bExecute:= TRUE,
|
|
tTimeout:= T#30S,
|
|
bBusy=> ,
|
|
bError=> ,
|
|
nErrId=> ,
|
|
cbRead=> );
|
|
|
|
// Check if reading mudbus register is done
|
|
IF NOT _fbReadRegister.bBusy THEN
|
|
IF (NOT _fbReadRegister.bError) THEN
|
|
_iState := 20;
|
|
ELSE
|
|
// Goto error state
|
|
_iState := 0;
|
|
END_IF
|
|
_fbReadRegister(bExecute := FALSE);
|
|
END_IF
|
|
|
|
20: // Set min voltage
|
|
_fbWriteRegister(
|
|
sIPAddr:= sInverterIPAddr,
|
|
nTCPPort:= 502,
|
|
nUnitID:= 16#01, // 16#FF for Modbus TCP
|
|
nQuantity:= 1,
|
|
nMBAddr:= DIS_MIN_V,
|
|
cbLength:= SIZEOF(uiMinDisVoltage),
|
|
pSrcAddr:= ADR(uiMinDisVoltage),
|
|
bExecute:= TRUE,
|
|
tTimeout:= T#5S,
|
|
bBusy=> ,
|
|
bError=> ,
|
|
nErrId=> );
|
|
|
|
// If writing modbus register is done
|
|
IF NOT _fbWriteRegister.bBusy THEN
|
|
// And there is no error, then continue
|
|
IF (NOT _fbWriteRegister.bError) THEN
|
|
_iState := 30;
|
|
ELSE
|
|
// Goto error state
|
|
_iState := 1000;
|
|
END_IF
|
|
_fbWriteRegister(bExecute := FALSE);
|
|
END_IF
|
|
|
|
30: // Set max voltage
|
|
_fbWriteRegister(
|
|
sIPAddr:= sInverterIPAddr,
|
|
nTCPPort:= 502,
|
|
nUnitID:= 16#01, // 16#FF for Modbus TCP
|
|
nQuantity:= 1,
|
|
nMBAddr:= CHA_MAX_V,
|
|
cbLength:= SIZEOF(uiMaxChaVoltage),
|
|
pSrcAddr:= ADR(uiMaxChaVoltage),
|
|
bExecute:= TRUE,
|
|
tTimeout:= T#5S,
|
|
bBusy=> ,
|
|
bError=> ,
|
|
nErrId=> );
|
|
|
|
// If writing modbus register is done
|
|
IF NOT _fbWriteRegister.bBusy THEN
|
|
// And there is no error, then continue
|
|
IF (NOT _fbWriteRegister.bError) THEN
|
|
_iState := 40;
|
|
ELSE
|
|
// Goto error state
|
|
_iState := 1000;
|
|
END_IF
|
|
_fbWriteRegister(bExecute := FALSE);
|
|
END_IF
|
|
|
|
40: // Set charge current
|
|
_fbWriteRegister(
|
|
sIPAddr:= sInverterIPAddr,
|
|
nTCPPort:= 502,
|
|
nUnitID:= 16#01, // 16#FF for Modbus TCP
|
|
nQuantity:= 1,
|
|
nMBAddr:= CHA_MAX_A,
|
|
cbLength:= SIZEOF(uiMaxChaCurrent),
|
|
pSrcAddr:= ADR(uiMaxChaCurrent),
|
|
bExecute:= TRUE,
|
|
tTimeout:= T#5S,
|
|
bBusy=> ,
|
|
bError=> ,
|
|
nErrId=> );
|
|
|
|
// If writing modbus register is done
|
|
IF NOT _fbWriteRegister.bBusy THEN
|
|
// And there is no error, then continue
|
|
IF (NOT _fbWriteRegister.bError) THEN
|
|
_iState := 50;
|
|
ELSE
|
|
// Goto error state
|
|
_xErrorSetBatteryLimits := TRUE;
|
|
_iState := 1000;
|
|
END_IF
|
|
_fbWriteRegister(bExecute := FALSE);
|
|
END_IF
|
|
|
|
50: // Set discharge current
|
|
_fbWriteRegister(
|
|
sIPAddr:= sInverterIPAddr,
|
|
nTCPPort:= 502,
|
|
nUnitID:= 16#01, // 16#FF for Modbus TCP
|
|
nQuantity:= 1,
|
|
nMBAddr:= DIS_MAX_A,
|
|
cbLength:= SIZEOF(uiMaxDisCurrent),
|
|
pSrcAddr:= ADR(uiMaxDisCurrent),
|
|
bExecute:= TRUE,
|
|
tTimeout:= T#5S,
|
|
bBusy=> ,
|
|
bError=> ,
|
|
nErrId=> );
|
|
|
|
// If writing modbus register is done
|
|
IF NOT _fbWriteRegister.bBusy THEN
|
|
// And there is no error, then continue
|
|
IF (NOT _fbWriteRegister.bError) THEN
|
|
_iState := 60;
|
|
ELSE
|
|
// Goto error state
|
|
_xErrorSetBatteryLimits := TRUE;
|
|
_iState := 1000;
|
|
END_IF
|
|
_fbWriteRegister(bExecute := FALSE);
|
|
END_IF
|
|
|
|
60: // Enable battery limits
|
|
_fbWriteRegister(
|
|
sIPAddr:= sInverterIPAddr,
|
|
nTCPPort:= 502,
|
|
nUnitID:= 16#01, // 16#FF for Modbus TCP
|
|
nQuantity:= 1,
|
|
nMBAddr:= EN_LIMIT,
|
|
cbLength:= SIZEOF(_uiEnableLimit),
|
|
pSrcAddr:= ADR(_uiEnableLimit),
|
|
bExecute:= TRUE,
|
|
tTimeout:= T#5S,
|
|
bBusy=> ,
|
|
bError=> ,
|
|
nErrId=> );
|
|
|
|
// If writing modbus register is done
|
|
IF NOT _fbWriteRegister.bBusy THEN
|
|
// And there is no error, then continue
|
|
IF (NOT _fbWriteRegister.bError) THEN
|
|
// Battery limits set
|
|
_xBatteryLimitsSet := TRUE;
|
|
_xStartupBusy := FALSE;
|
|
_iState := 70;
|
|
ELSE
|
|
// Goto error state
|
|
//_xErrorSetBatteryLimits := TRUE;
|
|
_iState := 1000;
|
|
END_IF
|
|
_fbWriteRegister(bExecute := FALSE);
|
|
END_IF
|
|
|
|
70: // Try to set battery limits
|
|
_rPowerInternal := 0;
|
|
_iWSetPct := 0;
|
|
_iState := 80;
|
|
|
|
80: // Battery limits are set, write zero power
|
|
_fbWriteRegister(
|
|
sIPAddr:= sInverterIPAddr,
|
|
nTCPPort:= 502,
|
|
nUnitID:= 16#01, // 16#FF for Modbus TCP
|
|
nQuantity:= 1,
|
|
nMBAddr:= W_SET_PCT,
|
|
cbLength:= SIZEOF(_iWSetPct),
|
|
pSrcAddr:= ADR(_iWSetPct),
|
|
bExecute:= TRUE,
|
|
tTimeout:= T#5S,
|
|
bBusy=> ,
|
|
bError=> ,
|
|
nErrId=> );
|
|
|
|
IF NOT _fbWriteRegister.bBusy THEN
|
|
IF (NOT _fbWriteRegister.bError) THEN
|
|
_iState := 90;
|
|
ELSE
|
|
// Goto error state
|
|
_iState := 1000;
|
|
END_IF
|
|
_fbWriteRegister(bExecute := FALSE);
|
|
END_IF
|
|
|
|
90: // Read max power scaling
|
|
_fbReadRegister(
|
|
sIPAddr:= sInverterIPAddr,
|
|
nTCPPort:= 502,
|
|
nUnitID:= 16#01, // 16#FF for Modbus TCP
|
|
nQuantity:= 1,
|
|
nMBAddr:= W_MAX_SF,
|
|
cbLength:= SIZEOF(_iWMaxSF),
|
|
pDestAddr:= ADR(_iWMaxSF),
|
|
bExecute:= TRUE,
|
|
tTimeout:= T#5S,
|
|
bBusy=> ,
|
|
bError=> ,
|
|
nErrId=> ,
|
|
cbRead=> );
|
|
|
|
// Check if reading mudbus register is done
|
|
IF NOT _fbReadRegister.bBusy THEN
|
|
// If there was no error then continue
|
|
IF NOT _fbReadRegister.bError THEN
|
|
_iState := 100;
|
|
// Check for valid value
|
|
IF (_iWMaxSF < -10) OR (_iWMaxSF > 10) OR (_iWMaxSF = 16#8000) THEN
|
|
ADSLOGSTR(msgCtrlMask := ADSLOG_MSGTYPE_HINT, msgFmtStr := 'FBInverter into error state from: %s', strArg := TO_STRING(_iState));
|
|
// Goto error state
|
|
_iState := 1000;
|
|
END_IF
|
|
ELSE
|
|
xError := TRUE;
|
|
// Goto error state
|
|
_iState := 1000;
|
|
END_IF
|
|
_fbReadRegister(bExecute := FALSE);
|
|
END_IF
|
|
|
|
|
|
100: // Read max power
|
|
_fbReadRegister(
|
|
sIPAddr:= sInverterIPAddr,
|
|
nTCPPort:= 502,
|
|
nUnitID:= 16#01, // 16#FF for Modbus TCP
|
|
nQuantity:= 1,
|
|
nMBAddr:= W_MAX,
|
|
cbLength:= SIZEOF(_uiWMax),
|
|
pDestAddr:= ADR(_uiWMax),
|
|
bExecute:= TRUE,
|
|
tTimeout:= T#5S,
|
|
bBusy=> ,
|
|
bError=> ,
|
|
nErrId=> ,
|
|
cbRead=> );
|
|
|
|
// Check if reading mudbus register is done
|
|
IF NOT _fbReadRegister.bBusy THEN
|
|
// If there was no error then continue
|
|
IF NOT _fbReadRegister.bError THEN
|
|
_iState := 110;
|
|
// Calculate WMax
|
|
// Reading a register with scaling factor = value * 10^SF
|
|
_rWMax := LREAL_TO_REAL(_uiWMax * EXPT(10,_iWMaxSF));
|
|
ELSE
|
|
xError := TRUE;
|
|
// Goto error state
|
|
_iState := 1000;
|
|
END_IF
|
|
_fbReadRegister(bExecute := FALSE);
|
|
END_IF
|
|
|
|
|
|
110: // Idle state, wait for enable
|
|
IF _tonPollingTimer.Q THEN
|
|
_tonPollingTimer(IN := FALSE, PT := _timPollingDelay);
|
|
_iState := 120;
|
|
END_IF
|
|
|
|
// If enable and INTLK Ok
|
|
IF xEnable THEN
|
|
// _iState := 130;
|
|
_iState := 130;
|
|
_rPowerInternal := 0.0;
|
|
_iWSetPct := 0;
|
|
_uiPCSSetOperation := 11;
|
|
_tonPollingTimer(IN := FALSE, PT := _timPollingDelay);
|
|
ELSE
|
|
_tonPollingTimer(IN := TRUE, PT := _timPollingDelay);
|
|
_rPowerInternal := 0.0;
|
|
_iWSetPct := 0;
|
|
END_IF
|
|
|
|
|
|
120: // Write current power demand
|
|
_fbWriteRegister(
|
|
sIPAddr:= sInverterIPAddr,
|
|
nTCPPort:= 502,
|
|
nUnitID:= 16#01, // 16#FF for Modbus TCP
|
|
nQuantity:= 1,
|
|
nMBAddr:= W_SET_PCT,
|
|
cbLength:= SIZEOF(_iWSetPct),
|
|
pSrcAddr:= ADR(_iWSetPct),
|
|
bExecute:= TRUE,
|
|
tTimeout:= T#5S,
|
|
bBusy=> ,
|
|
bError=> ,
|
|
nErrId=> );
|
|
|
|
IF NOT _fbWriteRegister.bBusy THEN
|
|
IF (NOT _fbWriteRegister.bError) THEN
|
|
_iState := 121;
|
|
ELSE
|
|
// Goto error state
|
|
_iState := 1000;
|
|
END_IF
|
|
_fbWriteRegister(bExecute := FALSE);
|
|
END_IF
|
|
|
|
|
|
121: // Read current dc values
|
|
_fbReadRegister(
|
|
sIPAddr:= sInverterIPAddr,
|
|
nTCPPort:= 502,
|
|
nUnitID:= 16#01, // 16#FF for Modbus TCP
|
|
nQuantity:= 6,
|
|
nMBAddr:= DC_VALUES_START_REGISTER,
|
|
cbLength:= SIZEOF(_awCurrentDCValues),
|
|
pDestAddr:= ADR(_awCurrentDCValues),
|
|
bExecute:= TRUE,
|
|
tTimeout:= T#5S,
|
|
bBusy=> ,
|
|
bError=> ,
|
|
nErrId=> ,
|
|
cbRead=> );
|
|
|
|
// Check if reading mudbus register is done
|
|
IF NOT _fbReadRegister.bBusy THEN
|
|
// If there was no error and the converter has no error continue
|
|
IF NOT _fbReadRegister.bError THEN
|
|
_iState := 122;
|
|
stCurrentValues.rActDCCurrent := LREAL_TO_REAL(WORD_TO_INT(_awCurrentDCValues[0]) * EXPT(10,WORD_TO_INT(_awCurrentDCValues[1])));
|
|
stCurrentValues.rActDCVoltage := LREAL_TO_REAL(WORD_TO_UINT(_awCurrentDCValues[2]) * EXPT(10,WORD_TO_INT(_awCurrentDCValues[3])));
|
|
stCurrentValues.rActDCPower := LREAL_TO_REAL(WORD_TO_INT(_awCurrentDCValues[4]) * EXPT(10,WORD_TO_INT(_awCurrentDCValues[5])));
|
|
ELSE
|
|
xError := TRUE;
|
|
// Goto error state
|
|
_iState := 1000;
|
|
END_IF
|
|
_fbReadRegister(bExecute := FALSE);
|
|
END_IF
|
|
|
|
|
|
122: // Read current ac values
|
|
_fbReadRegister(
|
|
sIPAddr:= sInverterIPAddr,
|
|
nTCPPort:= 502,
|
|
nUnitID:= 16#01, // 16#FF for Modbus TCP
|
|
nQuantity:= 22,
|
|
nMBAddr:= AC_VALUES_START_REGISTER,
|
|
cbLength:= SIZEOF(_awCurrentACValues),
|
|
pDestAddr:= ADR(_awCurrentACValues),
|
|
bExecute:= TRUE,
|
|
tTimeout:= T#5S,
|
|
bBusy=> ,
|
|
bError=> ,
|
|
nErrId=> ,
|
|
cbRead=> );
|
|
|
|
// Check if reading mudbus register is done
|
|
IF NOT _fbReadRegister.bBusy THEN
|
|
// If there was no error and the converter has no error continue
|
|
IF NOT _fbReadRegister.bError THEN
|
|
_iState := 123;
|
|
stCurrentValues.rActACCurrent := LREAL_TO_REAL(WORD_TO_INT(_awCurrentACValues[0]) * EXPT(10,WORD_TO_INT(_awCurrentACValues[4])));
|
|
stCurrentValues.rActtACPhaseACurrent := LREAL_TO_REAL(WORD_TO_INT(_awCurrentACValues[1]) * EXPT(10,WORD_TO_INT(_awCurrentACValues[4])));
|
|
stCurrentValues.rActtACPhaseBCurrent := LREAL_TO_REAL(WORD_TO_INT(_awCurrentACValues[2]) * EXPT(10,WORD_TO_INT(_awCurrentACValues[4])));
|
|
stCurrentValues.rActtACPhaseCCurrent := LREAL_TO_REAL(WORD_TO_INT(_awCurrentACValues[3]) * EXPT(10,WORD_TO_INT(_awCurrentACValues[4])));
|
|
stCurrentValues.rActACPower := LREAL_TO_REAL(WORD_TO_INT(_awCurrentACValues[12]) * EXPT(10,WORD_TO_INT(_awCurrentACValues[13])));
|
|
stCurrentValues.rActACFreq := LREAL_TO_REAL(WORD_TO_UINT(_awCurrentACValues[14]) * EXPT(10,WORD_TO_INT(_awCurrentACValues[15])));
|
|
stCurrentValues.rActApparentPower := LREAL_TO_REAL(WORD_TO_INT(_awCurrentACValues[16]) * EXPT(10,WORD_TO_INT(_awCurrentACValues[17])));
|
|
stCurrentValues.rActReactivePower := LREAL_TO_REAL(WORD_TO_INT(_awCurrentACValues[18]) * EXPT(10,WORD_TO_INT(_awCurrentACValues[19])));
|
|
stCurrentValues.rActPowerFactor := LREAL_TO_REAL(WORD_TO_INT(_awCurrentACValues[20]) * EXPT(10,WORD_TO_INT(_awCurrentACValues[21])));
|
|
ELSE
|
|
// Read error register
|
|
_iState := 1000;
|
|
END_IF
|
|
_fbReadRegister(bExecute := FALSE);
|
|
END_IF
|
|
|
|
|
|
123: // Read current status
|
|
_fbReadRegister(
|
|
sIPAddr:= sInverterIPAddr,
|
|
nTCPPort:= 502,
|
|
nUnitID:= 16#01, // 16#FF for Modbus TCP
|
|
nQuantity:= 1,
|
|
nMBAddr:= STATUS_REGISTER,
|
|
cbLength:= SIZEOF(_uiInverterState),
|
|
pDestAddr:= ADR(_uiInverterState),
|
|
bExecute:= TRUE,
|
|
tTimeout:= T#5S,
|
|
bBusy=> ,
|
|
bError=> ,
|
|
nErrId=> ,
|
|
cbRead=> );
|
|
|
|
// Check if reading mudbus register is done
|
|
IF NOT _fbReadRegister.bBusy THEN
|
|
IF (NOT _fbReadRegister.bError) THEN
|
|
// IF (NOT xEnable) THEN
|
|
// _iState := 110;
|
|
// ELSE
|
|
// _iState := 140;
|
|
// END_IF
|
|
_iState := 124;
|
|
ELSE
|
|
_iState := 1000;
|
|
END_IF
|
|
_fbReadRegister(bExecute := FALSE);
|
|
END_IF
|
|
|
|
124: // Reset if in error state and not enabled
|
|
// Inverter in error state
|
|
IF (_uiInverterState = 7) THEN
|
|
// Inverter not enabled, so try a reset
|
|
IF (NOT xEnable) THEN
|
|
_uiPCSSetOperation := 1;
|
|
_iState := 125;
|
|
ELSE
|
|
// Else go to error state
|
|
_iState := 1000;
|
|
END_IF
|
|
ELSE
|
|
// No error and not enabled
|
|
IF (NOT xEnable) THEN
|
|
// Goto polling state
|
|
_iState := 110;
|
|
ELSE
|
|
// No error and enabled, goto enabled polling state
|
|
_iState := 140;
|
|
END_IF
|
|
END_IF
|
|
|
|
|
|
125: // Send off/reset command
|
|
_fbWriteRegister(
|
|
sIPAddr:= sInverterIPAddr,
|
|
nTCPPort:= 502,
|
|
nUnitID:= 16#01, // 16#FF for Modbus TCP
|
|
nQuantity:= 1,
|
|
nMBAddr:= PCS_SET_OPERATION_REGISTER,
|
|
cbLength:= SIZEOF(_uiPCSSetOperation),
|
|
pSrcAddr:= ADR(_uiPCSSetOperation),
|
|
bExecute:= TRUE,
|
|
tTimeout:= T#5S,
|
|
bBusy=> ,
|
|
bError=> ,
|
|
nErrId=> );
|
|
|
|
// If writing modbus register is done
|
|
IF NOT _fbWriteRegister.bBusy THEN
|
|
// And there is no error, then continue
|
|
IF (NOT _fbWriteRegister.bError) THEN
|
|
_iState := 110;
|
|
ELSE
|
|
// Goto error state
|
|
_iState := 1000;
|
|
END_IF
|
|
_fbWriteRegister(bExecute := FALSE);
|
|
END_IF
|
|
|
|
|
|
130: // Go to started
|
|
_fbWriteRegister(
|
|
sIPAddr:= sInverterIPAddr,
|
|
nTCPPort:= 502,
|
|
nUnitID:= 16#01, // 16#FF for Modbus TCP
|
|
nQuantity:= 1,
|
|
nMBAddr:= PCS_SET_OPERATION_REGISTER,
|
|
cbLength:= SIZEOF(_uiPCSSetOperation),
|
|
pSrcAddr:= ADR(_uiPCSSetOperation),
|
|
bExecute:= TRUE,
|
|
tTimeout:= T#5S,
|
|
bBusy=> ,
|
|
bError=> ,
|
|
nErrId=> );
|
|
|
|
// If writing modbus register is done
|
|
IF NOT _fbWriteRegister.bBusy THEN
|
|
// And there is no error, then continue
|
|
IF (NOT _fbWriteRegister.bError) THEN
|
|
_iState := 133;
|
|
ELSE
|
|
_uiPCSSetOperation := 1;
|
|
// Goto error state
|
|
_iState := 1000;
|
|
END_IF
|
|
_fbWriteRegister(bExecute := FALSE);
|
|
END_IF
|
|
|
|
|
|
131: // Wait for inverter to be online and in state Grid Connected(11)
|
|
_fbReadRegister(
|
|
sIPAddr:= sInverterIPAddr,
|
|
nTCPPort:= 502,
|
|
nUnitID:= 16#01, // 16#FF for Modbus TCP
|
|
nQuantity:= 1,
|
|
nMBAddr:= STATUS_REGISTER,
|
|
cbLength:= SIZEOF(_uiInverterState),
|
|
pDestAddr:= ADR(_uiInverterState),
|
|
bExecute:= TRUE,
|
|
tTimeout:= T#5S,
|
|
bBusy=> ,
|
|
bError=> ,
|
|
nErrId=> ,
|
|
cbRead=> );
|
|
|
|
// Check if reading mudbus register is done
|
|
IF NOT _fbReadRegister.bBusy THEN
|
|
// If there was no error and the state is Grid Connected(11) then continue
|
|
IF NOT _fbReadRegister.bError AND _uiInverterState = 8 THEN
|
|
_uiInverterState := 11;
|
|
_iState := 133;
|
|
END_IF
|
|
|
|
// If the inverter is not ready wait some time before polling again
|
|
IF NOT _fbReadRegister.bError AND _uiInverterState <> 8 THEN
|
|
_iState := 132;
|
|
END_IF
|
|
|
|
_fbReadRegister(bExecute := FALSE);
|
|
END_IF
|
|
|
|
// If not enable, go back to idle
|
|
IF (NOT xEnable) THEN
|
|
_fbReadRegister(bExecute := FALSE);
|
|
_iState := 110;
|
|
END_IF
|
|
|
|
132: // Delay polling inverter ready
|
|
_tonPollingTimer(IN := TRUE, PT := _timPollingDelay);
|
|
IF _tonPollingTimer.Q THEN
|
|
_tonPollingTimer(IN := FALSE);
|
|
_iState := 131;
|
|
END_IF
|
|
|
|
// If not enable, go back to idle
|
|
IF (NOT xEnable) THEN
|
|
_tonPollingTimer(IN := FALSE);
|
|
_iState := 110;
|
|
END_IF
|
|
|
|
|
|
133: // Standby mode to grid connect
|
|
_fbReadRegister(
|
|
sIPAddr:= sInverterIPAddr,
|
|
nTCPPort:= 502,
|
|
nUnitID:= 16#01, // 16#FF for Modbus TCP
|
|
nQuantity:= 1,
|
|
nMBAddr:= STATUS_REGISTER,
|
|
cbLength:= SIZEOF(_uiInverterState),
|
|
pDestAddr:= ADR(_uiInverterState),
|
|
bExecute:= TRUE,
|
|
tTimeout:= T#5S,
|
|
bBusy=> ,
|
|
bError=> ,
|
|
nErrId=> ,
|
|
cbRead=> );
|
|
|
|
// Check if reading mudbus register is done
|
|
IF NOT _fbReadRegister.bBusy THEN
|
|
// If there was no error and the state is Grid Connected(11) then continue
|
|
IF NOT _fbReadRegister.bError AND _uiInverterState = 4 THEN
|
|
//_uiPCSSetOperation := 11;
|
|
xActive := TRUE;
|
|
_iState := 140;
|
|
END_IF
|
|
|
|
// If the inverter is not ready wait some time before polling again
|
|
IF NOT _fbReadRegister.bError AND _uiInverterState <> 4 THEN
|
|
_iState := 134;
|
|
END_IF
|
|
|
|
_fbReadRegister(bExecute := FALSE);
|
|
END_IF
|
|
|
|
// If not enable, go back to idle
|
|
// IF (NOT xEnable) THEN
|
|
// _fbReadRegister(bExecute := FALSE);
|
|
// _iState := 110;
|
|
// END_IF
|
|
|
|
134: // Polling timer delay
|
|
_tonPollingTimer(IN := TRUE, PT := _timPollingDelay);
|
|
IF _tonPollingTimer.Q THEN
|
|
_tonPollingTimer(IN := FALSE);
|
|
_iState := 133;
|
|
END_IF
|
|
|
|
// If not enable, go back to idle
|
|
IF (NOT xEnable) THEN
|
|
_tonPollingTimer(IN := FALSE);
|
|
_iState := 110;
|
|
END_IF
|
|
|
|
140: // Enabled
|
|
_tonPollingTimer(IN := TRUE);
|
|
IF _tonPollingTimer.Q THEN
|
|
_tonPollingTimer(IN := FALSE, PT := _timPollingDelay);
|
|
|
|
// Calculate power to write to register
|
|
_iWSetPct := LREAL_TO_INT((_rPowerInternal*100)/(_rWMax * EXPT(10,-2)));
|
|
|
|
_iState := 120;
|
|
END_IF
|
|
|
|
// If enable and INTLK Ok
|
|
IF (NOT xEnable) THEN
|
|
_rPowerInternal := 0.0;
|
|
_iWSetPct := 0;
|
|
_uiPCSSetOperation := 1;
|
|
_tonPollingTimer(IN := FALSE, PT := _timPollingDelay);
|
|
_iState := 150;
|
|
END_IF
|
|
|
|
150: // Send shutdown power command
|
|
_fbWriteRegister(
|
|
sIPAddr:= sInverterIPAddr,
|
|
nTCPPort:= 502,
|
|
nUnitID:= 16#01, // 16#FF for Modbus TCP
|
|
nQuantity:= 1,
|
|
nMBAddr:= W_SET_PCT,
|
|
cbLength:= SIZEOF(_iWSetPct),
|
|
pSrcAddr:= ADR(_iWSetPct),
|
|
bExecute:= TRUE,
|
|
tTimeout:= T#5S,
|
|
bBusy=> ,
|
|
bError=> ,
|
|
nErrId=> );
|
|
|
|
IF NOT _fbWriteRegister.bBusy THEN
|
|
IF (NOT _fbWriteRegister.bError) THEN
|
|
_iState := 151;
|
|
ELSE
|
|
// Goto error state
|
|
_iState := 1000;
|
|
END_IF
|
|
_fbWriteRegister(bExecute := FALSE);
|
|
END_IF
|
|
|
|
151: // Goto off state
|
|
_fbWriteRegister(
|
|
sIPAddr:= sInverterIPAddr,
|
|
nTCPPort:= 502,
|
|
nUnitID:= 16#01, // 16#FF for Modbus TCP
|
|
nQuantity:= 1,
|
|
nMBAddr:= PCS_SET_OPERATION_REGISTER,
|
|
cbLength:= SIZEOF(_uiPCSSetOperation),
|
|
pSrcAddr:= ADR(_uiPCSSetOperation),
|
|
bExecute:= TRUE,
|
|
tTimeout:= T#5S,
|
|
bBusy=> ,
|
|
bError=> ,
|
|
nErrId=> );
|
|
|
|
// If writing modbus register is done
|
|
IF NOT _fbWriteRegister.bBusy THEN
|
|
// And there is no error, then continue
|
|
IF (NOT _fbWriteRegister.bError) THEN
|
|
xActive := FALSE;
|
|
_iState := 110;
|
|
ELSE
|
|
// Goto error state
|
|
_iState := 1000;
|
|
END_IF
|
|
_fbWriteRegister(bExecute := FALSE);
|
|
END_IF
|
|
|
|
|
|
1000: // Error state
|
|
xActive := FALSE;
|
|
xError := TRUE;
|
|
_iState := 1001;
|
|
|
|
1001: // Error state, wait for reset
|
|
IF xReset AND (NOT xEnable) THEN
|
|
_uiPCSSetOperation := 1;
|
|
xError := FALSE;
|
|
_iState := 1002;
|
|
END_IF
|
|
|
|
|
|
1002: // Try to clear all error by going into off state
|
|
_fbWriteRegister(
|
|
sIPAddr:= sInverterIPAddr,
|
|
nTCPPort:= 502,
|
|
nUnitID:= 16#01, // 16#FF for Modbus TCP
|
|
nQuantity:= 1,
|
|
nMBAddr:= PCS_SET_OPERATION_REGISTER,
|
|
cbLength:= SIZEOF(_uiPCSSetOperation),
|
|
pSrcAddr:= ADR(_uiPCSSetOperation),
|
|
bExecute:= TRUE,
|
|
tTimeout:= T#5S,
|
|
bBusy=> ,
|
|
bError=> ,
|
|
nErrId=> );
|
|
|
|
// If writing modbus register is done
|
|
IF NOT _fbWriteRegister.bBusy THEN
|
|
// And there is no error, then continue
|
|
IF (NOT _fbWriteRegister.bError) THEN
|
|
xActive := FALSE;
|
|
_iState := 110;
|
|
ELSE
|
|
// Goto error state
|
|
_iState := 1000;
|
|
END_IF
|
|
_fbWriteRegister(bExecute := FALSE);
|
|
END_IF
|
|
|
|
END_CASE
|
|
// Calculate power to write to register
|
|
// (could not find where the scaling for wset can be read but its -2!)
|
|
// => 10% = 1000
|
|
// Writing a register with scaling factor = value / (10^SF)
|
|
//_iWSetPct := LREAL_TO_INT((_rPowerInternal*100)/(_rWMax * EXPT(10,-2)));]]></ST>
|
|
</Implementation>
|
|
<Method Name="FB_Init" Id="{5f7291f3-1517-49b9-b6a8-07debcc66730}">
|
|
<Declaration><![CDATA[//FB_Init ist immer implizit verfügbar und wird primär für die Initialisierung verwendet.
|
|
//Der Rückgabewert wird nicht ausgewertet. Für gezielte Einflussnahme können Sie
|
|
//die Methoden explizit deklarieren und darin mit dem Standard-Initialisierungscode
|
|
//zusätzlichen Code bereitstellen. Sie können den Rückgabewert auswerten.
|
|
METHOD FB_Init: BOOL
|
|
VAR_INPUT
|
|
bInitRetains: BOOL; // TRUE: Die Retain-Variablen werden initialisiert (Reset warm / Reset kalt)
|
|
bInCopyCode: BOOL; // TRUE: Die Instanz wird danach in den Kopiercode kopiert (Online-Change)
|
|
|
|
sName : STRING;
|
|
END_VAR]]></Declaration>
|
|
<Implementation>
|
|
<ST><![CDATA[_sName := sName;
|
|
|
|
// Create inverter main alarm
|
|
_fbErrorInverterAlarm.CreateEx(stEventEntry := TC_EVENTS.Inverter.InverterError, bWithConfirmation := TRUE, 0);
|
|
_fbErrorInverterAlarm.ipArguments.Clear().AddString(_sName);]]></ST>
|
|
</Implementation>
|
|
</Method>
|
|
<Property Name="Name" Id="{1af22804-e4c4-4295-b5b9-5968e747d45b}">
|
|
<Declaration><![CDATA[PROPERTY Name : STRING]]></Declaration>
|
|
<Get Name="Get" Id="{6338c761-e06b-4a94-a0d3-0502e3ee997d}">
|
|
<Declaration><![CDATA[VAR
|
|
END_VAR
|
|
]]></Declaration>
|
|
<Implementation>
|
|
<ST><![CDATA[Name := _sName;]]></ST>
|
|
</Implementation>
|
|
</Get>
|
|
<Set Name="Set" Id="{eebb6389-e8f3-42a9-a08a-6e1cad8f0192}">
|
|
<Declaration><![CDATA[VAR
|
|
END_VAR
|
|
]]></Declaration>
|
|
<Implementation>
|
|
<ST><![CDATA[_sName := Name;
|
|
|
|
_fbErrorInverterAlarm.ipArguments.Clear().AddString(_sName);]]></ST>
|
|
</Implementation>
|
|
</Set>
|
|
</Property>
|
|
<Action Name="SetBatteryLimits" Id="{15c86a66-2f5b-42ab-82c5-3aeebcab0e43}">
|
|
<Implementation>
|
|
<ST><![CDATA[CASE _iStateStartup OF
|
|
0: // Start
|
|
IF _xSetBatteryLimits THEN
|
|
_xSetBatteryLimits := FALSE;
|
|
_xBatteryLimitsSet := FALSE;
|
|
_xErrorSetBatteryLimits := FALSE;
|
|
_xStartupBusy := TRUE;
|
|
_iStateStartup := 10;
|
|
END_IF
|
|
|
|
10: // Read scaling factors
|
|
_fbReadRegister(
|
|
sIPAddr:= sInverterIPAddr,
|
|
nTCPPort:= 502,
|
|
nUnitID:= 16#FF, // 16#FF for Modbus TCP
|
|
nQuantity:= 2,
|
|
nMBAddr:= BATTERY_LIMIT_SF,
|
|
cbLength:= SIZEOF(_arBattScalingFactors),
|
|
pDestAddr:= ADR(_arBattScalingFactors),
|
|
bExecute:= TRUE,
|
|
tTimeout:= T#5S,
|
|
bBusy=> ,
|
|
bError=> ,
|
|
nErrId=> ,
|
|
cbRead=> );
|
|
|
|
// Check if reading mudbus register is done
|
|
IF NOT _fbReadRegister.bBusy THEN
|
|
IF (NOT _fbReadRegister.bError) THEN
|
|
_iState := 20;
|
|
ELSE
|
|
// Goto error state
|
|
//_xErrorSetBatteryLimits := TRUE;
|
|
_iState := 1000;
|
|
END_IF
|
|
_fbReadRegister(bExecute := FALSE);
|
|
END_IF
|
|
|
|
20: // Set min voltage
|
|
_fbWriteRegister(
|
|
sIPAddr:= sInverterIPAddr,
|
|
nTCPPort:= 502,
|
|
nUnitID:= 16#FF, // 16#FF for Modbus TCP
|
|
nQuantity:= 1,
|
|
nMBAddr:= DIS_MIN_V,
|
|
cbLength:= SIZEOF(uiMinDisVoltage),
|
|
pSrcAddr:= ADR(uiMinDisVoltage),
|
|
bExecute:= TRUE,
|
|
tTimeout:= T#5S,
|
|
bBusy=> ,
|
|
bError=> ,
|
|
nErrId=> );
|
|
|
|
// If writing modbus register is done
|
|
IF NOT _fbWriteRegister.bBusy THEN
|
|
// And there is no error, then continue
|
|
IF (NOT _fbWriteRegister.bError) THEN
|
|
_iState := 30;
|
|
ELSE
|
|
// Goto error state
|
|
//_xErrorSetBatteryLimits := TRUE;
|
|
_iState := 1000;
|
|
END_IF
|
|
_fbWriteRegister(bExecute := FALSE);
|
|
END_IF
|
|
|
|
30: // Set max voltage
|
|
_fbWriteRegister(
|
|
sIPAddr:= sInverterIPAddr,
|
|
nTCPPort:= 502,
|
|
nUnitID:= 16#FF, // 16#FF for Modbus TCP
|
|
nQuantity:= 1,
|
|
nMBAddr:= CHA_MAX_V,
|
|
cbLength:= SIZEOF(uiMaxChaVoltage),
|
|
pSrcAddr:= ADR(uiMaxChaVoltage),
|
|
bExecute:= TRUE,
|
|
tTimeout:= T#5S,
|
|
bBusy=> ,
|
|
bError=> ,
|
|
nErrId=> );
|
|
|
|
// If writing modbus register is done
|
|
IF NOT _fbWriteRegister.bBusy THEN
|
|
// And there is no error, then continue
|
|
IF (NOT _fbWriteRegister.bError) THEN
|
|
_iState := 40;
|
|
ELSE
|
|
// Goto error state
|
|
//_xErrorSetBatteryLimits := TRUE;
|
|
_iState := 1000;
|
|
END_IF
|
|
_fbWriteRegister(bExecute := FALSE);
|
|
END_IF
|
|
|
|
40: // Set charge current
|
|
_fbWriteRegister(
|
|
sIPAddr:= sInverterIPAddr,
|
|
nTCPPort:= 502,
|
|
nUnitID:= 16#FF, // 16#FF for Modbus TCP
|
|
nQuantity:= 1,
|
|
nMBAddr:= CHA_MAX_A,
|
|
cbLength:= SIZEOF(uiMaxChaCurrent),
|
|
pSrcAddr:= ADR(uiMaxChaCurrent),
|
|
bExecute:= TRUE,
|
|
tTimeout:= T#5S,
|
|
bBusy=> ,
|
|
bError=> ,
|
|
nErrId=> );
|
|
|
|
// If writing modbus register is done
|
|
IF NOT _fbWriteRegister.bBusy THEN
|
|
// And there is no error, then continue
|
|
IF (NOT _fbWriteRegister.bError) THEN
|
|
_iState := 50;
|
|
ELSE
|
|
// Goto error state
|
|
_xErrorSetBatteryLimits := TRUE;
|
|
_iState := 1000;
|
|
END_IF
|
|
_fbWriteRegister(bExecute := FALSE);
|
|
END_IF
|
|
|
|
50: // Set discharge current
|
|
_fbWriteRegister(
|
|
sIPAddr:= sInverterIPAddr,
|
|
nTCPPort:= 502,
|
|
nUnitID:= 16#FF, // 16#FF for Modbus TCP
|
|
nQuantity:= 1,
|
|
nMBAddr:= DIS_MAX_A,
|
|
cbLength:= SIZEOF(uiMaxDisCurrent),
|
|
pSrcAddr:= ADR(uiMaxDisCurrent),
|
|
bExecute:= TRUE,
|
|
tTimeout:= T#5S,
|
|
bBusy=> ,
|
|
bError=> ,
|
|
nErrId=> );
|
|
|
|
// If writing modbus register is done
|
|
IF NOT _fbWriteRegister.bBusy THEN
|
|
// And there is no error, then continue
|
|
IF (NOT _fbWriteRegister.bError) THEN
|
|
_iState := 60;
|
|
ELSE
|
|
// Goto error state
|
|
_xErrorSetBatteryLimits := TRUE;
|
|
_iState := 1000;
|
|
END_IF
|
|
_fbWriteRegister(bExecute := FALSE);
|
|
END_IF
|
|
|
|
60: // Enable battery limits
|
|
_fbWriteRegister(
|
|
sIPAddr:= sInverterIPAddr,
|
|
nTCPPort:= 502,
|
|
nUnitID:= 16#FF, // 16#FF for Modbus TCP
|
|
nQuantity:= 1,
|
|
nMBAddr:= EN_LIMIT,
|
|
cbLength:= SIZEOF(_uiEnableLimit),
|
|
pSrcAddr:= ADR(_uiEnableLimit),
|
|
bExecute:= TRUE,
|
|
tTimeout:= T#5S,
|
|
bBusy=> ,
|
|
bError=> ,
|
|
nErrId=> );
|
|
|
|
// If writing modbus register is done
|
|
IF NOT _fbWriteRegister.bBusy THEN
|
|
// And there is no error, then continue
|
|
IF (NOT _fbWriteRegister.bError) THEN
|
|
// Battery limits set
|
|
_xBatteryLimitsSet := TRUE;
|
|
_xStartupBusy := FALSE;
|
|
_iState := 0;
|
|
ELSE
|
|
// Goto error state
|
|
//_xErrorSetBatteryLimits := TRUE;
|
|
_iState := 1000;
|
|
END_IF
|
|
_fbWriteRegister(bExecute := FALSE);
|
|
END_IF
|
|
|
|
1000: // Error state
|
|
_iStateStartup := 0;
|
|
_xErrorSetBatteryLimits := TRUE;
|
|
_xBatteryLimitsSet := FALSE;
|
|
_xStartupBusy := FALSE;
|
|
END_CASE]]></ST>
|
|
</Implementation>
|
|
</Action>
|
|
</POU>
|
|
</TcPlcObject> |