, eState=> ); IF _xFirstCycle THEN _xFirstCycle := FALSE; _fbString.Name := 'String 1'; END_IF // Dely release of errors during PLC startup phase _tonStartupDelay(IN := TRUE); // Ack alarms from HMI _xConfirmAlarms := GVL_SCADA.stAckAlarmsButton.xRequest; IF GVL_SCADA.stAckAlarmsButton.xRequest THEN GVL_SCADA.stAckAlarmsButton.xRequest := FALSE; END_IF // Call string 1 _fbString( xEnable := _xEnableString, stHMIInterface:= GVL_SCADA.stHMIInterface, xEmergencyStopOk:= _xEmergencyStopOk, xReleaseErrors:= _xReleaseErrors AND _tonStartupDelay.Q, xReleaseLimitErrors:= _xReleaseLimitsErrors AND _tonStartupDelay.Q, xReleaseManualMode := _xReleaseManualMode, xConfirmAlarms:= _xConfirmAlarms); // HMI Feedback GVL_SCADA.stHMIInterface.rVoltage := _fbString.rCurrentVoltage; IF _fbString.eStatus = E_COMPONENT_STATUS.ON THEN IF _iState = 30 AND _rPowerInverter > 0 THEN GVL_SCADA.stHMIInterface.eStatus := E_COMPONENT_STATUS.DISCHARGING; ELSIF _iState = 30 AND _rPowerInverter < 0 THEN GVL_SCADA.stHMIInterface.eStatus := E_COMPONENT_STATUS.CHARGING; ELSE GVL_SCADA.stHMIInterface.eStatus :=_fbString.eStatus; END_IF ELSE GVL_SCADA.stHMIInterface.eStatus :=_fbString.eStatus; END_IF // DEACTIVATED FOR DEBUG REASONS !!! // Call inverter //_fbInverter( // sInverterIPAddr:= GVL_CONFIG.sInverterIp, // xEnable:= _xEnableInverter, // rPower:= _rPowerInverter, // xReset:= _xConfirmAlarms, // rMaxBattPower:= DINT_TO_REAL(GVL_CONFIG.diMaxStringDischargePower), // stCurrentValues => _stInverterData); // =============================== // 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=> ); // =============================== // State machine // =============================== CASE _eBMSControlMode OF E_BMS_CONTROL_MODE.AUTO_REMOTE: SM_AUTO_REMOTE(); E_BMS_CONTROL_MODE.AUTO_LOCAL: GVL_MODBUS.stModbusEMSComm.stModbusReg11.eBatteryStatus := E_BATTERY_STATUS.TESTING; SM_AUTO_LOCAL(); E_BMS_CONTROL_MODE.MANUAL: GVL_MODBUS.stModbusEMSComm.stModbusReg11.eBatteryStatus := E_BATTERY_STATUS.MAINTENANCE; SM_MANUAL(); E_BMS_CONTROL_MODE.SAFETY_CHECK: GVL_MODBUS.stModbusEMSComm.stModbusReg11.eBatteryStatus := E_BATTERY_STATUS.MAINTENANCE; SM_SAFETY_CHECK(); E_BMS_CONTROL_MODE.CAPACITY_TEST: GVL_MODBUS.stModbusEMSComm.stModbusReg11.eBatteryStatus := E_BATTERY_STATUS.TESTING; SM_CAPACITY_TEST(); END_CASE // Reset alarm confirmation IF _xConfirmAlarms THEN _xConfirmAlarms := FALSE; END_IF]]> GVL_CONFIG.diMinimumAbsPowerForEnable) AND _fbString.xSafetyIntlksOk AND (NOT _fbString.xError) THEN _iState := 5; END_IF 5: // Check if power command is within limits IF GVL_MODBUS.stModbusEMSComm.stModbusReg12.diSetpointActivePower < GVL_CONFIG.diMaxStringDischargePower AND GVL_MODBUS.stModbusEMSComm.stModbusReg12.diSetpointActivePower > GVL_CONFIG.diMaxStringChargingPower THEN _xEnableString := TRUE; _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 IF _fbString.xReady AND (NOT _fbString.xError) THEN _iState := 20; END_IF 20: // Start main inverter with zero power _rPowerInverter := 0.0; _xEnableInverter := TRUE; _iState := 25; 25: // Wait for inverter to be ready IF _fbInverter.xActive AND (NOT _fbInverter.xError) THEN // Set battery status for modbus GVL_MODBUS.stModbusEMSComm.stModbusReg11.eBatteryStatus := E_BATTERY_STATUS.ACTIVE; GVL_MODBUS.stModbusEMSComm.stModbusReg10.uiActiveParallelMembers := 1; _iState := 30; END_IF // Check for errors IF _fbString.xError OR _fbInverter.xError THEN _iState := 1000; END_IF 30: // String and inverter enabled // Set inverter power to modbus requested power _rPowerInverter := DINT_TO_REAL(GVL_MODBUS.stModbusEMSComm.stModbusReg12.diSetpointActivePower);//DINT_TO_REAL(GVL_MODBUS.stModbusEMSComm.stModbusReg12.diSetpointActivePower); // Check if the battery should still be active IF (GVL_MODBUS.stModbusEMSComm.stModbusReg12.diSetpointActivePower = 0) THEN _xNoPowerRequested := TRUE; ELSE _xNoPowerRequested := FALSE; END_IF // Set battery status IF GVL_MODBUS.stModbusEMSComm.stModbusReg12.diSetpointActivePower > 0 THEN GVL_MODBUS.stModbusEMSComm.stModbusReg11.eChargeStatus := E_CHARGE_STATUS.DISCHARGING; ELSIF GVL_MODBUS.stModbusEMSComm.stModbusReg12.diSetpointActivePower < 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.UNDEFINED; 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 (_fbString.rCurrentVoltage >= GVL_CONFIG.rStringFullyChargedVoltage) THEN _tonBeginShutdown(In := FALSE); // Set inverter to zero power _rPowerInverter := 0.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 (_fbString.rCurrentVoltage <= GVL_CONFIG.rStringEmptyVoltage) THEN _tonBeginShutdown(In := FALSE); // 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 _fbString.xError OR _fbInverter.xError THEN _tonBeginShutdown(In := FALSE); _iState := 1000; END_IF 35: // Wait for string to be in shutdown discharge mode IF _fbString.xInShutdownDischargeMode THEN // Check if we are allowed to discharge during shutdown with inverter IF GVL_CONFIG.xShutdownDischargeWithInverter THEN _iState := 40; ELSE _rPowerInverter := 0.0; _xEnableInverter := FALSE; _iState := 45; END_IF END_IF // Check for errors IF _fbString.xError OR _fbInverter.xError THEN _iState := 1000; END_IF 40: // Wait for inverter discharge done IF _fbString.xShutdownDischargeAllowed THEN _rPowerInverter := GVL_CONFIG.rAbsShutdownDischargePower; ELSE _rPowerInverter := 0.0; _xEnableInverter := FALSE; _iState := 45; END_IF // Check for errors IF _fbString.xError OR _fbInverter.xError THEN _iState := 1000; END_IF 45: // Wait for shutdown of string to be done IF (NOT _fbString.xInShutdownDischargeMode) AND _fbString.xOff THEN GVL_MODBUS.stModbusEMSComm.stModbusReg11.eBatteryStatus := E_BATTERY_STATUS.OFF; _iState := 0; END_IF // Check for errors IF _fbString.xError OR _fbInverter.xError THEN _iState := 1000; END_IF 1000: // Error state _xEnableString := FALSE; _xEnableInverter := FALSE; _rPowerInverter := 0.0; GVL_MODBUS.stModbusEMSComm.stModbusReg11.eBatteryStatus := E_BATTERY_STATUS.ERROR; _iState := 1010; 1010: // Wait for reset from error state IF (GVL_MODBUS.stModbusEMSComm.stModbusReg12.diSetpointActivePower = 0) AND (NOT _fbString.xError) AND (NOT _fbInverter.xError) 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; END_IF END_CASE]]>