Added Kaco inverter implementation
- Added Kaco inverter implementation - Small refactoring - Added release inverter power signal for simultaneous start of the inverters
This commit is contained in:
25
PLC/POUs/Sunspec/Kaco/E_KACO_CURRENT_STATE.TcDUT
Normal file
25
PLC/POUs/Sunspec/Kaco/E_KACO_CURRENT_STATE.TcDUT
Normal file
@@ -0,0 +1,25 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<TcPlcObject Version="1.1.0.1" ProductVersion="3.1.4026.12">
|
||||
<DUT Name="E_KACO_CURRENT_STATE" Id="{2acb7f0e-c391-4c10-827a-e41a4c5478d9}">
|
||||
<Declaration><![CDATA[{attribute 'qualified_only'}
|
||||
{attribute 'strict'}
|
||||
{attribute 'to_string'}
|
||||
TYPE E_KACO_CURRENT_STATE :
|
||||
(
|
||||
OFF := 1,
|
||||
SLEEPING := 2,
|
||||
STARTING := 3,
|
||||
MPPT := 4,
|
||||
THROTTLED := 5,
|
||||
SHUTTING_DOWN := 6,
|
||||
FAULT := 7,
|
||||
STANDBY := 8,
|
||||
PRECHARGE := 9,
|
||||
GRID_PRE_CONNECTED := 10,
|
||||
GRID_CONNECTED := 11,
|
||||
NO_ERROR_PENDING := 12
|
||||
);
|
||||
END_TYPE
|
||||
]]></Declaration>
|
||||
</DUT>
|
||||
</TcPlcObject>
|
||||
24
PLC/POUs/Sunspec/Kaco/E_KACO_PCU_ERROR.TcDUT
Normal file
24
PLC/POUs/Sunspec/Kaco/E_KACO_PCU_ERROR.TcDUT
Normal file
@@ -0,0 +1,24 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<TcPlcObject Version="1.1.0.1" ProductVersion="3.1.4026.12">
|
||||
<DUT Name="E_KACO_PCU_ERROR" Id="{9d943541-7a37-4e53-993d-e5fb676cf523}">
|
||||
<Declaration><![CDATA[{attribute 'qualified_only'}
|
||||
{attribute 'strict'}
|
||||
{attribute 'to_string'}
|
||||
TYPE E_KACO_PCU_ERROR :
|
||||
(
|
||||
NO_EVENT := 0,
|
||||
OVER_TEMP := 1,
|
||||
OVER_VOLT := 2,
|
||||
UNDER_VOLT := 3,
|
||||
BATT_POL_INCORREC := 4,
|
||||
COUNTER_TOO_HIGH := 5,
|
||||
DURING_PRECHARGE := 6,
|
||||
BATT_VOLT_OUT_OF_RANGE := 7,
|
||||
I2C_COMM := 8,
|
||||
CAN_COMM := 9,
|
||||
SWITCH_OFF_AC_DSP := 10
|
||||
);
|
||||
END_TYPE
|
||||
]]></Declaration>
|
||||
</DUT>
|
||||
</TcPlcObject>
|
||||
22
PLC/POUs/Sunspec/Kaco/E_KACO_PCU_STATE.TcDUT
Normal file
22
PLC/POUs/Sunspec/Kaco/E_KACO_PCU_STATE.TcDUT
Normal file
@@ -0,0 +1,22 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<TcPlcObject Version="1.1.0.1" ProductVersion="3.1.4026.12">
|
||||
<DUT Name="E_KACO_PCU_STATE" Id="{d3455f44-85c3-4eb1-962f-0096c587ed27}">
|
||||
<Declaration><![CDATA[{attribute 'qualified_only'}
|
||||
{attribute 'strict'}
|
||||
{attribute 'to_string'}
|
||||
TYPE E_KACO_PCU_STATE :
|
||||
(
|
||||
WAIT_FOR_STARTUP :=1,
|
||||
STANDBY :=2,
|
||||
SWITCH_REL_MINUS :=3,
|
||||
SWITCH_REL_PRECHARGE :=4,
|
||||
SWITCH_REL_PLUS :=5,
|
||||
RUNNING :=6,
|
||||
COOLDOWN :=7,
|
||||
ERROR :=8,
|
||||
CLEAR_ERROR :=9
|
||||
);
|
||||
END_TYPE
|
||||
]]></Declaration>
|
||||
</DUT>
|
||||
</TcPlcObject>
|
||||
848
PLC/POUs/Sunspec/Kaco/FB_PowerSupplyKaco.TcPOU
Normal file
848
PLC/POUs/Sunspec/Kaco/FB_PowerSupplyKaco.TcPOU
Normal file
@@ -0,0 +1,848 @@
|
||||
<?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;
|
||||
xReleasePower : BOOL;
|
||||
rPower : REAL;
|
||||
xReset : BOOL;
|
||||
rMaxBattPower : REAL := 40_000; // 24kW
|
||||
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
|
||||
// Battery limits
|
||||
// 0 - Min discharge voltage
|
||||
// 1 - Max discharge current
|
||||
// 2 - Discharge cutoff amp -> 0 = off
|
||||
// 3 - Max charge voltage
|
||||
// 4 - Max chrage current
|
||||
// 5 - Charge cutoff amp -> 0 = off
|
||||
_auiBatteryLimitValues : ARRAY[0..5] OF UINT := [6600, 300, 0, 9600, 300, 0];
|
||||
|
||||
// Current state
|
||||
_iState : INT := 0;
|
||||
|
||||
// State for startup state machine
|
||||
_iStateStartup : INT := 0;
|
||||
|
||||
// Startup busy flag
|
||||
_xStartupBusy : BOOL;
|
||||
|
||||
// Internal power command
|
||||
_rPowerInternal : REAL := 0;
|
||||
|
||||
// Enum for requested state
|
||||
_eRequestedState : (OFF := 1, STANDBY := 8, GRID_PRE_CONNECTED := 10, GRID_CONNECTED := 11) := OFF;
|
||||
|
||||
// Watchdog timeout in seconds
|
||||
_uiWatchdogTimeoutSeconds : UINT := 10;
|
||||
|
||||
// FB for reading Modbus holding registers
|
||||
_fbReadRegisters : FB_MBReadRegs;
|
||||
|
||||
// FB for writing Modbus holding registers
|
||||
_fbWriteRegisters : FB_MBWriteRegs;
|
||||
|
||||
// FB for writing heartbeat register
|
||||
_fbWriteHearbeatRegister : FB_MBWriteSingleReg;
|
||||
|
||||
// FB for writing requested state
|
||||
_fbWriteRequestedState : FB_MBWriteSingleReg;
|
||||
|
||||
// FB for writing current power command
|
||||
// Write multiple registers is used here
|
||||
// because FB_MBWriteSingleReg expects an
|
||||
// unsigned data type
|
||||
_fbWritePowerCommand : FB_MBWriteRegs;
|
||||
|
||||
// FB for reading current state
|
||||
_fbReadCurrentState : FB_MBReadRegs;
|
||||
|
||||
// FB for reading pcu state register
|
||||
_fbReadPCUState : FB_MBReadRegs;
|
||||
|
||||
// FB for reading dc values
|
||||
_fbReadDCValues : FB_MBReadRegs;
|
||||
|
||||
// FB for reading ac values
|
||||
_fbReadACValues : FB_MBReadRegs;
|
||||
|
||||
// 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;
|
||||
_tTimeoutPolling : TIME := T#5S;
|
||||
|
||||
// Timer for setting the inverter power
|
||||
_tonSetPowerTimer : TON;
|
||||
|
||||
// Timer for watchdog
|
||||
_tonWatchdogResetTimer : TON := (PT := T#1S);
|
||||
_tTimeoutWriteWatchdogRegister : TIME := T#5S;
|
||||
|
||||
// Inverter alarm
|
||||
_fbErrorInverterAlarm : FB_TcAlarm;
|
||||
|
||||
// Flag if battery limits have been set
|
||||
_xBatteryLimitsSet : 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#5S);
|
||||
|
||||
// Inverter power output
|
||||
_iWSetPct : INT := 0;
|
||||
|
||||
// Converter max power scaling factor
|
||||
_iWMaxSF : INT;
|
||||
|
||||
// Scaling factor for power setpoint
|
||||
_iWSetPctSF : INT := -2;
|
||||
|
||||
// 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
|
||||
_eCurrentState : E_KACO_CURRENT_STATE;
|
||||
|
||||
// Current PCU state and alarm messages
|
||||
_stPCUState : ST_KACU_PCU;
|
||||
|
||||
// Error during cyclic reading
|
||||
_xErrorCyclicData : BOOL;
|
||||
|
||||
// Internal inverter error
|
||||
_xErrorInverter : BOOL;
|
||||
|
||||
// Inverter name for alarm message
|
||||
_sName : STRING;
|
||||
END_VAR
|
||||
VAR CONSTANT
|
||||
// Battery limits registers (Model 64202)
|
||||
// 41120 is Voltage and 41121 is amp
|
||||
BATTERY_LIMIT_SF_START : WORD := 41120;
|
||||
BATTERY_SET_LIMITS_START : WORD := 41122;
|
||||
DIS_MIN_V : WORD := 41122;
|
||||
DIS_MAX_A : WORD := 41123;
|
||||
CHA_MAX_V : WORD := 41125;
|
||||
CHA_MAX_A : WORD := 41126;
|
||||
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)
|
||||
PCU_STATUS_START_REGISTER : WORD := 41078;
|
||||
|
||||
// Inverter current state
|
||||
CURRENT_STATE_REGISTER : WORD := 41065;
|
||||
|
||||
// Control register to set the target state of the inverters state machine
|
||||
// Size 1, enum16 (Range = 0 .. 65534, Not implemented = 0xFFFF)
|
||||
REQUESTED_STATE_REGISTER : WORD := 41064;
|
||||
|
||||
// Hearbeat register
|
||||
WATCHDOG_REGISTER : WORD := 41068;
|
||||
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
|
||||
|
||||
HandleHeartbeat();
|
||||
HandleCyclicData();
|
||||
|
||||
CASE _iState OF
|
||||
0: // Pre-init phase (no battery limits set)
|
||||
_fbTONSetBatteryLimits(IN := TRUE);
|
||||
IF _fbTONSetBatteryLimits.Q THEN
|
||||
_fbTONSetBatteryLimits(IN := FALSE);
|
||||
_eRequestedState := OFF;
|
||||
_iStateStartup := 0;
|
||||
_iState := 10;
|
||||
END_IF
|
||||
|
||||
10: // Try to set battery limits
|
||||
SetBatteryLimits();
|
||||
IF (NOT _xStartupBusy) THEN
|
||||
// Battery limits set and no error
|
||||
IF _xBatteryLimitsSet AND (NOT _xErrorSetBatteryLimits) THEN
|
||||
_iWSetPct := 0;
|
||||
_iState := 20;
|
||||
END_IF
|
||||
|
||||
// If there was an error settings the battery limits, retry
|
||||
IF _xErrorSetBatteryLimits THEN
|
||||
_iState := 0;
|
||||
END_IF
|
||||
END_IF
|
||||
|
||||
|
||||
20: // Read max power scaling
|
||||
_fbReadRegisters(
|
||||
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 _fbReadRegisters.bBusy THEN
|
||||
// If there was no error then continue
|
||||
IF NOT _fbReadRegisters.bError THEN
|
||||
_iState := 30;
|
||||
// Check for valid value
|
||||
IF (_iWMaxSF < -10) OR (_iWMaxSF > 10) OR (_iWMaxSF = 16#8000) THEN
|
||||
// Goto error state
|
||||
_iState := 1000;
|
||||
END_IF
|
||||
ELSE
|
||||
xError := TRUE;
|
||||
// Goto error state
|
||||
_iState := 1000;
|
||||
END_IF
|
||||
_fbReadRegisters(bExecute := FALSE);
|
||||
END_IF
|
||||
|
||||
|
||||
30: // Read max power
|
||||
_fbReadRegisters(
|
||||
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 _fbReadRegisters.bBusy THEN
|
||||
// If there was no error then continue
|
||||
IF NOT _fbReadRegisters.bError THEN
|
||||
_iState := 40;
|
||||
// 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
|
||||
_fbReadRegisters(bExecute := FALSE);
|
||||
END_IF
|
||||
|
||||
|
||||
40: // Idle state, wait for enable
|
||||
// If enable and INTLK Ok
|
||||
IF xEnable THEN
|
||||
_eRequestedState := GRID_CONNECTED;
|
||||
IF xReleasePower THEN
|
||||
// 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)));
|
||||
_iWSetPct := REAL_TO_INT((_rPowerInternal*100) / (_rWMax * EXPT(10,_iWSetPctSF)));
|
||||
ELSE
|
||||
_iWSetPct := 0;
|
||||
END_IF
|
||||
ELSE
|
||||
_eRequestedState := OFF;
|
||||
_iWSetPct := 0;
|
||||
END_IF
|
||||
|
||||
// Comm error or Watchdog error occured
|
||||
IF _xErrorCyclicData OR (NOT xHeartbeatOk) THEN
|
||||
_iWSetPct := 0;
|
||||
_eRequestedState := OFF;
|
||||
_iState := 1000;
|
||||
END_IF
|
||||
|
||||
// Dont set inverter into off state when an internal error occured
|
||||
// because this will reset the error message
|
||||
IF _xErrorInverter THEN
|
||||
_iWSetPct := 0;
|
||||
_iState := 1000;
|
||||
END_IF
|
||||
|
||||
1000: // Error state
|
||||
xError := TRUE;
|
||||
_iState := 1001;
|
||||
|
||||
1001: // Error state, wait for reset
|
||||
IF xReset AND (NOT xEnable) AND (NOT _xErrorCyclicData) AND (NOT _xErrorInverter) THEN
|
||||
_eRequestedState := OFF;
|
||||
xError := FALSE;
|
||||
_iState := 0;
|
||||
END_IF
|
||||
|
||||
|
||||
END_CASE
|
||||
|
||||
// ===============================
|
||||
// Inverter alarm handling
|
||||
// ===============================
|
||||
IF xError AND (NOT _fbErrorInverterAlarm.bRaised) THEN
|
||||
_fbErrorInverterAlarm.Raise(0);
|
||||
END_IF
|
||||
|
||||
IF (NOT xError) AND _fbErrorInverterAlarm.bRaised THEN
|
||||
_fbErrorInverterAlarm.Clear(0, FALSE);
|
||||
END_IF
|
||||
|
||||
IF (_fbErrorInverterAlarm.eConfirmationState = TcEventConfirmationState.WaitForConfirmation) AND xReset THEN
|
||||
_fbErrorInverterAlarm.Confirm(0);
|
||||
END_IF]]></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>
|
||||
<Action Name="HandleCyclicData" Id="{4343583a-b80a-437e-8fc8-9963ab894fbc}">
|
||||
<Implementation>
|
||||
<ST><![CDATA[// Reset error flags on reset command
|
||||
IF _xErrorCyclicData AND xReset THEN
|
||||
_xErrorCyclicData := FALSE;
|
||||
END_IF
|
||||
|
||||
// Fetch cyclic data with polling timer
|
||||
_tonPollingTimer(IN := TRUE);
|
||||
|
||||
// Write requested state
|
||||
_fbWriteRequestedState(
|
||||
sIPAddr:= sInverterIPAddr,
|
||||
nTCPPort:= 502,
|
||||
nUnitID:= 16#01,
|
||||
nMBAddr:= REQUESTED_STATE_REGISTER,
|
||||
nValue:= INT_TO_WORD(_eRequestedState),
|
||||
bExecute:= _tonPollingTimer.Q AND (NOT _fbWriteRequestedState.bBusy),
|
||||
tTimeout:= _tTimeoutPolling,
|
||||
bBusy=> ,
|
||||
bError=> ,
|
||||
nErrId=> );
|
||||
|
||||
IF (NOT _fbWriteRequestedState.bBusy) AND _fbWriteRequestedState.bError THEN
|
||||
_xErrorCyclicData := TRUE;
|
||||
END_IF
|
||||
|
||||
|
||||
// Write current power command
|
||||
_fbWritePowerCommand(
|
||||
sIPAddr:= sInverterIPAddr,
|
||||
nTCPPort:= 502,
|
||||
nUnitID:= 16#01,
|
||||
nQuantity := 1,
|
||||
nMBAddr:= W_SET_PCT,
|
||||
cbLength := SIZEOF(_iWSetPct),
|
||||
pSrcAddr:= ADR(_iWSetPct),
|
||||
bExecute:= _tonPollingTimer.Q AND (NOT _fbWritePowerCommand.bBusy),
|
||||
tTimeout:= _tTimeoutPolling,
|
||||
bBusy=> ,
|
||||
bError=> ,
|
||||
nErrId=> );
|
||||
|
||||
IF (NOT _fbWritePowerCommand.bBusy) AND _fbWritePowerCommand.bError THEN
|
||||
_xErrorCyclicData := TRUE;
|
||||
END_IF
|
||||
|
||||
|
||||
// Read current state
|
||||
_fbReadCurrentState(
|
||||
sIPAddr:= sInverterIPAddr,
|
||||
nTCPPort:= 502,
|
||||
nUnitID:= 16#01,
|
||||
nQuantity:= 1,
|
||||
nMBAddr:= CURRENT_STATE_REGISTER,
|
||||
cbLength:= SIZEOF(_eCurrentState),
|
||||
pDestAddr:= ADR(_eCurrentState),
|
||||
bExecute:= _tonPollingTimer.Q AND (NOT _fbReadCurrentState.bBusy),
|
||||
tTimeout:= _tTimeoutPolling,
|
||||
bBusy=> ,
|
||||
bError=> ,
|
||||
nErrId=> ,
|
||||
cbRead=> );
|
||||
|
||||
IF (NOT _fbReadCurrentState.bBusy) AND _fbReadCurrentState.bError THEN
|
||||
_xErrorCyclicData := TRUE;
|
||||
END_IF
|
||||
|
||||
IF _eCurrentState = E_KACO_CURRENT_STATE.GRID_CONNECTED OR _eCurrentState = E_KACO_CURRENT_STATE.THROTTLED THEN
|
||||
xActive := TRUE;
|
||||
ELSE
|
||||
xActive := FALSE;
|
||||
END_IF
|
||||
|
||||
|
||||
// Read current pcu status
|
||||
_fbReadPCUState(
|
||||
sIPAddr:= sInverterIPAddr,
|
||||
nTCPPort:= 502,
|
||||
nUnitID:= 16#01,
|
||||
nQuantity:= 2,
|
||||
nMBAddr:= PCU_STATUS_START_REGISTER,
|
||||
cbLength:= SIZEOF(_stPCUState),
|
||||
pDestAddr:= ADR(_stPCUState),
|
||||
bExecute:= _tonPollingTimer.Q AND (NOT _fbReadPCUState.bBusy),
|
||||
tTimeout:= _tTimeoutPolling,
|
||||
bBusy=> ,
|
||||
bError=> ,
|
||||
nErrId=> ,
|
||||
cbRead=> );
|
||||
|
||||
IF (NOT _fbReadPCUState.bBusy) AND _fbReadPCUState.bError THEN
|
||||
_xErrorCyclicData := TRUE;
|
||||
END_IF
|
||||
|
||||
IF (_stPCUState.ePCUState = E_KACO_PCU_STATE.ERROR) OR (_stPCUState.ePCUError <> E_KACO_PCU_ERROR.NO_EVENT) THEN
|
||||
_xErrorInverter := TRUE;
|
||||
ELSE
|
||||
_xErrorInverter := FALSE;
|
||||
END_IF
|
||||
|
||||
|
||||
// Read current dc values
|
||||
_fbReadDCValues(
|
||||
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:= _tonPollingTimer.Q AND (NOT _fbReadDCValues.bBusy),
|
||||
tTimeout:= _tTimeoutPolling,
|
||||
bBusy=> ,
|
||||
bError=> ,
|
||||
nErrId=> ,
|
||||
cbRead=> );
|
||||
|
||||
// Check if reading modbus register is done
|
||||
IF (NOT _fbReadDCValues.bBusy) THEN
|
||||
// If there was no error and the converter has no error continue
|
||||
IF (NOT _fbReadDCValues.bError) THEN
|
||||
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
|
||||
// Dont throw comm error here because this is just
|
||||
// informational data and not process critical
|
||||
stCurrentValues.rActDCCurrent := 0.0;
|
||||
stCurrentValues.rActDCVoltage := 0.0;
|
||||
stCurrentValues.rActDCPower := 0.0;
|
||||
END_IF
|
||||
END_IF
|
||||
|
||||
|
||||
// Read current ac values
|
||||
_fbReadACValues(
|
||||
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:= _tonPollingTimer.Q AND (NOT _fbReadACValues.bBusy),
|
||||
tTimeout:= _tTimeoutPolling,
|
||||
bBusy=> ,
|
||||
bError=> ,
|
||||
nErrId=> ,
|
||||
cbRead=> );
|
||||
|
||||
// Check if reading mudbus register is done
|
||||
IF (NOT _fbReadACValues.bBusy) THEN
|
||||
// If there was no error and the converter has no error continue
|
||||
IF (NOT _fbReadACValues.bError) THEN
|
||||
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
|
||||
// Dont throw comm error here because this is just
|
||||
// informational data and not process critical
|
||||
stCurrentValues.rActACCurrent := 0.0;
|
||||
stCurrentValues.rActtACPhaseACurrent := 0.0;
|
||||
stCurrentValues.rActtACPhaseBCurrent := 0.0;
|
||||
stCurrentValues.rActtACPhaseCCurrent := 0.0;
|
||||
stCurrentValues.rActACPower := 0.0;
|
||||
stCurrentValues.rActACFreq := 0.0;
|
||||
stCurrentValues.rActApparentPower := 0.0;
|
||||
stCurrentValues.rActReactivePower := 0.0;
|
||||
stCurrentValues.rActPowerFactor := 0.0;
|
||||
END_IF
|
||||
END_IF
|
||||
|
||||
// Reset polling timer
|
||||
IF _tonPollingTimer.Q THEN
|
||||
_tonPollingTimer(IN := FALSE);
|
||||
END_IF]]></ST>
|
||||
</Implementation>
|
||||
</Action>
|
||||
<Action Name="HandleHeartbeat" Id="{eeb5f65a-fd91-4c22-ab2e-3080c24e87fb}">
|
||||
<Implementation>
|
||||
<ST><![CDATA[// Reset hearbeat signal only with reset signal
|
||||
IF (NOT xHeartbeatOk) AND xReset THEN
|
||||
xHeartbeatOk := TRUE;
|
||||
END_IF
|
||||
|
||||
// Self resetting watchdog timer
|
||||
_tonWatchdogResetTimer(IN := TRUE);
|
||||
|
||||
// Timeout should be less than timer interval
|
||||
_fbWriteHearbeatRegister(
|
||||
sIPAddr:= sInverterIPAddr,
|
||||
nTCPPort:= 502,
|
||||
nUnitID:= 16#01,
|
||||
nMBAddr:= WATCHDOG_REGISTER,
|
||||
nValue:= _uiWatchdogTimeoutSeconds,
|
||||
bExecute:= _tonWatchdogResetTimer.Q AND (NOT _fbWriteHearbeatRegister.bBusy),
|
||||
tTimeout:= _tTimeoutWriteWatchdogRegister,
|
||||
bBusy=> ,
|
||||
bError=> ,
|
||||
nErrId=> );
|
||||
|
||||
// Because there is no heartbeat register to read,
|
||||
// we will use a successfull write as a valid heartbeat signal
|
||||
IF _fbWriteHearbeatRegister.bError THEN
|
||||
xHeartbeatOk := FALSE;
|
||||
xError := TRUE;
|
||||
END_IF
|
||||
|
||||
// Reset timer
|
||||
IF _tonWatchdogResetTimer.Q THEN
|
||||
_tonWatchdogResetTimer(IN := FALSE);
|
||||
END_IF]]></ST>
|
||||
</Implementation>
|
||||
</Action>
|
||||
<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
|
||||
_xBatteryLimitsSet := FALSE;
|
||||
_xErrorSetBatteryLimits := FALSE;
|
||||
_xStartupBusy := TRUE;
|
||||
_iStateStartup := 10;
|
||||
|
||||
10: // Read scaling factors
|
||||
_fbReadRegisters(
|
||||
sIPAddr:= sInverterIPAddr,
|
||||
nTCPPort:= 502,
|
||||
nUnitID:= 16#FF, // 16#FF for Modbus TCP
|
||||
nQuantity:= 2,
|
||||
nMBAddr:= BATTERY_LIMIT_SF_START,
|
||||
cbLength:= SIZEOF(_arBattScalingFactors),
|
||||
pDestAddr:= ADR(_arBattScalingFactors),
|
||||
bExecute:= TRUE,
|
||||
tTimeout:= T#5S,
|
||||
bBusy=> ,
|
||||
bError=> ,
|
||||
nErrId=> ,
|
||||
cbRead=> );
|
||||
|
||||
// Check if reading mudbus register is done
|
||||
IF NOT _fbReadRegisters.bBusy THEN
|
||||
IF (NOT _fbReadRegisters.bError) THEN
|
||||
_iStateStartup := 20;
|
||||
ELSE
|
||||
// Goto error state
|
||||
//_xErrorSetBatteryLimits := TRUE;
|
||||
_iStateStartup := 1000;
|
||||
END_IF
|
||||
_fbReadRegisters(bExecute := FALSE);
|
||||
END_IF
|
||||
|
||||
20: // Set battery limits
|
||||
_fbWriteRegisters(
|
||||
sIPAddr:= sInverterIPAddr,
|
||||
nTCPPort:= 502,
|
||||
nUnitID:= 16#FF, // 16#FF for Modbus TCP
|
||||
nQuantity:= 6,
|
||||
nMBAddr:= BATTERY_SET_LIMITS_START,
|
||||
cbLength:= SIZEOF(_auiBatteryLimitValues),
|
||||
pSrcAddr:= ADR(_auiBatteryLimitValues),
|
||||
bExecute:= TRUE,
|
||||
tTimeout:= T#5S,
|
||||
bBusy=> ,
|
||||
bError=> ,
|
||||
nErrId=> );
|
||||
|
||||
// If writing modbus register is done
|
||||
IF NOT _fbWriteRegisters.bBusy THEN
|
||||
// And there is no error, then continue
|
||||
IF (NOT _fbWriteRegisters.bError) THEN
|
||||
_iStateStartup := 60;
|
||||
ELSE
|
||||
// Goto error state
|
||||
_iStateStartup := 1000;
|
||||
END_IF
|
||||
_fbWriteRegisters(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
|
||||
_iStateStartup := 30;
|
||||
ELSE
|
||||
// Goto error state
|
||||
//_xErrorSetBatteryLimits := TRUE;
|
||||
_iStateStartup := 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
|
||||
_iStateStartup := 40;
|
||||
ELSE
|
||||
// Goto error state
|
||||
//_xErrorSetBatteryLimits := TRUE;
|
||||
_iStateStartup := 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
|
||||
_iStateStartup := 50;
|
||||
ELSE
|
||||
// Goto error state
|
||||
_xErrorSetBatteryLimits := TRUE;
|
||||
_iStateStartup := 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
|
||||
_iStateStartup := 60;
|
||||
ELSE
|
||||
// Goto error state
|
||||
_xErrorSetBatteryLimits := TRUE;
|
||||
_iStateStartup := 1000;
|
||||
END_IF
|
||||
_fbWriteRegister(bExecute := FALSE);
|
||||
END_IF
|
||||
*)
|
||||
|
||||
60: // Enable battery limits
|
||||
_fbWriteRegisters(
|
||||
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 _fbWriteRegisters.bBusy THEN
|
||||
// And there is no error, then continue
|
||||
IF (NOT _fbWriteRegisters.bError) THEN
|
||||
_iStateStartup := 70;
|
||||
ELSE
|
||||
// Goto error state
|
||||
//_xErrorSetBatteryLimits := TRUE;
|
||||
_iState := 1000;
|
||||
END_IF
|
||||
_fbWriteRegisters(bExecute := FALSE);
|
||||
END_IF
|
||||
|
||||
70: // Battery limits set
|
||||
_xBatteryLimitsSet := TRUE;
|
||||
_xStartupBusy := FALSE;
|
||||
|
||||
|
||||
1000: // Error state
|
||||
_xErrorSetBatteryLimits := TRUE;
|
||||
_xBatteryLimitsSet := FALSE;
|
||||
_xStartupBusy := FALSE;
|
||||
END_CASE]]></ST>
|
||||
</Implementation>
|
||||
</Action>
|
||||
</POU>
|
||||
</TcPlcObject>
|
||||
13
PLC/POUs/Sunspec/Kaco/ST_KACU_PCU.TcDUT
Normal file
13
PLC/POUs/Sunspec/Kaco/ST_KACU_PCU.TcDUT
Normal file
@@ -0,0 +1,13 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<TcPlcObject Version="1.1.0.1" ProductVersion="3.1.4026.12">
|
||||
<DUT Name="ST_KACU_PCU" Id="{268343cf-ebff-47ff-8ac9-f65faeb58856}">
|
||||
<Declaration><![CDATA[{attribute 'pack_mode' := '1'}
|
||||
TYPE ST_KACU_PCU :
|
||||
STRUCT
|
||||
ePCUState : E_KACO_PCU_STATE;
|
||||
ePCUError : E_KACO_PCU_ERROR;
|
||||
END_STRUCT
|
||||
END_TYPE
|
||||
]]></Declaration>
|
||||
</DUT>
|
||||
</TcPlcObject>
|
||||
Reference in New Issue
Block a user