Inverter heartbeat _uiPLCToInverterCounter : UINT; // Inverter -> PLC heartbeat _uiInverterToPLCCounter : UINT; _uiInverterToPLCCounterOld : UINT; // Flag to check if inverter has incremented the heartbeat counter _xInverterHBCounterIncremented : BOOL := TRUE; // Inverter alarm _fbErrorInverterAlarm : FB_TcAlarm; // Heartbeat timeout _fbHeartbeatTimeout : TON; _sName : STRING; END_VAR VAR CONSTANT // Inverter statemachine status register // Size 1, enum16 (Range = 0 .. 65534, Not implemented = 0xFFFF) STATUS_REGISTER : WORD := 40108; // Throttled power register // Size 1, int16 (Range = -32767 .. 32767, Not implemented 0x8000) W_MAX_LIM_PCT_REGISTER : WORD := 40187; // Throttled power register scaling factor // Size 1, sunssf (int16) (Range = -10 .. 10, Not implemented 0x8000) W_MAX_LIM_PCT_SF_REGISTER : WORD := 40205; // Control register to enable and dissable if the power throttleing should be active // Size1, enum16 (Range = 0 .. 65534, Not implemented = 0xFFFF) W_MAX_LIM_EN_REGISTER : WORD := 40191; // Register to reset latched alarms in the inverter // Size 1, uint16 (Range = 0 .. 65534, Not implemented = 0xFFFF) PCS_ALARM_RESET_REGISTER : WORD := 40230; // 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 := 40231; // Maximum inverter output power // Size 1, uint16 (Range = 0 .. 65534, Not implemented = 0xFFFF) W_MAX_REGISTER : WORD := 40152; // Maximum inverter output power scaling factor // Size 1, sunssf (int16) (Range = -10 .. 10, Not implemented 0x8000) W_MAX_SF_REGISTER : WORD := 40172; // Maximum inverter output power from type label // Size 1, uint16 (Range = 0 .. 65534, Not implemented = 0xFFFF) W_RTG_REGISTER : WORD := 40125; // Maximum inverter output power from type label scaling factor // Size 1, sunssf (int16) (Range = -10 .. 10, Not implemented 0x8000) W_RTG_SF_REGISTER : WORD := 40126; // 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; // Power factor register in cosine of angle // Size 1, int16 (Range = -32767 .. 32767, Not implemented 0x8000) //OUT_PF_SET : WORD := 40192; // Enable power factor controller // Size 1, enum16 (Range = 0 .. 65534, Not implemented = 0xFFFF) //OUT_PF_SET_ENA : WORD := 40196; // Reactive power in percent of W_Max // Size 1, int16 (Range = -32767 .. 32767, Not implemented 0x8000) VAR_W_MAX_PCT : WORD := 40197; // Enable percent limited var controller // Size 1, enum16 (Range = 0 .. 65534, Not implemented = 0xFFFF) VAR_PCT_ENA : WORD := 40204; // Register for reactive power percent scaling factor // Size 1, sunssf (int16) (Range = -10 .. 10, Not implemented 0x8000) VAR_PCT_SF : WORD := 40207; // Error bits register // Size 2 EVT_1_REGISTER : WORD := 40110; // PLC -> Inverter Heartbeat register CONTROLLER_HB : WORD := 40229; // Inverter -> PLC heartbeat register PCS_HB : WORD := 40228; END_VAR ]]> rMaxBattPower) THEN rPower := rMaxBattPower; END_IF IF (rPower < -rMaxBattPower) THEN rPower := -rMaxBattPower; END_IF // State machine CASE _iState OF 0: // Off IF _tonPollingTimer.Q THEN _tonPollingTimer(IN := FALSE, PT := _timPollingDelay); _iState := 1; END_IF // If enable and INTLK Ok IF xEnable THEN _iState := 10; _tonPollingTimer(IN := FALSE, PT := _timPollingDelay); ELSE _tonPollingTimer(IN := TRUE, PT := _timPollingDelay); END_IF 1: // Read inverter status _fbReadRegister( sIPAddr:= sInverterIPAddr, nTCPPort:= 502, nUnitID:= 16#FF, // 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 _iState := 2; ELSE _iState := 1000; END_IF _fbReadRegister(bExecute := FALSE); END_IF 2: // IF inverter is not in STANDYB(8) STATE, send command to shutdown inverter IF (_uiInverterState = 8) OR (_uiInverterState = 1) OR (_uiInverterState = 7) THEN _iState := 3; ELSE _uiPCSSetOperation := 3; _iState := 200; END_IF 3: // Read current DC values _iErrorInState := _iState; _fbReadRegister( sIPAddr:= sInverterIPAddr, nTCPPort:= 502, nUnitID:= 16#FF, // 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 := 4; 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 // Read error register _iState := 1000; END_IF _fbReadRegister(bExecute := FALSE); END_IF 4: // Read current ac values _iErrorInState := _iState; _fbReadRegister( sIPAddr:= sInverterIPAddr, nTCPPort:= 502, nUnitID:= 16#FF, // 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 // Go back to polling state _iState := 5; 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 5: // Send heartbeat signal _uiPLCToInverterCounter := _uiPLCToInverterCounter + 1; _iErrorInState := _iState; _fbWriteRegister( sIPAddr:= sInverterIPAddr, nTCPPort:= 502, nUnitID:= 16#FF, // 16#FF for Modbus TCP nQuantity:= 1, nMBAddr:= CONTROLLER_HB, cbLength:= SIZEOF(_uiPLCToInverterCounter), pSrcAddr:= ADR(_uiPLCToInverterCounter), 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 := 6; ELSE xError := TRUE; // Goto error state _iState := 1000; END_IF _fbWriteRegister(bExecute := FALSE); END_IF 6: // Check heartbeat signal _fbReadRegister( sIPAddr:= sInverterIPAddr, nTCPPort:= 502, nUnitID:= 16#FF, // 16#FF for Modbus TCP nQuantity:= 1, nMBAddr:= PCS_HB, cbLength:= SIZEOF(_uiInverterToPLCCounter), pDestAddr:= ADR(_uiInverterToPLCCounter), bExecute:= TRUE, tTimeout:= T#5S, bBusy=> , bError=> , nErrId=> , cbRead=> ); IF NOT _fbReadRegister.bBusy THEN IF (NOT _fbReadRegister.bError) THEN // Check if counter has been incremented by the inverter IF (_uiInverterToPLCCounter - _uiInverterToPLCCounterOld) > 0 THEN _xInverterHBCounterIncremented := TRUE; // Safe old value _uiInverterToPLCCounterOld := _uiInverterToPLCCounter; ELSE _xInverterHBCounterIncremented := FALSE; END_IF _iState := 0; ELSE xError := TRUE; // Goto error state _iState := 1000; END_IF _fbReadRegister(bExecute := FALSE); END_IF 10: // Wait for inverter to be online and in state STANDBY(8) _fbReadRegister( sIPAddr:= sInverterIPAddr, nTCPPort:= 502, nUnitID:= 16#FF, // 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 STANDBY(8) then continue IF NOT _fbReadRegister.bError AND _uiInverterState = 8 THEN _iState := 20; END_IF // If the inverter is not ready wait some time before polling again IF NOT _fbReadRegister.bError AND _uiInverterState <> 8 THEN _iState := 15; END_IF _fbReadRegister(bExecute := FALSE); END_IF // If not enable, go back to idle IF NOT xEnable THEN _fbReadRegister(bExecute := FALSE); _iState := 0; END_IF 15: // Delay polling inverter ready _tonPollingTimer(IN := TRUE, PT := _timPollingDelay); IF _tonPollingTimer.Q THEN _tonPollingTimer(IN := FALSE); _iState := 10; END_IF // If not enable, go back to idle IF NOT xEnable THEN _tonPollingTimer(IN := FALSE); _iState := 0; END_IF 20: // Read inverter max power scaling _iErrorInState := _iState; _fbReadRegister( sIPAddr:= sInverterIPAddr, nTCPPort:= 502, nUnitID:= 16#FF, // 16#FF for Modbus TCP nQuantity:= 1, nMBAddr:= W_MAX_SF_REGISTER, 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 := 25; // 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 25: // Read inverter Max power limit scaling _iErrorInState := _iState; _fbReadRegister( sIPAddr:= sInverterIPAddr, nTCPPort:= 502, nUnitID:= 16#FF, // 16#FF for Modbus TCP nQuantity:= 1, nMBAddr:= W_MAX_LIM_PCT_SF_REGISTER, cbLength:= SIZEOF(_iWMaxLimPctSF), pDestAddr:= ADR(_iWMaxLimPctSF), 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 := 30; // Check for valid value IF (_iWMaxLimPctSF < -10) OR (_iWMaxLimPctSF > 10) OR (_iWMaxLimPctSF = 16#8000) THEN // Goto error state _iState := 1000; END_IF ELSE xError := TRUE; // Goto error state _iState := 1000; END_IF _fbReadRegister(bExecute := FALSE); END_IF 26: // Read inverter scaling factor for reactive power _iErrorInState := _iState; _fbReadRegister( sIPAddr:= sInverterIPAddr, nTCPPort:= 502, nUnitID:= 16#FF, // 16#FF for Modbus TCP nQuantity:= 1, nMBAddr:= VAR_PCT_SF, cbLength:= SIZEOF(_iVarPctSF), pDestAddr:= ADR(_iVarPctSF), 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 := 30; // Check for valid value IF (_iVarPctSF < -10) OR (_iVarPctSF > 10) OR (_iVarPctSF = 16#8000) THEN // Goto error state _iState := 1000; END_IF ELSE xError := TRUE; // Goto error state _iState := 1000; END_IF _fbReadRegister(bExecute := FALSE); END_IF 30: // Read inverter max power _iErrorInState := _iState; _fbReadRegister( sIPAddr:= sInverterIPAddr, nTCPPort:= 502, nUnitID:= 16#FF, // 16#FF for Modbus TCP nQuantity:= 1, nMBAddr:= W_MAX_REGISTER, 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 := 40; // Reading a register with scaling factor = value * 10^SF _rWMax := LREAL_TO_REAL(_uiWMax * EXPT(10,_iWMaxSF)); // Calculate power to write to register _iWMaxLimPct := LREAL_TO_INT((rPower*100)/(_rWMax * EXPT(10,_iWMaxLimPctSF))); ELSE xError := TRUE; // Goto error state _iState := 1000; END_IF _fbReadRegister(bExecute := FALSE); END_IF 40: // Set power limit _iErrorInState := _iState; _fbWriteRegister( sIPAddr:= sInverterIPAddr, nTCPPort:= 502, nUnitID:= 16#FF, // 16#FF for Modbus TCP nQuantity:= 1, nMBAddr:= W_MAX_LIM_PCT_REGISTER, cbLength:= SIZEOF(_iWMaxLimPct), pSrcAddr:= ADR(_iWMaxLimPct), 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; _rOldPower := rPower; _uiMaxLimEn := 1; // Calculate reactive power setting //_iMaxPowerVar := LREAL_TO_INT((rReactivePower*100)/(_iMaxPowerVar * EXPT(10,_iVarPctSF))); ELSE xError := TRUE; // Goto error state _iState := 1000; END_IF _fbWriteRegister(bExecute := FALSE); END_IF 41: // Set max reactive power in percent _iErrorInState := _iState; _fbWriteRegister( sIPAddr:= sInverterIPAddr, nTCPPort:= 502, nUnitID:= 16#FF, // 16#FF for Modbus TCP nQuantity:= 1, nMBAddr:= VAR_W_MAX_PCT, cbLength:= SIZEOF(_iMaxPowerVar), pSrcAddr:= ADR(_iMaxPowerVar), 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 := 42; ELSE xError := TRUE; // Goto error state _iState := 1000; END_IF _fbWriteRegister(bExecute := FALSE); END_IF 42: // Enable reactive power percent limiting _iErrorInState := _iState; _fbWriteRegister( sIPAddr:= sInverterIPAddr, nTCPPort:= 502, nUnitID:= 16#FF, // 16#FF for Modbus TCP nQuantity:= 1, nMBAddr:= VAR_PCT_ENA, cbLength:= SIZEOF(_iMaxVarPct), pSrcAddr:= ADR(_iMaxVarPct), 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 xError := TRUE; // Goto error state _iState := 1000; END_IF _fbWriteRegister(bExecute := FALSE); END_IF 50: // Enable Power limiting (THROTTLED) _iErrorInState := _iState; _fbWriteRegister( sIPAddr:= sInverterIPAddr, nTCPPort:= 502, nUnitID:= 16#FF, // 16#FF for Modbus TCP nQuantity:= 1, nMBAddr:= W_MAX_LIM_EN_REGISTER, cbLength:= SIZEOF(_uiMaxLimEn), pSrcAddr:= ADR(_uiMaxLimEn), 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 := 51; _uiPCSSetOperation := 4; ELSE xError := TRUE; // Goto error state _iState := 1000; END_IF _fbWriteRegister(bExecute := FALSE); END_IF 51: // Go to started _iErrorInState := _iState; _fbWriteRegister( sIPAddr:= sInverterIPAddr, nTCPPort:= 502, nUnitID:= 16#FF, // 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 _uiPCSSetOperation := 1; _iState := 60; ELSE // Goto error state _iState := 1000; END_IF _fbWriteRegister(bExecute := FALSE); END_IF 60: // Switch to THROTTLED mode _iErrorInState := _iState; _fbWriteRegister( sIPAddr:= sInverterIPAddr, nTCPPort:= 502, nUnitID:= 16#FF, // 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 := 65; ELSE // Goto error state _iState := 1000; END_IF _fbWriteRegister(bExecute := FALSE); END_IF 65: // Wait for polling timer _tonPollingTimer(IN := TRUE, PT := _timPollingDelay); IF _tonPollingTimer.Q THEN _tonPollingTimer(IN := FALSE); _iState := 70; ELSIF (ABS(rPower - _rOldPower) > 0.1) THEN //_tonPollingTimer(IN := FALSE); // If power has ben changed, goto set power limit mode _iState := 40; // Calculate power to write to register _iWMaxLimPct := LREAL_TO_INT((rPower*100)/(_rWMax * EXPT(10,_iWMaxLimPctSF))); END_IF // check if inverter should shut down IF NOT xEnable THEN _uiPCSSetOperation := 3; // Goto shutdown sequence _iState := 200; END_IF 70: // Enabled, check for error _iErrorInState := _iState; _fbReadRegister( sIPAddr:= sInverterIPAddr, nTCPPort:= 502, nUnitID:= 16#FF, // 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 converter has no error continue IF NOT _fbReadRegister.bError AND (_uiInverterState <> 7) THEN _iState := 80; IF (_uiInverterState = 4) OR (_uiInverterState = 5) THEN xActive := TRUE; ELSE xActive := FALSE; END_IF ELSE xError := TRUE; _uiPCSSetOperation := 3; // Read error register _iState := 200; END_IF _fbReadRegister(bExecute := FALSE); END_IF 80: // Read current DC values _iErrorInState := _iState; _fbReadRegister( sIPAddr:= sInverterIPAddr, nTCPPort:= 502, nUnitID:= 16#FF, // 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 := 85; stCurrentValues.rActDCCurrent := LREAL_TO_REAL(WORD_TO_INT(_awCurrentDCValues[0]) * EXPT(10,WORD_TO_INT(_awCurrentDCValues[1]))); stCurrentValues.rActDCVoltage := LREAL_TO_REAL(WORD_TO_INT(_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 // Read error register _iState := 1000; END_IF _fbReadRegister(bExecute := FALSE); END_IF 85: // Read current ac values _iErrorInState := _iState; _fbReadRegister( sIPAddr:= sInverterIPAddr, nTCPPort:= 502, nUnitID:= 16#FF, // 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 // Go back to polling state _iState := 90; 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 90: // Read current inverter status _fbReadRegister( sIPAddr:= sInverterIPAddr, nTCPPort:= 502, nUnitID:= 16#FF, // 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 _iState := 91; stCurrentValues.uiStatus := _uiInverterState; ELSE // Read error register _iState := 1000; END_IF _fbReadRegister(bExecute := FALSE); END_IF 91: // Send heartbeat signal _uiPLCToInverterCounter := _uiPLCToInverterCounter + 1; _iErrorInState := _iState; _fbWriteRegister( sIPAddr:= sInverterIPAddr, nTCPPort:= 502, nUnitID:= 16#FF, // 16#FF for Modbus TCP nQuantity:= 1, nMBAddr:= CONTROLLER_HB, cbLength:= SIZEOF(_uiPLCToInverterCounter), pSrcAddr:= ADR(_uiPLCToInverterCounter), 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 := 92; ELSE xError := TRUE; // Goto error state _iState := 1000; END_IF _fbWriteRegister(bExecute := FALSE); END_IF 92: // Check heartbeat signal _fbReadRegister( sIPAddr:= sInverterIPAddr, nTCPPort:= 502, nUnitID:= 16#FF, // 16#FF for Modbus TCP nQuantity:= 1, nMBAddr:= PCS_HB, cbLength:= SIZEOF(_uiInverterToPLCCounter), pDestAddr:= ADR(_uiInverterToPLCCounter), bExecute:= TRUE, tTimeout:= T#5S, bBusy=> , bError=> , nErrId=> , cbRead=> ); IF NOT _fbReadRegister.bBusy THEN IF (NOT _fbReadRegister.bError) THEN // Check if counter has been incremented by the inverter IF (_uiInverterToPLCCounter - _uiInverterToPLCCounterOld) > 0 THEN _xInverterHBCounterIncremented := TRUE; // Safe old value _uiInverterToPLCCounterOld := _uiInverterToPLCCounter; ELSE _xInverterHBCounterIncremented := FALSE; END_IF _iState := 65; ELSE xError := TRUE; // Goto error state _iState := 1000; END_IF _fbReadRegister(bExecute := FALSE); END_IF 200: // Shutdown sequence _iErrorInState := _iState; _fbWriteRegister( sIPAddr:= sInverterIPAddr, nTCPPort:= 502, nUnitID:= 16#FF, // 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 := 210; ELSE // Goto error state _iState := 1000; END_IF _fbWriteRegister(bExecute := FALSE); END_IF 210: // Wait for poll timer to _tonPollingTimer(IN := TRUE, PT := _timPollingDelay); IF _tonPollingTimer.Q THEN _tonPollingTimer(IN := FALSE); _iState := 220; END_IF 220: // Poll and wait for standby state _iErrorInState := _iState; _fbReadRegister( sIPAddr:= sInverterIPAddr, nTCPPort:= 502, nUnitID:= 16#FF, // 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 converter has no error continue IF NOT _fbReadRegister.bError THEN _iState := 0; xActive := FALSE; //xCloseDCRelais := FALSE; END_IF _fbReadRegister(bExecute := FALSE); END_IF 990: // Read error register _iErrorInState := _iState; _fbReadRegister( sIPAddr:= sInverterIPAddr, nTCPPort:= 502, nUnitID:= 16#FF, // 16#FF for Modbus TCP nQuantity:= 2, nMBAddr:= EVT_1_REGISTER, cbLength:= SIZEOF(_dwErrorBits), pDestAddr:= ADR(_dwErrorBits), 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 := 1000; END_IF _fbReadRegister(bExecute := FALSE); END_IF 1000: // Error state, wait for reset IF xReset AND (NOT xEnable) THEN _iState := 1010; END_IF 1010: // Try to clear all latched events _fbWriteRegister( sIPAddr:= sInverterIPAddr, nTCPPort:= 502, nUnitID:= 16#FF, // 16#FF for Modbus TCP nQuantity:= 1, nMBAddr:= PCS_ALARM_RESET_REGISTER, cbLength:= SIZEOF(_uiResetInverter), pSrcAddr:= ADR(_uiResetInverter), bExecute:= TRUE, tTimeout:= T#5S, bBusy=> , bError=> , nErrId=> ); IF NOT _fbWriteRegister.bBusy THEN _iState := 0; xError := FALSE; _fbWriteRegister(bExecute := FALSE); END_IF END_CASE // =============================== // Heartbeat check // =============================== _fbHeartbeatTimeout(IN := (NOT _xInverterHBCounterIncremented), PT := T#5S); // Reset heartbeat ok signal IF xReset AND (NOT _fbHeartbeatTimeout.Q) THEN xHeartbeatOk := TRUE; END_IF // Check for heartbeat IF _fbHeartbeatTimeout.Q THEN xHeartbeatOk := FALSE; END_IF // =============================== // 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]]>