, eState=> ); IF _xFirstCycle THEN _xFirstCycle := FALSE; _afbStrings[0].Name := 'String 1'; _afbStrings[1].Name := 'String 2'; END_IF // Dely release of errors during PLC startup phase _tonStartupDelay(IN := TRUE); // Call string 1 _afbStrings[0]( stStringModuleVoltageConfig := GVL_CONFIG.stString1VoltageConfig, xEnable := _xEnableString, //AND GVL_CONFIG.xDummy, xStartBalancing := _xStartBalancing, // AND GVL_CONFIG.xDummy, sInverterIP := GVL_CONFIG.sInverterIpString1, rPowerInverter := _rPowerInverter, xInSafetyCheckMode := _xInSafetyCheckMode, stHMIInterface:= GVL_SCADA.stHMIInterface[0], xEmergencyStopOk:= _xEmergencyStopOk, xReleaseErrors:= _xReleaseErrors AND _tonStartupDelay.Q AND _xEtherCatString1Ok, xReleaseLimitErrors:= _xReleaseLimitsErrors AND _tonStartupDelay.Q, xReleaseManualMode := _xReleaseManualMode, xConfirmAlarms:= _xConfirmAlarms, xAllToManualMode := _xAllComponentsToManualMode, xResetSafety := xSafetyResterTaster); IF _afbStrings[0].xError THEN _xErrorActive := TRUE; END_IF // Call string 2 _afbStrings[1]( stStringModuleVoltageConfig := GVL_CONFIG.stString2VoltageConfig, xEnable := _xEnableString, xStartBalancing := _xStartBalancing, sInverterIP := GVL_CONFIG.sInverterIpString2, rPowerInverter := _rPowerInverter, xInSafetyCheckMode := _xInSafetyCheckMode, stHMIInterface:= GVL_SCADA.stHMIInterface[1], xEmergencyStopOk:= _xEmergencyStopOk, xReleaseErrors:= _xReleaseErrors AND _tonStartupDelay.Q AND _xEtherCatString2Ok AND FALSE, xReleaseLimitErrors:= _xReleaseLimitsErrors AND _tonStartupDelay.Q AND _xEtherCatString2Ok, xReleaseManualMode := _xReleaseManualMode, xConfirmAlarms:= _xConfirmAlarms, xAllToManualMode := _xAllComponentsToManualMode, xResetSafety := xSafetyResterTaster); IF _afbStrings[1].xError THEN _xErrorActive := TRUE; END_IF // =============================== // Hardware reset button part 2 // =============================== _xShowErrorOnButton := _xErrorActive; // HMI Feedback GVL_SCADA.stHMIInterface[0].rVoltage := _afbStrings[0].rCurrentVoltage; IF _afbStrings[0].eStatus = E_COMPONENT_STATUS.ON THEN IF _iState = 30 AND _rPowerInverter > 0 THEN GVL_SCADA.stHMIInterface[0].eStatus := E_COMPONENT_STATUS.DISCHARGING; ELSIF _iState = 30 AND _rPowerInverter < 0 THEN GVL_SCADA.stHMIInterface[0].eStatus := E_COMPONENT_STATUS.CHARGING; ELSE GVL_SCADA.stHMIInterface[0].eStatus :=_afbStrings[0].eStatus; END_IF ELSE GVL_SCADA.stHMIInterface[0].eStatus :=_afbStrings[0].eStatus; END_IF // =============================== // Read modbus request count // =============================== _timADSReadTimer(IN := NOT _fbADSReader.BUSY, PT := T#200MS); _fbADSReader( NETID:= '', PORT:= 10500, IDXGRP:= 16#2000, IDXOFFS:= 1, LEN:= 4, DESTADDR:= ADR(GVL_MODBUS.stModbusEMSComm.stModbusReg11.udiLifeMessage), READ:= _timADSReadTimer.Q, TMOUT:= T#1S, BUSY=> , ERR=> , ERRID=> ); _fbModbusRead( sIPAddr:= '127.0.0.1', nTCPPort:= 502, nUnitID:= 16#FF, nQuantity:= 48, nMBAddr:= 11000, cbLength:= SIZEOF(_ModbusDebugTest), pDestAddr:= ADR(_ModbusDebugTest), bExecute:= xDebugTest, tTimeout:= T#5S, bBusy=> , bError=> , nErrId=> , cbRead=> ); // =============================== // Copy data to modbus registers // =============================== // Modbus current inverter values GVL_MODBUS.stModbusEMSComm.stModbusReg11.diCurrentActivePower := REAL_TO_DINT(_afbStrings[_uiDebugCurrentString].stInverterData.rActACPower); GVL_MODBUS.stModbusEMSComm.stModbusReg11.diCurrentReactivePower := REAL_TO_DINT(_afbStrings[_uiDebugCurrentString].stInverterData.rActReactivePower); GVL_MODBUS.stModbusEMSComm.stModbusReg11.diTotalACCurrentPhase1 := REAL_TO_DINT(_afbStrings[_uiDebugCurrentString].stInverterData.rActtACPhaseACurrent); GVL_MODBUS.stModbusEMSComm.stModbusReg11.diTotalACCurrentPhase2 := REAL_TO_DINT(_afbStrings[_uiDebugCurrentString].stInverterData.rActtACPhaseBCurrent); GVL_MODBUS.stModbusEMSComm.stModbusReg11.diTotalACCurrentPhase3 := REAL_TO_DINT(_afbStrings[_uiDebugCurrentString].stInverterData.rActtACPhaseCCurrent); // Set Modbus mirror values GVL_MODBUS.stModbusEMSComm.stModbusReg11.diSetpointActivePowerMirror := GVL_MODBUS.stModbusEMSComm.stModbusReg12.diSetpointActivePower; GVL_MODBUS.stModbusEMSComm.stModbusReg11.rSetpointCosPhiMirror := GVL_MODBUS.stModbusEMSComm.stModbusReg12.rSetpointCosPhi; // =============================== // Calculate highest and lowest // segment voltage // =============================== _rSmallestSegmentVoltage := _afbStrings[_uiDebugCurrentString].rSmallestSegmentVoltage; _rHighestSegmentVoltage := _afbStrings[_uiDebugCurrentString].rHighestSegmentVoltage; // =============================== // State machine // =============================== CASE _eBMSControlMode OF E_BMS_CONTROL_MODE.AUTO_REMOTE: _xAllComponentsToManualMode := FALSE; _xInSafetyCheckMode := FALSE; _xReleaseManualMode := FALSE; _rAutoPowerRequest := DINT_TO_REAL(GVL_MODBUS.stModbusEMSComm.stModbusReg12.diSetpointActivePower); IF (GVL_SCADA.eRequestedControlMode <> _eBMSControlMode) AND (GVL_SCADA.xCanChangeControlMode) THEN _eBMSControlMode := GVL_SCADA.eRequestedControlMode; END_IF SM_AUTO(); E_BMS_CONTROL_MODE.AUTO_LOCAL: _xAllComponentsToManualMode := FALSE; _xInSafetyCheckMode := FALSE; _xReleaseManualMode := FALSE; GVL_MODBUS.stModbusEMSComm.stModbusReg11.eBatteryStatus := E_BATTERY_STATUS.TESTING; _rAutoPowerRequest := DINT_TO_REAL(GVL_SCADA.stAutomaticModeHMI.diSetpointAutomatic); IF (GVL_SCADA.eRequestedControlMode <> _eBMSControlMode) AND (GVL_SCADA.xCanChangeControlMode) THEN _eBMSControlMode := GVL_SCADA.eRequestedControlMode; END_IF SM_AUTO(); E_BMS_CONTROL_MODE.MANUAL: _xAllComponentsToManualMode := TRUE; _xInSafetyCheckMode := FALSE; _xReleaseManualMode := TRUE; GVL_MODBUS.stModbusEMSComm.stModbusReg11.eBatteryStatus := E_BATTERY_STATUS.MAINTENANCE; IF (GVL_SCADA.eRequestedControlMode <> _eBMSControlMode) AND (GVL_SCADA.xCanChangeControlMode) THEN _eBMSControlMode := GVL_SCADA.eRequestedControlMode; END_IF SM_MANUAL(); E_BMS_CONTROL_MODE.SAFETY_CHECK: _xAllComponentsToManualMode := FALSE; _xInSafetyCheckMode := TRUE; _xReleaseManualMode := FALSE; GVL_MODBUS.stModbusEMSComm.stModbusReg11.eBatteryStatus := E_BATTERY_STATUS.MAINTENANCE; IF (GVL_SCADA.eRequestedControlMode <> _eBMSControlMode) AND (GVL_SCADA.xCanChangeControlMode) THEN _eBMSControlMode := GVL_SCADA.eRequestedControlMode; END_IF SM_SAFETY_CHECK(); E_BMS_CONTROL_MODE.CAPACITY_TEST: _xAllComponentsToManualMode := FALSE; _xInSafetyCheckMode := FALSE; _xReleaseManualMode := FALSE; GVL_MODBUS.stModbusEMSComm.stModbusReg11.eBatteryStatus := E_BATTERY_STATUS.TESTING; IF (GVL_SCADA.eRequestedControlMode <> _eBMSControlMode) AND (GVL_SCADA.xCanChangeControlMode) THEN _eBMSControlMode := GVL_SCADA.eRequestedControlMode; END_IF SM_CAPACITY_TEST(); E_BMS_CONTROL_MODE.BALANCING: _xAllComponentsToManualMode := FALSE; _xInSafetyCheckMode := FALSE; _xReleaseManualMode := FALSE; IF (GVL_SCADA.eRequestedControlMode <> _eBMSControlMode) AND (GVL_SCADA.xCanChangeControlMode) THEN _xStartBalancing := FALSE; _eBMSControlMode := GVL_SCADA.eRequestedControlMode; END_IF SM_BALANCING(); END_CASE GVL_SCADA.xCanChangeControlMode := _xCanChangeMode; GVL_SCADA.eCurrentControlMode := _eBMSControlMode; // Calculate current battery dc power GVL_SCADA.diCurrentBatteryPower := REAL_TO_DINT(_afbStrings[0].stInverterData.rActDCPower + _afbStrings[1].stInverterData.rActDCPower); _fbSafety(); // Reset automatic buttons IF GVL_SCADA.stAutomaticModeHMI.stStartAutoButton.xRequest THEN GVL_SCADA.stAutomaticModeHMI.stStartAutoButton.xRequest := FALSE; END_IF IF GVL_SCADA.stAutomaticModeHMI.stStopAutoButton.xRequest THEN GVL_SCADA.stAutomaticModeHMI.stStopAutoButton.xRequest := FALSE; END_IF // Reset alarm confirmation IF _xConfirmAlarms OR _rtHardwareResetButton.Q THEN _xConfirmAlarms := FALSE; END_IF]]> DINT_TO_REAL(GVL_CONFIG.diMinimumAbsPowerForEnable)) AND (NOT _afbStrings[_uiDebugCurrentString].xError) AND _afbStrings[_uiDebugCurrentString].xAllModulesInAutoMode THEN _iState := 5; _xCanChangeMode := FALSE; END_IF 5: // Check if power command is within limits IF _rAutoPowerRequest < DINT_TO_REAL(GVL_CONFIG.diMaxStringDischargePower) AND _rAutoPowerRequest > DINT_TO_REAL(GVL_CONFIG.diMaxStringChargingPower) THEN _xEnableString := TRUE; _xStartBalancing := FALSE; _iState := 10; ELSE // Set error bitmap flag GVL_MODBUS.stModbusEMSComm.stModbusReg11.lwErrorBitmap.0 := 1; // Goto error state _iState := 1000; END_IF 10: // Wait for string to be ready _fbStringReadyTimeout(IN := TRUE, PT := GVL_CONFIG.timStringReadyTimeout); IF _afbStrings[_uiDebugCurrentString].xReady AND (NOT _afbStrings[_uiDebugCurrentString].xError) THEN _fbStringReadyTimeout(IN := FALSE); _rPowerInverter := 0.0; _iState := 30; END_IF IF _afbStrings[_uiDebugCurrentString].xError OR _fbStringReadyTimeout.Q THEN _fbStringReadyTimeout(IN := FALSE); _xEnableString := FALSE; GVL_SCADA.stAutomaticModeHMI.diSetpointAutomatic := 0; _xCanChangeMode := TRUE; _iState := 45; END_IF IF (ABS(_rAutoPowerRequest) < DINT_TO_REAL(GVL_CONFIG.diMinimumAbsPowerForEnable)) THEN _fbStringReadyTimeout(IN := FALSE); _xEnableString := FALSE; _xCanChangeMode := TRUE; _iState := 45; END_IF 30: // String and inverter enabled // Set inverter power to modbus requested power _rPowerInverter := _rAutoPowerRequest;//DINT_TO_REAL(GVL_MODBUS.stModbusEMSComm.stModbusReg12.diSetpointActivePower); // Check if the battery should still be active IF (_rAutoPowerRequest = 0.0) THEN _xNoPowerRequested := TRUE; ELSE _xNoPowerRequested := FALSE; END_IF // Set battery status IF _rAutoPowerRequest > 0 THEN GVL_MODBUS.stModbusEMSComm.stModbusReg11.eChargeStatus := E_CHARGE_STATUS.DISCHARGING; ELSIF _rAutoPowerRequest < 0 THEN GVL_MODBUS.stModbusEMSComm.stModbusReg11.eChargeStatus := E_CHARGE_STATUS.CHARGING; ELSE GVL_MODBUS.stModbusEMSComm.stModbusReg11.eChargeStatus := E_CHARGE_STATUS.UNDEFINED; END_IF // Add small delay before shutdown by EMS is detected _tonBeginShutdown(IN := _xNoPowerRequested); // shutdown triggered from EMS IF _tonBeginShutdown.Q THEN _tonBeginShutdown(In := FALSE); // Set inverter to zero power _rPowerInverter := 0.0; // Start string shutdown _xEnableString := FALSE; GVL_MODBUS.stModbusEMSComm.stModbusReg11.eChargeStatus := E_CHARGE_STATUS.DISCHARGING; GVL_MODBUS.stModbusEMSComm.stModbusReg10.uiActiveParallelMembers := 0; _iState := 35; END_IF // Shutdown triggered by battery fully charged IF GVL_MODBUS.stModbusEMSComm.stModbusReg11.eChargeStatus = E_CHARGE_STATUS.CHARGING AND ((_afbStrings[_uiDebugCurrentString].stInverterData.rActDCVoltage >= GVL_CONFIG.rStringFullyChargedVoltage) OR _rHighestSegmentVoltage >= GVL_CONFIG.rMaximumUnitVoltage) THEN _tonBeginShutdown(In := FALSE); // Send message _fbBatteryFullMessage.Send(0); // Set inverter to zero power _rPowerInverter := 0.0; // Set local remote to zero power GVL_SCADA.stAutomaticModeHMI.diSetpointAutomatic := 0; // Start string shutdown _xEnableString := FALSE; // Change battery status GVL_MODBUS.stModbusEMSComm.stModbusReg11.eChargeStatus := E_CHARGE_STATUS.FULL; GVL_MODBUS.stModbusEMSComm.stModbusReg10.uiActiveParallelMembers := 0; _iState := 35; END_IF // Shutdown triggered by battery empty IF GVL_MODBUS.stModbusEMSComm.stModbusReg11.eChargeStatus = E_CHARGE_STATUS.DISCHARGING AND ((_afbStrings[_uiDebugCurrentString].stInverterData.rActDCVoltage <= GVL_CONFIG.rStringEmptyVoltage) OR _rSmallestSegmentVoltage <= GVL_CONFIG.rMinimumUnitVoltage) THEN _tonBeginShutdown(In := FALSE); // Send Message _fbBatteryEmptyMessage.Send(0); // Set local remote to zero power GVL_SCADA.stAutomaticModeHMI.diSetpointAutomatic := 0; // Set inverter to zero power _rPowerInverter := 0.0; // Start string shutdown _xEnableString := FALSE; // Change battery status GVL_MODBUS.stModbusEMSComm.stModbusReg11.eChargeStatus := E_CHARGE_STATUS.EMPTY; GVL_MODBUS.stModbusEMSComm.stModbusReg10.uiActiveParallelMembers := 0; _iState := 35; END_IF // Check for errors IF _afbStrings[_uiDebugCurrentString].xError THEN _xEnableString := FALSE; _tonBeginShutdown(In := FALSE); GVL_SCADA.stAutomaticModeHMI.diSetpointAutomatic := 0; _xCanChangeMode := TRUE; _iState := 45; END_IF 35: // Wait for string to be in shutdown discharge mode IF _afbStrings[_uiDebugCurrentString].xInShutdownDischargeMode THEN // Check if we are allowed to discharge during shutdown with inverter IF GVL_CONFIG.xShutdownDischargeWithInverter THEN _iState := 40; ELSE _rPowerInverter := 0.0; _xEnableString := FALSE; GVL_SCADA.stAutomaticModeHMI.diSetpointAutomatic := 0; _xCanChangeMode := TRUE; _iState := 45; END_IF END_IF // Check for errors IF _afbStrings[_uiDebugCurrentString].xError THEN _iState := 1000; END_IF 40: // Wait for inverter discharge done IF _afbStrings[_uiDebugCurrentString].xShutdownDischargeAllowed THEN _rPowerInverter := GVL_CONFIG.rAbsShutdownDischargePower; ELSE _rPowerInverter := 0.0; GVL_SCADA.stAutomaticModeHMI.diSetpointAutomatic := 0; _xEnableString := FALSE; _xCanChangeMode := TRUE; _iState := 45; END_IF // Check for errors IF _afbStrings[_uiDebugCurrentString].xError THEN _iState := 1000; END_IF // Restart if possible IF (ABS(_rAutoPowerRequest) > DINT_TO_REAL(GVL_CONFIG.diMinimumAbsPowerForEnable)) AND (NOT _afbStrings[_uiDebugCurrentString].xError) AND _afbStrings[_uiDebugCurrentString].xAllModulesInAutoMode THEN _iState := 5; END_IF 45: // Wait for shutdown of string to be done IF (NOT _afbStrings[_uiDebugCurrentString].xInShutdownDischargeMode) AND _afbStrings[_uiDebugCurrentString].xOff THEN GVL_MODBUS.stModbusEMSComm.stModbusReg11.eBatteryStatus := E_BATTERY_STATUS.OFF; GVL_MODBUS.stModbusEMSComm.stModbusReg11.eChargeStatus := E_CHARGE_STATUS.UNDEFINED; _iState := 0; END_IF // Restart if possible IF (ABS(_rAutoPowerRequest) > DINT_TO_REAL(GVL_CONFIG.diMinimumAbsPowerForEnable)) AND (NOT _afbStrings[_uiDebugCurrentString].xError) AND _afbStrings[_uiDebugCurrentString].xAllModulesInAutoMode THEN _iState := 5; END_IF // Check for errors IF _afbStrings[_uiDebugCurrentString].xError THEN _iState := 1000; END_IF 1000: // Error state _xEnableString := FALSE; _rPowerInverter := 0.0; GVL_MODBUS.stModbusEMSComm.stModbusReg11.eBatteryStatus := E_BATTERY_STATUS.ERROR; _iState := 1010; 1010: // Wait for reset from error state IF (_rAutoPowerRequest = 0.0) AND (NOT _afbStrings[_uiDebugCurrentString].xError) AND _xConfirmAlarms THEN // Reset modbus error register GVL_MODBUS.stModbusEMSComm.stModbusReg11.lwErrorBitmap := 0; // Reset modbus error flag GVL_MODBUS.stModbusEMSComm.stModbusReg11.eBatteryStatus := E_BATTERY_STATUS.OFF; // Goto init state _iState := 0; _xCanChangeMode := TRUE; END_IF END_CASE]]>