diff --git a/PLC/POUs/Sunspec/Kaco/E_KACO_PCU_REQUESTED_STATE.TcDUT b/PLC/POUs/Sunspec/Kaco/E_KACO_PCU_REQUESTED_STATE.TcDUT
new file mode 100644
index 0000000..acd2dd6
--- /dev/null
+++ b/PLC/POUs/Sunspec/Kaco/E_KACO_PCU_REQUESTED_STATE.TcDUT
@@ -0,0 +1,18 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/PLC/POUs/Sunspec/Kaco/FB_PowerSupplyKaco.TcPOU b/PLC/POUs/Sunspec/Kaco/FB_PowerSupplyKaco.TcPOU
index 6cda1a9..9db725d 100644
--- a/PLC/POUs/Sunspec/Kaco/FB_PowerSupplyKaco.TcPOU
+++ b/PLC/POUs/Sunspec/Kaco/FB_PowerSupplyKaco.TcPOU
@@ -49,7 +49,8 @@ VAR
_rPowerInternal : REAL := 0;
// Enum for requested state
- _eRequestedState : (OFF := 1, STANDBY := 8, GRID_PRE_CONNECTED := 10, GRID_CONNECTED := 11) := OFF;
+ _eRequestedState : E_KACO_PCU_REQUESTED_STATE := E_KACO_PCU_REQUESTED_STATE.OFF;
+ _eLastRequestedState : E_KACO_PCU_REQUESTED_STATE := E_KACO_PCU_REQUESTED_STATE.UNDEFINED;
// Watchdog timeout in seconds
_uiWatchdogTimeoutSeconds : UINT := 10;
@@ -85,20 +86,13 @@ VAR
_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;
+ _timPollingDelay : TIME := T#200MS;
// 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);
+ // watchdog write timeout
_tTimeoutWriteWatchdogRegister : TIME := T#5S;
// Inverter alarm
@@ -199,10 +193,12 @@ VAR CONSTANT
// 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)
@@ -248,7 +244,6 @@ IF (rPower < -rMaxBattPower) THEN
_rPowerInternal := -rMaxBattPower;
END_IF
-HandleHeartbeat();
HandleCyclicData();
CASE _iState OF
@@ -256,7 +251,7 @@ CASE _iState OF
_fbTONSetBatteryLimits(IN := TRUE);
IF _fbTONSetBatteryLimits.Q THEN
_fbTONSetBatteryLimits(IN := FALSE);
- _eRequestedState := OFF;
+ _eRequestedState := E_KACO_PCU_REQUESTED_STATE.OFF;
_iStateStartup := 0;
_iState := 10;
END_IF
@@ -348,7 +343,7 @@ CASE _iState OF
40: // Idle state, wait for enable
// If enable and INTLK Ok
IF xEnable THEN
- _eRequestedState := GRID_CONNECTED;
+ _eRequestedState := E_KACO_PCU_REQUESTED_STATE.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!)
@@ -360,14 +355,14 @@ CASE _iState OF
_iWSetPct := 0;
END_IF
ELSE
- _eRequestedState := OFF;
+ _eRequestedState := E_KACO_PCU_REQUESTED_STATE.OFF;
_iWSetPct := 0;
END_IF
// Comm error or Watchdog error occured
IF _xErrorCyclicData OR (NOT xHeartbeatOk) THEN
_iWSetPct := 0;
- _eRequestedState := OFF;
+ _eRequestedState := E_KACO_PCU_REQUESTED_STATE.OFF;
_iState := 1000;
END_IF
@@ -384,7 +379,7 @@ CASE _iState OF
1001: // Error state, wait for reset
IF xReset AND (NOT xEnable) AND (NOT _xErrorInverter) AND (NOT _xErrorCyclicData) AND xHeartbeatOk THEN
- _eRequestedState := OFF;
+ _eRequestedState := E_KACO_PCU_REQUESTED_STATE.OFF;
xError := FALSE;
_xFaultInverter := FALSE;
_xErrorCyclicDataLedge := FALSE;
@@ -463,14 +458,16 @@ _fbCyclicDataAlarm.ipArguments.Clear().AddString(_sName);]]>
_iErrorIDRACV := 0;
END_IF
-// Fetch cyclic data with polling timer
-_tonPollingTimer(IN := TRUE, PT := _timPollingDelay);
-
CASE _iStateCyclicData OF
0: // init state
+ _tonPollingTimer(IN := TRUE, PT := _timPollingDelay);
IF _tonPollingTimer.Q THEN
_tonPollingTimer(IN := FALSE);
- _iStateCyclicData := 10;
+ IF _eRequestedState <> _eLastRequestedState THEN
+ _iStateCyclicData := 10;
+ ELSE
+ _iStateCyclicData := 20;
+ END_IF
END_IF
10: // Write requested state
@@ -488,15 +485,23 @@ CASE _iStateCyclicData OF
IF (NOT _fbWriteRequestedState.bBusy) THEN
_fbWriteRequestedState(bExecute := FALSE);
- _iStateCyclicData := 20;
+ _iStateCyclicData := 11;
IF _fbWriteRequestedState.bError THEN
_iCurrentErrorCountWRS := _iCurrentErrorCountWRS + 1;
_iErrorCountWRS := _iErrorCountWRS + 1;
_iErrorIDWRS := _fbWriteRequestedState.nErrId;
ELSE
_iCurrentErrorCountWRS := 0;
+ _eLastRequestedState := _eRequestedState;
END_IF
END_IF
+
+ 11: // wait for next state
+ _tonPollingTimer(IN := TRUE, PT := _timPollingDelay);
+ IF _tonPollingTimer.Q THEN
+ _tonPollingTimer(IN := FALSE);
+ _iStateCyclicData := 20;
+ END_IF
20: // Write current power command
_fbWritePowerCommand(
@@ -515,7 +520,7 @@ CASE _iStateCyclicData OF
IF (NOT _fbWritePowerCommand.bBusy) THEN
_fbWritePowerCommand(bExecute := FALSE);
- _iStateCyclicData := 30;
+ _iStateCyclicData := 21;
IF _fbWritePowerCommand.bError THEN
_iCurrentErrorCountWPC := _iCurrentErrorCountWPC + 1;
_iErrorCountWPC := _iErrorCountWPC + 1;
@@ -525,6 +530,13 @@ CASE _iStateCyclicData OF
END_IF
END_IF
+ 21: // wait for next state
+ _tonPollingTimer(IN := TRUE, PT := _timPollingDelay);
+ IF _tonPollingTimer.Q THEN
+ _tonPollingTimer(IN := FALSE);
+ _iStateCyclicData := 30;
+ END_IF
+
30: // Read current state
_fbReadCurrentState(
sIPAddr:= sInverterIPAddr,
@@ -543,7 +555,7 @@ CASE _iStateCyclicData OF
IF (NOT _fbReadCurrentState.bBusy) THEN
_fbReadCurrentState(bExecute := FALSE);
- _iStateCyclicData := 40;
+ _iStateCyclicData := 31;
IF _fbReadCurrentState.bError THEN
_iCurrentErrorCountRCS := _iCurrentErrorCountRCS + 1;
_iErrorCountRCS := _iErrorCountRCS + 1;
@@ -552,6 +564,13 @@ CASE _iStateCyclicData OF
_iCurrentErrorCountRCS := 0;
END_IF
END_IF
+
+ 31: // wait for next state
+ _tonPollingTimer(IN := TRUE, PT := _timPollingDelay);
+ IF _tonPollingTimer.Q THEN
+ _tonPollingTimer(IN := FALSE);
+ _iStateCyclicData := 40;
+ END_IF
40: // Read current pcu status
_fbReadPCUState(
@@ -571,7 +590,7 @@ CASE _iStateCyclicData OF
IF (NOT _fbReadPCUState.bBusy) THEN
_fbReadPCUState(bExecute := FALSE);
- _iStateCyclicData := 50;
+ _iStateCyclicData := 41;
IF _fbReadPCUState.bError THEN
_iCurrentErrorCountRPCUS := _iCurrentErrorCountRPCUS + 1;
_iErrorCountRPCUS := _iErrorCountRPCUS + 1;
@@ -584,6 +603,13 @@ CASE _iStateCyclicData OF
END_IF
END_IF
+ 41: // wait for next state
+ _tonPollingTimer(IN := TRUE, PT := _timPollingDelay);
+ IF _tonPollingTimer.Q THEN
+ _tonPollingTimer(IN := FALSE);
+ _iStateCyclicData := 50;
+ END_IF
+
50: // Read current dc values
_fbReadDCValues(
sIPAddr:= sInverterIPAddr,
@@ -603,7 +629,7 @@ CASE _iStateCyclicData OF
// Check if reading modbus register is done
IF (NOT _fbReadDCValues.bBusy) THEN
_fbReadDCValues(bExecute := FALSE);
- _iStateCyclicData := 60;
+ _iStateCyclicData := 51;
// 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])));
@@ -620,6 +646,13 @@ CASE _iStateCyclicData OF
END_IF
END_IF
+ 51: // wait for next state
+ _tonPollingTimer(IN := TRUE, PT := _timPollingDelay);
+ IF _tonPollingTimer.Q THEN
+ _tonPollingTimer(IN := FALSE);
+ _iStateCyclicData := 60;
+ END_IF
+
60: // Read current ac values
_fbReadACValues(
sIPAddr:= sInverterIPAddr,
@@ -639,7 +672,7 @@ CASE _iStateCyclicData OF
// Check if reading mudbus register is done
IF (NOT _fbReadACValues.bBusy) THEN
_fbReadACValues(bExecute := FALSE);
- _iStateCyclicData := 0;
+ _iStateCyclicData := 61;
// 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])));
@@ -667,8 +700,49 @@ CASE _iStateCyclicData OF
_iErrorIDRACV := _fbReadACValues.nErrId;
END_IF
END_IF
+
+ 61: // wait for next state
+ _tonPollingTimer(IN := TRUE, PT := _timPollingDelay);
+ IF _tonPollingTimer.Q THEN
+ _tonPollingTimer(IN := FALSE);
+ _iStateCyclicData := 70;
+ END_IF
+
+ 70:
+ // Timeout should be less than timer interval
+ _fbWriteHearbeatRegister(
+ sIPAddr:= sInverterIPAddr,
+ nTCPPort:= 502,
+ nUnitID:= 16#01,
+ nMBAddr:= WATCHDOG_REGISTER,
+ nValue:= _uiWatchdogTimeoutSeconds,
+ bExecute:= TRUE,
+ 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 NOT _fbWriteHearbeatRegister.bBusy THEN
+ _fbWriteHearbeatRegister(bExecute := FALSE);
+ _iStateCyclicData := 0;
+ IF _fbWriteHearbeatRegister.bError THEN
+ _iCurrentErrorCountHB := _iCurrentErrorCountHB + 1;
+ _iErrorCountHB := _iErrorCountHB + 1;
+ _iErrorIDHB := _fbWriteHearbeatRegister.nErrId;
+ IF _iCurrentErrorCountHB >= GVL_CONFIG.udiMaxConsecutiveInvError THEN
+ xHeartbeatOk := FALSE;
+ xError := TRUE;
+ END_IF
+ ELSE
+ _iCurrentErrorCountHB := 0;
+ xHeartbeatOk := TRUE;
+ END_IF
+ END_IF
END_CASE
+
// evaluate inverter state
IF _eCurrentState = E_KACO_CURRENT_STATE.GRID_CONNECTED OR _eCurrentState = E_KACO_CURRENT_STATE.THROTTLED THEN
xActive := TRUE;
@@ -710,7 +784,7 @@ IF _xErrorCyclicData THEN
_xErrorCyclicDataLedge := TRUE;
END_IF
-// handle alarm
+// handle cyclic data alarm
IF _xErrorCyclicData AND NOT _fbCyclicDataAlarm.bRaised THEN
_fbCyclicDataAlarm.Raise();
END_IF
@@ -721,42 +795,6 @@ END_IF
IF _fbCyclicDataAlarm.eConfirmationState = TcEventConfirmationState.WaitForConfirmation AND xReset THEN
_fbCyclicDataAlarm.Confirm(0);
-END_IF]]>
-
-
-
-
- ,
- bError=> ,
- nErrId=> );
-
-// Because there is no heartbeat register to read,
-// we will use a successfull write as a valid heartbeat signal
-IF NOT _fbWriteHearbeatRegister.bBusy THEN
- IF _fbWriteHearbeatRegister.bError THEN
- _iCurrentErrorCountHB := _iCurrentErrorCountHB + 1;
- _iErrorCountHB := _iErrorCountHB + 1;
- _iErrorIDHB := _fbWriteHearbeatRegister.nErrId;
- IF _iCurrentErrorCountHB >= GVL_CONFIG.udiMaxConsecutiveInvError THEN
- xHeartbeatOk := FALSE;
- xError := TRUE;
- END_IF
- ELSE
- _iCurrentErrorCountHB := 0;
- xHeartbeatOk := TRUE;
- END_IF
END_IF
// set fault flag when error active
@@ -764,7 +802,7 @@ IF NOT xHeartbeatOk THEN
_xHeartBeatNOK := TRUE;
END_IF
-// handle alarm
+// handle heartbeat alarm
IF NOT xHeartbeatOk AND NOT _fbHeartBeatAlarm.bRaised THEN
_fbHeartBeatAlarm.Raise();
END_IF
@@ -775,11 +813,6 @@ END_IF
IF _fbHeartBeatAlarm.eConfirmationState = TcEventConfirmationState.WaitForConfirmation AND xReset THEN
_fbHeartBeatAlarm.Confirm(0);
-END_IF
-
-// Reset timer
-IF _tonWatchdogResetTimer.Q THEN
- _tonWatchdogResetTimer(IN := FALSE);
END_IF]]>