Added PreCharge Timer, CableCheck Timer to TimeRestrictions.java and added handling for those timers in EVCC package.

Minor bugfixing with regards to genChallenge in AuthorizationReq
This commit is contained in:
Marc Mültin 2017-08-23 11:26:34 +02:00
parent d933ba39ad
commit af1f3480e0
11 changed files with 159 additions and 18 deletions

View File

@ -36,6 +36,7 @@ import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.ACEVChargeParameterType;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.ChargingProfileType;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.DCEVChargeParameterType;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.DCEVErrorCodeType;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.DCEVPowerDeliveryParameterType;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.DCEVStatusType;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.EnergyTransferModeType;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.PMaxScheduleEntryType;
@ -319,4 +320,14 @@ public class DummyEVController implements IACEVController, IDCEVController {
public void setChargingLoopCounter(int chargingLoopCounter) {
this.chargingLoopCounter = chargingLoopCounter;
}
@Override
public DCEVPowerDeliveryParameterType getEVPowerDeliveryParameter() {
DCEVPowerDeliveryParameterType dcEvPowerDeliveryParameter = new DCEVPowerDeliveryParameterType();
dcEvPowerDeliveryParameter.setBulkChargingComplete(false);
dcEvPowerDeliveryParameter.setChargingComplete(false);
dcEvPowerDeliveryParameter.setDCEVStatus(getDCEVStatus());
return dcEvPowerDeliveryParameter;
}
}

View File

@ -26,6 +26,7 @@ package org.v2gclarity.risev2g.evcc.evController;
import javax.xml.bind.JAXBElement;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.DCEVChargeParameterType;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.DCEVPowerDeliveryParameterType;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.DCEVStatusType;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.PhysicalValueType;
@ -107,4 +108,11 @@ public interface IDCEVController extends IEVController {
* @return The estimated time given as a PhysicalValueType
*/
public PhysicalValueType getRemainingTimeToBulkSOC();
/**
* Returns the DC_EVPowerDeliverParameter
* @return The DC_EVPowerDeliverParameter
*/
public DCEVPowerDeliveryParameterType getEVPowerDeliveryParameter();
}

View File

@ -27,6 +27,9 @@ import java.security.KeyStore;
import java.util.Arrays;
import java.util.ListIterator;
import javax.xml.bind.JAXBElement;
import javax.xml.namespace.QName;
import org.v2gclarity.risev2g.evcc.evController.DummyEVController;
import org.v2gclarity.risev2g.evcc.evController.IACEVController;
import org.v2gclarity.risev2g.evcc.evController.IDCEVController;
@ -38,6 +41,7 @@ import org.v2gclarity.risev2g.shared.misc.State;
import org.v2gclarity.risev2g.shared.utils.ByteUtils;
import org.v2gclarity.risev2g.shared.utils.MiscUtils;
import org.v2gclarity.risev2g.shared.utils.SecurityUtils;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.ACEVSEStatusType;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.AuthorizationReqType;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.AuthorizationResType;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.BodyBaseType;
@ -53,7 +57,9 @@ import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.ChargingSessionType;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.ChargingStatusResType;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.CurrentDemandReqType;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.CurrentDemandResType;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.DCEVPowerDeliveryParameterType;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.EMAIDType;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.EVSENotificationType;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.EnergyTransferModeType;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.MessageHeaderType;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.MeteringReceiptResType;
@ -418,9 +424,16 @@ public abstract class ClientState extends State {
powerDeliveryReq.setChargeProgress(chargeProgress);
powerDeliveryReq.setSAScheduleTupleID(getCommSessionContext().getEvController().getChosenSAScheduleTupleID());
// Optionally set DC_EVPowerDeliveryParameter if in DC charging mode
// Set DC_EVPowerDeliveryParameter if in DC charging mode
if (getCommSessionContext().getRequestedEnergyTransferMode().toString().startsWith("DC")) {
/*
* The MessageHandler method getJAXBElement() cannot be used here because of the difference in the
* class name (DCEVPowerDeliveryParameter) and the name in the XSD (DC_EVPowerDeliveryParameter)
*/
JAXBElement jaxbDcEvPowerDeliveryParameter = new JAXBElement(new QName("urn:iso:15118:2:2013:MsgDataTypes", "DC_EVPowerDeliveryParameter"),
DCEVPowerDeliveryParameterType.class,
((IDCEVController) getCommSessionContext().getEvController()).getEVPowerDeliveryParameter());
powerDeliveryReq.setEVPowerDeliveryParameter(jaxbDcEvPowerDeliveryParameter);
}
return powerDeliveryReq;

View File

@ -23,16 +23,20 @@
*******************************************************************************/
package org.v2gclarity.risev2g.evcc.states;
import java.util.concurrent.TimeUnit;
import org.v2gclarity.risev2g.evcc.session.V2GCommunicationSessionEVCC;
import org.v2gclarity.risev2g.shared.enumerations.GlobalValues;
import org.v2gclarity.risev2g.shared.enumerations.V2GMessages;
import org.v2gclarity.risev2g.shared.messageHandling.ReactionToIncomingMessage;
import org.v2gclarity.risev2g.shared.messageHandling.TerminateSession;
import org.v2gclarity.risev2g.shared.misc.TimeRestrictions;
import org.v2gclarity.risev2g.shared.utils.SecurityUtils;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.AuthorizationReqType;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.AuthorizationResType;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.ChargeParameterDiscoveryReqType;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.EVSEProcessingType;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.PaymentOptionType;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.V2GMessage;
public class WaitForAuthorizationRes extends ClientState {
@ -49,6 +53,11 @@ public class WaitForAuthorizationRes extends ClientState {
(AuthorizationResType) v2gMessageRes.getBody().getBodyElement().getValue();
if (authorizationRes.getEVSEProcessing().equals(EVSEProcessingType.FINISHED)) {
getLogger().debug("EVSEProcessing was set to FINISHED");
getCommSessionContext().setOngoingTimer(0L);
getCommSessionContext().setOngoingTimerActive(false);
ChargeParameterDiscoveryReqType chargeParameterDiscoveryReq = getChargeParameterDiscoveryReq();
/*
@ -59,19 +68,40 @@ public class WaitForAuthorizationRes extends ClientState {
return getSendMessage(chargeParameterDiscoveryReq, V2GMessages.CHARGE_PARAMETER_DISCOVERY_RES);
} else {
// Set xml reference element
AuthorizationReqType authorizationReq = getAuthorizationReq(null);
getXMLSignatureRefElements().put(
authorizationReq.getId(),
SecurityUtils.generateDigest(getMessageHandler().getJaxbElement(authorizationReq)));
getLogger().debug("EVSEProcessing was set to ONGOING");
// Set signing private key
setSignaturePrivateKey(SecurityUtils.getPrivateKey(
SecurityUtils.getKeyStore(
GlobalValues.EVCC_KEYSTORE_FILEPATH.toString(),
GlobalValues.PASSPHRASE_FOR_CERTIFICATES_AND_KEYS.toString()),
GlobalValues.ALIAS_CONTRACT_CERTIFICATE.toString())
);
if (getCommSessionContext().isOngoingTimerActive()) {
long elapsedTime = System.nanoTime() - getCommSessionContext().getOngoingTimer();
long elapsedTimeInMs = TimeUnit.MILLISECONDS.convert(elapsedTime, TimeUnit.NANOSECONDS);
if (elapsedTimeInMs > TimeRestrictions.V2G_EVCC_ONGOING_TIMEOUT)
return new TerminateSession("Ongoing timer timed out for AuthorizationReq");
} else {
getCommSessionContext().setOngoingTimer(System.nanoTime());
getCommSessionContext().setOngoingTimerActive(true);
}
AuthorizationReqType authorizationReq = null;
if (getCommSessionContext().getSelectedPaymentOption().equals(PaymentOptionType.CONTRACT) &&
getCommSessionContext().isTlsConnection()) {
authorizationReq = getAuthorizationReq(getCommSessionContext().getSentGenChallenge());
// Set xml reference element
getXMLSignatureRefElements().put(
authorizationReq.getId(),
SecurityUtils.generateDigest(getMessageHandler().getJaxbElement(authorizationReq)));
// Set signing private key
setSignaturePrivateKey(SecurityUtils.getPrivateKey(
SecurityUtils.getKeyStore(
GlobalValues.EVCC_KEYSTORE_FILEPATH.toString(),
GlobalValues.PASSPHRASE_FOR_CERTIFICATES_AND_KEYS.toString()),
GlobalValues.ALIAS_CONTRACT_CERTIFICATE.toString())
);
} else {
authorizationReq = getAuthorizationReq(null);
}
return getSendMessage(authorizationReq, V2GMessages.AUTHORIZATION_RES);
}

View File

@ -23,11 +23,14 @@
*******************************************************************************/
package org.v2gclarity.risev2g.evcc.states;
import java.util.concurrent.TimeUnit;
import org.v2gclarity.risev2g.evcc.evController.IDCEVController;
import org.v2gclarity.risev2g.evcc.session.V2GCommunicationSessionEVCC;
import org.v2gclarity.risev2g.shared.enumerations.V2GMessages;
import org.v2gclarity.risev2g.shared.messageHandling.ReactionToIncomingMessage;
import org.v2gclarity.risev2g.shared.messageHandling.TerminateSession;
import org.v2gclarity.risev2g.shared.misc.TimeRestrictions;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.CableCheckResType;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.EVSEProcessingType;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.PreChargeReqType;
@ -56,10 +59,24 @@ public class WaitForCableCheckRes extends ClientState {
preChargeReq.setEVTargetCurrent(dcEvController.getTargetCurrent());
preChargeReq.setEVTargetVoltage(dcEvController.getTargetVoltage());
getCommSessionContext().setOngoingTimer(System.nanoTime());
getCommSessionContext().setOngoingTimerActive(true);
return getSendMessage(preChargeReq, V2GMessages.PRE_CHARGE_RES);
} else {
getLogger().debug("EVSEProcessing was set to ONGOING");
if (getCommSessionContext().isOngoingTimerActive()) {
long elapsedTime = System.nanoTime() - getCommSessionContext().getOngoingTimer();
long elapsedTimeInMs = TimeUnit.MILLISECONDS.convert(elapsedTime, TimeUnit.NANOSECONDS);
if (elapsedTimeInMs > TimeRestrictions.V2G_EVCC_CABLE_CHECK_TIMEOUT)
return new TerminateSession("CableCheck timer timed out for CableCheckReq");
} else {
getCommSessionContext().setOngoingTimer(System.nanoTime());
getCommSessionContext().setOngoingTimerActive(true);
}
return getSendMessage(getCableCheckReq(), V2GMessages.CABLE_CHECK_RES);
}
} else {

View File

@ -26,6 +26,7 @@ package org.v2gclarity.risev2g.evcc.states;
import java.security.cert.X509Certificate;
import java.util.HashMap;
import java.util.List;
import java.util.concurrent.TimeUnit;
import org.v2gclarity.risev2g.evcc.session.V2GCommunicationSessionEVCC;
import org.v2gclarity.risev2g.shared.enumerations.CPStates;
@ -33,6 +34,7 @@ import org.v2gclarity.risev2g.shared.enumerations.GlobalValues;
import org.v2gclarity.risev2g.shared.enumerations.V2GMessages;
import org.v2gclarity.risev2g.shared.messageHandling.ReactionToIncomingMessage;
import org.v2gclarity.risev2g.shared.messageHandling.TerminateSession;
import org.v2gclarity.risev2g.shared.misc.TimeRestrictions;
import org.v2gclarity.risev2g.shared.utils.SecurityUtils;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.ACEVSEChargeParameterType;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.ChargeParameterDiscoveryResType;
@ -60,8 +62,25 @@ public class WaitForChargeParameterDiscoveryRes extends ClientState {
if (chargeParameterDiscoveryRes.getEVSEProcessing().equals(EVSEProcessingType.ONGOING)) {
getLogger().debug("EVSEProcessing was set to ONGOING");
if (getCommSessionContext().isOngoingTimerActive()) {
long elapsedTime = System.nanoTime() - getCommSessionContext().getOngoingTimer();
long elapsedTimeInMs = TimeUnit.MILLISECONDS.convert(elapsedTime, TimeUnit.NANOSECONDS);
if (elapsedTimeInMs > TimeRestrictions.V2G_EVCC_ONGOING_TIMEOUT)
return new TerminateSession("Ongoing timer timed out for ChargeParameterDiscoveryReq");
} else {
getCommSessionContext().setOngoingTimer(System.nanoTime());
getCommSessionContext().setOngoingTimerActive(true);
}
return getSendMessage(getCommSessionContext().getChargeParameterDiscoveryReq(), V2GMessages.CHARGE_PARAMETER_DISCOVERY_RES);
} else {
getLogger().debug("EVSEProcessing was set to FINISHED");
getCommSessionContext().setOngoingTimer(0L);
getCommSessionContext().setOngoingTimerActive(false);
// Check for the EVSENotification
EVSENotificationType evseNotification = null;
@ -111,9 +130,13 @@ public class WaitForChargeParameterDiscoveryRes extends ClientState {
return getSendMessage(getPowerDeliveryReq(ChargeProgressType.START), V2GMessages.POWER_DELIVERY_RES);
} else if (getCommSessionContext().getRequestedEnergyTransferMode().toString().startsWith("DC")) {
// CP state C signaling BEFORE sending CableCheckReq message in DC
if (getCommSessionContext().getEvController().setCPState(CPStates.STATE_C))
if (getCommSessionContext().getEvController().setCPState(CPStates.STATE_C)) {
// Set timer for CableCheck
getCommSessionContext().setOngoingTimer(System.nanoTime());
getCommSessionContext().setOngoingTimerActive(true);
return getSendMessage(getCableCheckReq(), V2GMessages.CABLE_CHECK_RES);
else
} else
return new TerminateSession("CP state C not ready (current state = " +
getCommSessionContext().getEvController().getCPState() +
")");

View File

@ -54,6 +54,9 @@ public class WaitForPaymentDetailsRes extends ClientState {
if (paymentDetailsRes.getGenChallenge() == null) {
return new TerminateSession("GenChallenge not provided in PaymentDetailsRes");
} else {
// Save the sent genChallenge for the state WaitForAuthorizationRes if EVSEProcessing is set to ONGOING
getCommSessionContext().setSentGenChallenge(paymentDetailsRes.getGenChallenge());
AuthorizationReqType authorizationReq = getAuthorizationReq(paymentDetailsRes.getGenChallenge());
// Set xml reference element

View File

@ -23,11 +23,16 @@
*******************************************************************************/
package org.v2gclarity.risev2g.evcc.states;
import java.util.concurrent.TimeUnit;
import org.v2gclarity.risev2g.evcc.evController.IDCEVController;
import org.v2gclarity.risev2g.evcc.session.V2GCommunicationSessionEVCC;
import org.v2gclarity.risev2g.shared.enumerations.V2GMessages;
import org.v2gclarity.risev2g.shared.messageHandling.ReactionToIncomingMessage;
import org.v2gclarity.risev2g.shared.messageHandling.TerminateSession;
import org.v2gclarity.risev2g.shared.misc.TimeRestrictions;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.ChargeProgressType;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.PreChargeReqType;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.PreChargeResType;
import org.v2gclarity.risev2g.shared.v2gMessages.msgDef.V2GMessage;
@ -44,9 +49,32 @@ public class WaitForPreChargeRes extends ClientState {
PreChargeResType preChargeRes =
(PreChargeResType) v2gMessageRes.getBody().getBodyElement().getValue();
// TODO how to react to DC_EVSEStatus and EVSEPresentVoltage?
// TODO how to react to DC_EVSEStatus
return getSendMessage(getPowerDeliveryReq(ChargeProgressType.START), V2GMessages.POWER_DELIVERY_RES);
IDCEVController dcEvController = (IDCEVController) getCommSessionContext().getEvController();
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) {
getCommSessionContext().setOngoingTimerActive(false);
getCommSessionContext().setOngoingTimer(0L);
return getSendMessage(getPowerDeliveryReq(ChargeProgressType.START), V2GMessages.POWER_DELIVERY_RES);
} else {
long elapsedTime = System.nanoTime() - getCommSessionContext().getOngoingTimer();
long elapsedTimeInMs = TimeUnit.MILLISECONDS.convert(elapsedTime, TimeUnit.NANOSECONDS);
if (elapsedTimeInMs > TimeRestrictions.V2G_EVCC_PRE_CHARGE_TIMEOUT)
return new TerminateSession("PreCharge timer timed out for PreChargeReq");
else {
PreChargeReqType preChargeReq = new PreChargeReqType();
preChargeReq.setDCEVStatus(dcEvController.getDCEVStatus());
preChargeReq.setEVTargetCurrent(dcEvController.getTargetCurrent());
preChargeReq.setEVTargetVoltage(dcEvController.getTargetVoltage());
return getSendMessage(preChargeReq, V2GMessages.PRE_CHARGE_RES);
}
}
} else {
return new TerminateSession("Incoming message raised an error");
}

View File

@ -58,6 +58,7 @@ public abstract class StatefulTransportLayerClient extends Observable implement
setClientPort(MiscUtils.getRandomPortNumber());
setClientAddress(MiscUtils.getLinkLocalAddress());
setV2gTPHeader(new byte[8]);
setTimeout(2000); // Needed for the supportedAppProtocol timeout
}
protected boolean processIncomingMessage() throws IOException {

View File

@ -38,6 +38,9 @@ public class TimeRestrictions {
private static Logger logger = LogManager.getLogger(TimeRestrictions.class.getSimpleName());
public static final int V2G_EVCC_SEQUENCE_PERFORMANCE_TIME = 40000;
public static final int V2G_SECC_SEQUENCE_TIMEOUT = 60000;
public static final int V2G_EVCC_ONGOING_TIMEOUT = 60000;
public static final int V2G_EVCC_CABLE_CHECK_TIMEOUT = 40000;
public static final int V2G_EVCC_PRE_CHARGE_TIMEOUT = 7000;
/**
* SDP client shall wait for SECC Discovery Response message for _at least_ 250 ms (see [V2G2-159])

View File

@ -826,6 +826,8 @@ public final class SecurityUtils {
privateKey = (ECPrivateKey) keyStore.getKey(
alias,
GlobalValues.PASSPHRASE_FOR_CERTIFICATES_AND_KEYS.toString().toCharArray());
getLogger().debug("Private key used for creating signature: " + ByteUtils.toHexString(privateKey.getEncoded()));
} catch (KeyStoreException | UnrecoverableKeyException | NoSuchAlgorithmException e) {
getLogger().error("The private key from keystore with alias '" + alias +
"' could not be retrieved (" + e.getClass().getSimpleName() + ")", e);
@ -981,6 +983,8 @@ public final class SecurityUtils {
contractCert.getSubjectX500Principal().getName() + "' saved. " +
"Valid until " + contractCert.getNotAfter()
);
getLogger().debug("Decrypted private key belonging to contract certificate saved. Key bytes: " +
ByteUtils.toHexString(contractCertPrivateKey.getEncoded()));
} else {
getLogger().error("Private key for contract certificate is not valid");
return false;