Ver Fonte

- TargetCurrent in DummyEVController, used in PreChargeReq message, was set to 2A to comply to IEC 61851-23
- The necessary change from State C to State B during a renegotiation in DC charging is now correctly implemented
- Added the EV setting "voltage.accuracy" to allow for a percentage of deviation from the target current in PreCharge

Marc Mültin há 7 anos atrás
pai
commit
cba5e041f6

+ 9 - 0
RISE-V2G-EVCC/EVCCConfig.properties

@@ -126,3 +126,12 @@ signature.verification.showlog = true
 # - open_exi
 # If no correct value is provided here, 'exificient' will be used
 exi.codec = exificient
+
+
+# Voltage accuracy
+#----------
+#
+# Used for the PreCharge target voltage. The present voltage indicated by the charging station in PreChargeRes can deviate from the present voltage 
+# set in PreChargeReq by an EV-specific deviation factor. This value is given in percent.
+# Example: voltage.accuracy = 10 means: present voltage may deviate from target voltage by 10 percent in order to successfully stop PreCharge
+voltage.accuracy = 5

+ 1 - 1
RISE-V2G-EVCC/src/main/java/com/v2gclarity/risev2g/evcc/evController/DummyEVController.java

@@ -228,7 +228,7 @@ public class DummyEVController implements IACEVController, IDCEVController {
 		PhysicalValueType targetCurrent = new PhysicalValueType();
 		targetCurrent.setMultiplier(new Byte("0"));
 		targetCurrent.setUnit(UnitSymbolType.A);
-		targetCurrent.setValue((short) 32); 
+		targetCurrent.setValue((short) 2); // according to IEC 61851-23, this value should be limited to 2A as it seems (see https://github.com/V2GClarity/RISE-V2G/issues/20)
 		
 		return targetCurrent;
 	}

+ 1 - 0
RISE-V2G-EVCC/src/main/java/com/v2gclarity/risev2g/evcc/states/WaitForCurrentDemandRes.java

@@ -92,6 +92,7 @@ public class WaitForCurrentDemandRes extends ClientState {
 										  V2GMessages.POWER_DELIVERY_RES,
 										  " (ChargeProgress = STOP_CHARGING)");
 				case RE_NEGOTIATION:
+					getCommSessionContext().setRenegotiationRequested(true);
 					return getSendMessage(getPowerDeliveryReq(ChargeProgressType.RENEGOTIATE), 
 										  V2GMessages.POWER_DELIVERY_RES,
 							  			  " (ChargeProgress = RE_NEGOTIATION)");

+ 5 - 0
RISE-V2G-EVCC/src/main/java/com/v2gclarity/risev2g/evcc/states/WaitForPowerDeliveryRes.java

@@ -61,6 +61,11 @@ public class WaitForPowerDeliveryRes extends ClientState {
 			 */
 			
 			if (getCommSessionContext().isRenegotiationRequested()) {
+				// In DC charging, we need to switch to state B during renegotiation because we need to go through CableCheckReq and PreChargeReq again for which state B is required
+				if (getCommSessionContext().getRequestedEnergyTransferMode().toString().startsWith("DC")) {
+					getCommSessionContext().setChangeToState(CPStates.STATE_B);
+				}
+				
 				getCommSessionContext().setRenegotiationRequested(false);
 				return getSendMessage(getChargeParameterDiscoveryReq(), V2GMessages.CHARGE_PARAMETER_DISCOVERY_RES);
 			} else if (getCommSessionContext().isStopChargingRequested()) {

+ 6 - 1
RISE-V2G-EVCC/src/main/java/com/v2gclarity/risev2g/evcc/states/WaitForPreChargeRes.java

@@ -31,6 +31,7 @@ import com.v2gclarity.risev2g.shared.enumerations.V2GMessages;
 import com.v2gclarity.risev2g.shared.messageHandling.ReactionToIncomingMessage;
 import com.v2gclarity.risev2g.shared.messageHandling.TerminateSession;
 import com.v2gclarity.risev2g.shared.misc.TimeRestrictions;
+import com.v2gclarity.risev2g.shared.utils.MiscUtils;
 import com.v2gclarity.risev2g.shared.v2gMessages.msgDef.ChargeProgressType;
 import com.v2gclarity.risev2g.shared.v2gMessages.msgDef.PreChargeReqType;
 import com.v2gclarity.risev2g.shared.v2gMessages.msgDef.PreChargeResType;
@@ -55,7 +56,10 @@ public class WaitForPreChargeRes extends ClientState {
 			double targetVoltage = dcEvController.getTargetVoltage().getValue() * Math.pow(10, dcEvController.getTargetVoltage().getMultiplier());
 			double presentVoltage = preChargeRes.getEVSEPresentVoltage().getValue() * Math.pow(10, preChargeRes.getEVSEPresentVoltage().getMultiplier());
 					
-			if (targetVoltage == presentVoltage) {
+			// Each EV has an EV-specific allowed deviation when measuring a target voltage
+			int voltageAccuracy = (int) MiscUtils.getPropertyValue("voltage.accuracy");
+			
+			if (presentVoltage >= targetVoltage * (1 - voltageAccuracy / 100) && presentVoltage <= targetVoltage * (1 + voltageAccuracy / 100)) {
 				getCommSessionContext().setOngoingTimerActive(false);
 				getCommSessionContext().setOngoingTimer(0L);
 				
@@ -67,6 +71,7 @@ public class WaitForPreChargeRes extends ClientState {
 				if (elapsedTimeInMs > TimeRestrictions.V2G_EVCC_PRE_CHARGE_TIMEOUT) 
 					return new TerminateSession("PreCharge timer timed out for PreChargeReq");
 				else {
+					getLogger().debug("Target voltage of " + targetVoltage + " V not yet reached. Present voltage at EVSE is " + presentVoltage);
 					PreChargeReqType preChargeReq = new PreChargeReqType();
 					preChargeReq.setDCEVStatus(dcEvController.getDCEVStatus());
 					preChargeReq.setEVTargetCurrent(dcEvController.getTargetCurrent());

+ 1 - 1
RISE-V2G-EVCC/src/main/java/com/v2gclarity/risev2g/evcc/states/WaitForServiceDiscoveryRes.java

@@ -73,7 +73,7 @@ public class WaitForServiceDiscoveryRes extends ClientState {
 					getCommSessionContext().getOfferedServices().getService().add(serviceDiscoveryRes.getChargeService());
 					addSelectedService(1, null); // Assumption: a charge service is always used
 				} else {
-					return new TerminateSession("Offered EnergyTransferModes not compatible with the requested one");
+					return new TerminateSession("Offered EnergyTransferModes not compatible with the requested one, which is " + requestedEnergyTransferMode.toString());
 				}
 			} else return new TerminateSession("No charge service available");
 			

+ 3 - 2
RISE-V2G-SECC/src/main/java/com/v2gclarity/risev2g/secc/backend/DummyBackendInterface.java

@@ -68,7 +68,7 @@ public class DummyBackendInterface implements IBackendInterface {
 		 */
 		ECPrivateKey privateKey = SecurityUtils.getPrivateKey("./moSubCA2.pkcs8.der");
 		if (privateKey == null) 
-			getLogger().error("No private key available from MO Sub-CA 2 PKCS#8 file");
+			getLogger().warn("No private key available from MO Sub-CA 2 PKCS#8 file. Signing a SalesTariff will therefore not be possible");
 		else
 			setMoSubCA2PrivateKey(privateKey);
 		
@@ -152,7 +152,7 @@ public class DummyBackendInterface implements IBackendInterface {
 		SAScheduleTupleType saScheduleTuple = new SAScheduleTupleType();
 		saScheduleTuple.setSAScheduleTupleID((short) 1); 
 		saScheduleTuple.setPMaxSchedule(pMaxSchedule);
-		saScheduleTuple.setSalesTariff(salesTariff);
+		saScheduleTuple.setSalesTariff(salesTariff); 
 		
 		SAScheduleListType saScheduleList = new SAScheduleListType();
 		saScheduleList.getSAScheduleTuple().add(saScheduleTuple);
@@ -235,6 +235,7 @@ public class DummyBackendInterface implements IBackendInterface {
 		 */
 		ArrayList<EMAIDType> authorizedEMAIDs = new ArrayList<EMAIDType>();
 		
+		// This is a list of EMAIDs used for testing purposes, like a whitelist 
 		EMAIDType authorizedEMAID1 = new EMAIDType();
 		authorizedEMAID1.setId("id1");
 		authorizedEMAID1.setValue("DE1ABCD2EF357A");

+ 8 - 0
RISE-V2G-SECC/src/main/java/com/v2gclarity/risev2g/secc/backend/IBackendInterface.java

@@ -51,6 +51,8 @@ public interface IBackendInterface {
 	 * Provides a certificate chain coming from a secondary actor with the leaf certificate being 
 	 * the contract certificate and possible intermediate certificates (Sub-CAs) included.
 	 * 
+	 * This interface is to be used for the CertificateUpdate
+	 * 
 	 * @param oldContractCertificateChain The to-be-updated contract certificate chain
 	 * @return Certificate chain for contract certificate
 	 */
@@ -61,6 +63,8 @@ public interface IBackendInterface {
 	 * Provides a certificate chain coming from a secondary actor with the leaf certificate being 
 	 * the contract certificate and possible intermediate certificates (Sub-CAs) included.
 	 * 
+	 * This interface is to be used for the CertificateInstallation
+	 * 
 	 * @param oemProvisioningCert The OEM provisioning certificate
 	 * @return Certificate chain for contract certificate
 	 */
@@ -69,6 +73,7 @@ public interface IBackendInterface {
 	
 	/**
 	 * Provides the private key belonging to the contract certificate.
+	 * 
 	 * @return PrivateKey of the contract certificate
 	 */
 	public ECPrivateKey getContractCertificatePrivateKey();
@@ -77,6 +82,7 @@ public interface IBackendInterface {
 	/**
 	 * Provides a certificate chain coming from a secondary actor with the leaf certificate being 
 	 * the provisioning certificate and possible intermediate certificates (sub CAs) included.
+	 * 
 	 * @return Certificate chain for provisioning certificate
 	 */
 	public CertificateChainType getCPSCertificateChain();
@@ -84,6 +90,7 @@ public interface IBackendInterface {
 	
 	/**
 	 * Provides the private key belonging to the SA provisioning certificate.
+	 * 
 	 * @return PrivateKey of the SA provisioning certificate
 	 */
 	public ECPrivateKey getCPSLeafPrivateKey();
@@ -91,6 +98,7 @@ public interface IBackendInterface {
 	
 	/**
 	 * Provides the private key belonging to the MO Sub-CA 2 certificate (signature of SalesTariff).
+	 * 
 	 * @return PrivateKey of the MO Sub-CA 2 certificate
 	 */
 	public ECPrivateKey getMOSubCA2PrivateKey();

+ 0 - 5
RISE-V2G-SECC/src/main/java/com/v2gclarity/risev2g/secc/evseController/DummyACEVSEController.java

@@ -86,11 +86,6 @@ public class DummyACEVSEController implements IACEVSEController {
 	}
 
 	
-	public V2GCommunicationSessionSECC getCommSessionContext() {
-		return commSessionContext;
-	}
-
-	
 	public void setCommSessionContext(V2GCommunicationSessionSECC commSessionContext) {
 		this.commSessionContext = commSessionContext;
 	}

+ 6 - 2
RISE-V2G-SECC/src/main/java/com/v2gclarity/risev2g/secc/states/WaitForPowerDeliveryReq.java

@@ -114,6 +114,9 @@ public class WaitForPowerDeliveryReq extends ServerState {
 	public boolean isResponseCodeOK(PowerDeliveryReqType powerDeliveryReq) {
 		SAScheduleTupleType chosenSASchedule = getChosenSASCheduleTuple(powerDeliveryReq.getSAScheduleTupleID());
 		
+		// This debug message is helpful to determine why the EV might not send a ChargingProfile (parameter is optional and should only be left out if ChargeProgress is set to Stop)
+		getLogger().debug("ChargeProgress is set to " + powerDeliveryReq.getChargeProgress());
+		
 		if (powerDeliveryReq.getChargeProgress().equals(ChargeProgressType.RENEGOTIATE) && 
 				!getCommSessionContext().isChargeProgressStarted()) {
 				getLogger().error("EVCC wants to renegotiate, but charge progress has not started yet (no "
@@ -171,7 +174,8 @@ public class WaitForPowerDeliveryReq extends ServerState {
 	
 	
 	protected void setEVSEStatus(PowerDeliveryResType powerDeliveryRes) {
-		if (getCommSessionContext().getRequestedEnergyTransferMode().toString().startsWith("AC")) {
+		// In case the SECC received a PowerDeliveryReq before a PaymentServiceSelectionReq, the field requestedEnergyTransferMode will be null. So we need to check for it.
+		if (getCommSessionContext().getRequestedEnergyTransferMode() != null && getCommSessionContext().getRequestedEnergyTransferMode().toString().startsWith("AC")) {
 			/*
 			 * The MiscUtils method getJAXBElement() cannot be used here because of the difference in the
 			 * class name (ACEVSEStatus) and the name in the XSD (AC_EVSEStatus)
@@ -180,7 +184,7 @@ public class WaitForPowerDeliveryReq extends ServerState {
 					ACEVSEStatusType.class, 
 					getCommSessionContext().getACEvseController().getACEVSEStatus(EVSENotificationType.NONE));
 			powerDeliveryRes.setEVSEStatus(jaxbEVSEStatus);
-		} else if (getCommSessionContext().getRequestedEnergyTransferMode().toString().startsWith("DC")) {
+		} else if (getCommSessionContext().getRequestedEnergyTransferMode() != null && getCommSessionContext().getRequestedEnergyTransferMode().toString().startsWith("DC")) {
 			/*
 			 * The MiscUtils method getJAXBElement() cannot be used here because of the difference in the
 			 * class name (DCEVSEStatus) and the name in the XSD (DC_EVSEStatus)

+ 1 - 1
RISE-V2G-Shared/src/main/java/com/v2gclarity/risev2g/shared/enumerations/GlobalValues.java

@@ -71,7 +71,7 @@ public enum GlobalValues {
 	 */
 	V2GTP_HEADER_MAX_PAYLOAD_LENGTH((long) Integer.MAX_VALUE * 2, GlobalTypes.PAYLOAD_LENGTH),
 	
-	// Protocol versions (1 = IS compliant), see Table 9
+	// Protocol version of V2GTP messages (1 = IS compliant), see Table 9
 	V2GTP_VERSION_1_IS(ByteUtils.toByteFromHexString("01"), GlobalTypes.PROTOCOL_VERSION),
 	
 	// Schema information

+ 0 - 15
RISE-V2G-Shared/src/main/java/com/v2gclarity/risev2g/shared/misc/TimeRestrictions.java

@@ -27,12 +27,6 @@ import org.apache.logging.log4j.LogManager;
 import org.apache.logging.log4j.Logger;
 import com.v2gclarity.risev2g.shared.enumerations.V2GMessages;
 
-/**
- * All time restrictions are given as millisecond values. 
- * 
- * @author Marc
- *
- */
 public class TimeRestrictions {
 	
 	private static Logger logger = LogManager.getLogger(TimeRestrictions.class.getSimpleName());
@@ -59,15 +53,6 @@ public class TimeRestrictions {
 	 */
 	public static final int V2G_EVCC_COMMUNICATION_SETUP_TIMEOUT = 20000;
 	
-	/**
-	 * Timeout for retrieving a response from the ProtoTCPClient after having sent a request
-	 */
-	public static final int PROTO_TCP_CLIENT_RESPONSE_TIMEOUT = 30000;
-	
-	/**
-	 * Threshold time in seconds for sending the EV controller to sleep
-	 */
-	public static final int STAY_AWAKE_THRESHOLD = 125;
 	
 	public static int getV2gEvccMsgTimeout(V2GMessages messageType) {
 		switch(messageType) {

+ 20 - 8
RISE-V2G-Shared/src/main/java/com/v2gclarity/risev2g/shared/utils/MiscUtils.java

@@ -95,6 +95,16 @@ public final class MiscUtils {
 	}
 	
 	
+	/**
+	 * Is used by the UDP client as well as by the TCP/TLS server whose ports may be in the range
+	 * of 49152 and 65535.
+	 * @return A port number given as an integer value.
+	 */
+	public static int getRandomPortNumber() {
+		return (int) Math.round(Math.random() * (65535-49152)) + 49152;
+	}
+	
+	
 	public static byte[] getMacAddress() {
 		String networkInterfaceConfig = getPropertyValue("network.interface").toString();
 		NetworkInterface nif = null;
@@ -114,14 +124,6 @@ public final class MiscUtils {
 		return macAddress;
 	}
 	
-	/**
-	 * Is used by the UDP client as well as by the TCP/TLS server whose ports may be in the range
-	 * of 49152 and 65535.
-	 * @return A port number given as an integer value.
-	 */
-	public static int getRandomPortNumber() {
-		return (int) Math.round(Math.random() * (65535-49152)) + 49152;
-	}
 	
 	/**
 	 * This is a more sophisticated method compared to the getProperty(String propertyName) method
@@ -246,6 +248,16 @@ public final class MiscUtils {
 			if (propertyValue.equals("open_exi")) returnValue = "open_exi";
 			else returnValue = "exificient";
 			break;
+		case "voltage.accuracy": // EV property
+			try {
+				returnValue = Integer.parseInt(propertyValue);
+			} catch (NumberFormatException e) {
+				getLogger().warn("Voltage accuracy '" + propertyValue + "' not supported. " +
+							     "Setting default value to 5.", e);
+				getV2gEntityConfig().setProperty("voltage.accuracy", "5");
+				returnValue = 5;
+			}
+			break;
 		default:
 			getLogger().error("No property with name '" + propertyName + "' found");
 		}