RISE-V2G/RISE-V2G-EVCC/src/main/java/com/v2gclarity/risev2g/evcc/session/V2GCommunicationSessionEVCC...

512 lines
19 KiB
Java

/*******************************************************************************
* The MIT License (MIT)
*
* Copyright 2017 Dr.-Ing. Marc Mültin (V2G Clarity)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*******************************************************************************/
package com.v2gclarity.risev2g.evcc.session;
import java.util.ArrayList;
import java.util.List;
import java.util.Observable;
import java.util.Observer;
import com.v2gclarity.risev2g.evcc.evController.DummyEVController;
import com.v2gclarity.risev2g.evcc.evController.IEVController;
import com.v2gclarity.risev2g.evcc.states.WaitForAuthorizationRes;
import com.v2gclarity.risev2g.evcc.states.WaitForCableCheckRes;
import com.v2gclarity.risev2g.evcc.states.WaitForCertificateInstallationRes;
import com.v2gclarity.risev2g.evcc.states.WaitForCertificateUpdateRes;
import com.v2gclarity.risev2g.evcc.states.WaitForChargeParameterDiscoveryRes;
import com.v2gclarity.risev2g.evcc.states.WaitForChargingStatusRes;
import com.v2gclarity.risev2g.evcc.states.WaitForCurrentDemandRes;
import com.v2gclarity.risev2g.evcc.states.WaitForMeteringReceiptRes;
import com.v2gclarity.risev2g.evcc.states.WaitForPaymentDetailsRes;
import com.v2gclarity.risev2g.evcc.states.WaitForPaymentServiceSelectionRes;
import com.v2gclarity.risev2g.evcc.states.WaitForPowerDeliveryRes;
import com.v2gclarity.risev2g.evcc.states.WaitForPreChargeRes;
import com.v2gclarity.risev2g.evcc.states.WaitForServiceDetailRes;
import com.v2gclarity.risev2g.evcc.states.WaitForServiceDiscoveryRes;
import com.v2gclarity.risev2g.evcc.states.WaitForSessionSetupRes;
import com.v2gclarity.risev2g.evcc.states.WaitForSessionStopRes;
import com.v2gclarity.risev2g.evcc.states.WaitForSupportedAppProtocolRes;
import com.v2gclarity.risev2g.evcc.states.WaitForWeldingDetectionRes;
import com.v2gclarity.risev2g.evcc.transportLayer.StatefulTransportLayerClient;
import com.v2gclarity.risev2g.evcc.transportLayer.TCPClient;
import com.v2gclarity.risev2g.evcc.transportLayer.TLSClient;
import com.v2gclarity.risev2g.shared.enumerations.CPStates;
import com.v2gclarity.risev2g.shared.enumerations.GlobalValues;
import com.v2gclarity.risev2g.shared.enumerations.V2GMessages;
import com.v2gclarity.risev2g.shared.messageHandling.ChangeProcessingState;
import com.v2gclarity.risev2g.shared.messageHandling.PauseSession;
import com.v2gclarity.risev2g.shared.messageHandling.ReactionToIncomingMessage;
import com.v2gclarity.risev2g.shared.messageHandling.SendMessage;
import com.v2gclarity.risev2g.shared.messageHandling.TerminateSession;
import com.v2gclarity.risev2g.shared.misc.V2GCommunicationSession;
import com.v2gclarity.risev2g.shared.misc.V2GTPMessage;
import com.v2gclarity.risev2g.shared.utils.SecurityUtils.ContractCertificateStatus;
import com.v2gclarity.risev2g.shared.v2gMessages.appProtocol.AppProtocolType;
import com.v2gclarity.risev2g.shared.v2gMessages.appProtocol.SupportedAppProtocolRes;
import com.v2gclarity.risev2g.shared.v2gMessages.msgDef.ChargeParameterDiscoveryReqType;
import com.v2gclarity.risev2g.shared.v2gMessages.msgDef.ChargingProfileType;
import com.v2gclarity.risev2g.shared.v2gMessages.msgDef.EnergyTransferModeType;
import com.v2gclarity.risev2g.shared.v2gMessages.msgDef.PaymentOptionType;
import com.v2gclarity.risev2g.shared.v2gMessages.msgDef.SAScheduleListType;
import com.v2gclarity.risev2g.shared.v2gMessages.msgDef.SelectedServiceListType;
import com.v2gclarity.risev2g.shared.v2gMessages.msgDef.SelectedServiceType;
import com.v2gclarity.risev2g.shared.v2gMessages.msgDef.ServiceListType;
import com.v2gclarity.risev2g.shared.v2gMessages.msgDef.V2GMessage;
// The state machine
public class V2GCommunicationSessionEVCC extends V2GCommunicationSession implements Observer {
private long v2gEVCCCommunicationSetupTimer;
private String evseID;
private PaymentOptionType selectedPaymentOption;
private ReactionToIncomingMessage reactionToIncomingMessage;
/*
* chargeParameterDiscoveryReq is saved in the session because it might need to be resent in case
* the EVSEProcessing parameter of the respective response message is set to "Ongoing"
* (saves some processing time)
*/
private ChargeParameterDiscoveryReqType chargeParameterDiscoveryReq;
private boolean stopChargingRequested;
private boolean renegotiationRequested;
private boolean pausingV2GCommSession;
private ChargingProfileType chargingProfile;
private ServiceListType offeredServices;
private SelectedServiceListType selectedServices;
private ArrayList<Short> serviceDetailsToBeRequested;
private EnergyTransferModeType requestedEnergyTransferMode;
private long evseScheduleReceived; // The timestamp of receiving the SAScheduleList from the EVSE, is used as a reference
private List<AppProtocolType> supportedAppProtocols;
private AppProtocolType chosenAppProtocol;
private boolean oldSessionJoined;
private IEVController evController;
private long evseTimeStamp;
private SAScheduleListType saSchedules;
private long saSchedulesReceived;
private CPStates changeToState; // signals a needed state change (checked when sending the request message)
private StatefulTransportLayerClient transportLayerClient;
private ContractCertificateStatus contractCertStatus;
private long ongoingTimer; // used for V2G_EVCC_Ongoing_Timer, V2G_EVCC_PreCharge_Timer, V2G_EVCC_CableCheck_Timer
private boolean isOngoingTimerActive;
private byte[] sentGenChallenge;
public V2GCommunicationSessionEVCC(StatefulTransportLayerClient transportLayerClient) {
setTransportLayerClient(transportLayerClient);
getStates().put(V2GMessages.SUPPORTED_APP_PROTOCOL_RES, new WaitForSupportedAppProtocolRes(this));
getStates().put(V2GMessages.SESSION_SETUP_RES, new WaitForSessionSetupRes(this));
getStates().put(V2GMessages.SERVICE_DISCOVERY_RES, new WaitForServiceDiscoveryRes(this));
getStates().put(V2GMessages.SERVICE_DETAIL_RES, new WaitForServiceDetailRes(this));
getStates().put(V2GMessages.PAYMENT_SERVICE_SELECTION_RES, new WaitForPaymentServiceSelectionRes(this));
getStates().put(V2GMessages.CERTIFICATE_INSTALLATION_RES, new WaitForCertificateInstallationRes(this));
getStates().put(V2GMessages.CERTIFICATE_UPDATE_RES, new WaitForCertificateUpdateRes(this));
getStates().put(V2GMessages.PAYMENT_DETAILS_RES, new WaitForPaymentDetailsRes(this));
getStates().put(V2GMessages.AUTHORIZATION_RES, new WaitForAuthorizationRes(this));
getStates().put(V2GMessages.CHARGE_PARAMETER_DISCOVERY_RES, new WaitForChargeParameterDiscoveryRes(this));
getStates().put(V2GMessages.CABLE_CHECK_RES, new WaitForCableCheckRes(this));
getStates().put(V2GMessages.PRE_CHARGE_RES, new WaitForPreChargeRes(this));
getStates().put(V2GMessages.POWER_DELIVERY_RES, new WaitForPowerDeliveryRes(this));
getStates().put(V2GMessages.CHARGING_STATUS_RES, new WaitForChargingStatusRes(this));
getStates().put(V2GMessages.CURRENT_DEMAND_RES, new WaitForCurrentDemandRes(this));
getStates().put(V2GMessages.METERING_RECEIPT_RES, new WaitForMeteringReceiptRes(this));
getStates().put(V2GMessages.WELDING_DETECTION_RES, new WaitForWeldingDetectionRes(this));
getStates().put(V2GMessages.SESSION_STOP_RES, new WaitForSessionStopRes(this));
setStartState(getStates().get(V2GMessages.SUPPORTED_APP_PROTOCOL_RES));
setCurrentState(getStartState());
// configure which EV controller implementation to use
// TODO the EV controller needs to run as a separate Thread (to receive notifications from the EV and to avoid blocking calls to the controller)
setEvController(new DummyEVController(this));
/*
* Is needed for measuring the time span between transition to state B (plug-in) and receipt
* of a SessionSetupRes (see V2G_EVCC_COMMUNICATION_SETUP_TIMEOUT in TimeRestrictions.java)
* TODO check if this timing requirement is still up to date
*/
setV2gEVCCCommunicationSetupTimer(System.nanoTime());
// Set default value for contract certificate status to UNKNOWN
setContractCertStatus(ContractCertificateStatus.UNKNOWN);
getLogger().debug("\n*******************************************" +
"\n* New V2G communication session initialized" +
"\n*******************************************");
}
@Override
public void update(Observable obs, Object obj) {
if ((obs instanceof TCPClient || obs instanceof TLSClient) && obj instanceof byte[]) {
setV2gTpMessage(new V2GTPMessage((byte[]) obj));
if (getMessageHandler().isV2GTPMessageValid(getV2gTpMessage())) {
/*
* We need to decide which schema to use for decoding the EXI encoded message. Only
* the supportedAppProtocolReq/Res message uses a different schema
*/
if (getCurrentState().equals(getStates().get(V2GMessages.SUPPORTED_APP_PROTOCOL_RES))) {
obj = (SupportedAppProtocolRes) getMessageHandler().exiToSuppAppProtocolMsg(getV2gTpMessage().getPayload());
} else {
try {
obj = (V2GMessage) getMessageHandler().exiToV2gMsg(getV2gTpMessage().getPayload());
} catch (ClassCastException e) {
terminateSession("Received incoming message is not a valid V2GTPMessage\n" + e, false);
}
}
processReaction(getCurrentState().processIncomingMessage(obj));
} else {
terminateSession("Received incoming message is not a valid V2GTPMessage", false);
}
} else if ((obs instanceof TCPClient || obs instanceof TLSClient) && obj == null) {
terminateSession("Transport layer has notified an error", false);
} else {
terminateSession("Notification received, but sending entity or received object not identifiable", false);
}
}
private void processReaction(ReactionToIncomingMessage reactionToIncomingMessage) {
if (reactionToIncomingMessage instanceof SendMessage) {
send((SendMessage) reactionToIncomingMessage);
} else if (reactionToIncomingMessage instanceof TerminateSession) {
deleteSessionProperties();
terminateSession((TerminateSession) reactionToIncomingMessage);
} else if (reactionToIncomingMessage instanceof PauseSession) {
saveSessionProperties();
pauseSession((PauseSession) reactionToIncomingMessage);
} else if (reactionToIncomingMessage instanceof ChangeProcessingState) {
setCurrentState(((ChangeProcessingState) reactionToIncomingMessage).getNewState());
processReaction(
getCurrentState().processIncomingMessage(reactionToIncomingMessage)
); // TODO ist das korrekt?! bspw. wenn Renegotiation angefragt wird von EVSE?
} else {
terminateSession("Reaction to incoming message is undefined", false);
}
}
public void send(SendMessage sendingParams) {
// Only EXI encoded messages starting from SessionSetupReq will be sent here
setV2gTpMessage(new V2GTPMessage(GlobalValues.V2GTP_VERSION_1_IS.getByteValue(),
GlobalValues.V2GTP_PAYLOAD_TYPE_EXI_ENCODED_V2G_MESSAGE.getByteArrayValue(),
(byte[]) getMessageHandler().v2gMsgToExi(sendingParams.getPayload()))
);
getTransportLayerClient().send(getV2gTpMessage(), sendingParams.getTimeout());
// Check for necessary CP state change (see [V2G2-847])
if (getChangeToState() != null) {
if (getEvController().setCPState(getChangeToState())) setChangeToState(null);
else terminateSession("State change to " + getChangeToState().toString() + " not successful", false);
}
if (sendingParams.getNextState() != null) {
setCurrentState(sendingParams.getNextState());
} else {
terminateSession("State machine interrupted, no new state provided", true);
}
}
private void saveSessionProperties() {
// TODO save respective parameters to properties file
}
private void deleteSessionProperties() {
// TODO delete the respective parameters from properties file
}
private void setV2gEVCCCommunicationSetupTimer(
long v2gEVCCCommunicationSetupTimer) {
this.v2gEVCCCommunicationSetupTimer = v2gEVCCCommunicationSetupTimer;
}
public long getV2gEVCCCommunicationSetupTimer() {
return v2gEVCCCommunicationSetupTimer;
}
public String getEvseID() {
return evseID;
}
public void setEvseID(String evseID) {
this.evseID = evseID;
}
public PaymentOptionType getSelectedPaymentOption() {
return selectedPaymentOption;
}
public void setSelectedPaymentOption(PaymentOptionType selectedPaymentOption) {
this.selectedPaymentOption = selectedPaymentOption;
}
public ChargeParameterDiscoveryReqType getChargeParameterDiscoveryReq() {
return chargeParameterDiscoveryReq;
}
public void setChargeParameterDiscoveryReq(
ChargeParameterDiscoveryReqType chargeParameterDiscoveryReq) {
this.chargeParameterDiscoveryReq = chargeParameterDiscoveryReq;
}
public ReactionToIncomingMessage getReactionToIncomingMessage() {
return reactionToIncomingMessage;
}
public void setReactionToIncomingMessage(ReactionToIncomingMessage reactionToIncomingMessage) {
this.reactionToIncomingMessage = reactionToIncomingMessage;
}
public boolean isStopChargingRequested() {
return stopChargingRequested;
}
public void setStopChargingRequested(boolean stopChargingRequested) {
this.stopChargingRequested = stopChargingRequested;
}
public boolean isRenegotiationRequested() {
return renegotiationRequested;
}
public void setRenegotiationRequested(boolean renegotiationRequested) {
this.renegotiationRequested = renegotiationRequested;
}
public boolean isPausingV2GCommSession() {
return pausingV2GCommSession;
}
public void setPausingV2GCommSession(boolean pausingV2GCommSession) {
this.pausingV2GCommSession = pausingV2GCommSession;
}
public long getEvseScheduleReceived() {
return evseScheduleReceived;
}
public void setEvseScheduleReceived(long evseScheduleReceived) {
this.evseScheduleReceived = evseScheduleReceived;
}
public ChargingProfileType getChargingProfile() {
return chargingProfile;
}
public void setChargingProfile(ChargingProfileType chargingProfile) {
this.chargingProfile = chargingProfile;
}
public List<AppProtocolType> getSupportedAppProtocols() {
return supportedAppProtocols;
}
public void setSupportedAppProtocols(List<AppProtocolType> supportedAppProtocols) {
this.supportedAppProtocols = supportedAppProtocols;
}
public AppProtocolType getChosenAppProtocol() {
return chosenAppProtocol;
}
public void setChosenAppProtocol(AppProtocolType chosenAppProtocol) {
this.chosenAppProtocol = chosenAppProtocol;
}
public boolean isOldSessionJoined() {
return oldSessionJoined;
}
public void setOldSessionJoined(boolean oldSessionJoined) {
this.oldSessionJoined = oldSessionJoined;
}
public IEVController getEvController() {
return evController;
}
public void setEvController(IEVController evController) {
this.evController = evController;
}
public long getEvseTimeStamp() {
return evseTimeStamp;
}
public void setEvseTimeStamp(long evseTimeStamp) {
this.evseTimeStamp = evseTimeStamp;
}
public EnergyTransferModeType getRequestedEnergyTransferMode() {
return requestedEnergyTransferMode;
}
public void setRequestedEnergyTransferMode(
EnergyTransferModeType requestedEnergyTransferMode) {
this.requestedEnergyTransferMode = requestedEnergyTransferMode;
}
public SAScheduleListType getSaSchedules() {
return saSchedules;
}
public void setSaSchedules(SAScheduleListType saSchedules) {
this.saSchedules = saSchedules;
this.saSchedulesReceived = System.nanoTime();
}
public long getSaSchedulesReceived() {
return saSchedulesReceived;
}
public CPStates getChangeToState() {
return changeToState;
}
public void setChangeToState(CPStates changeToState) {
this.changeToState = changeToState;
}
public StatefulTransportLayerClient getTransportLayerClient() {
return transportLayerClient;
}
public void setTransportLayerClient(StatefulTransportLayerClient transportLayerClient) {
this.transportLayerClient = transportLayerClient;
}
public SelectedServiceListType getSelectedServices() {
if (selectedServices == null) setSelectedServices(new SelectedServiceListType());
return selectedServices;
}
public void setSelectedServices(SelectedServiceListType selectedServices) {
this.selectedServices = selectedServices;
}
public ServiceListType getOfferedServices() {
if (offeredServices == null) setOfferedServices(new ServiceListType());
return offeredServices;
}
public void setOfferedServices(ServiceListType offeredServices) {
this.offeredServices = offeredServices;
}
public ArrayList<Short> getServiceDetailsToBeRequested() {
if (serviceDetailsToBeRequested == null) {
serviceDetailsToBeRequested = new ArrayList<Short>();
}
return serviceDetailsToBeRequested;
}
public void setServiceDetailsToBeRequested(
ArrayList<Short> serviceDetailsToBeRequested) {
this.serviceDetailsToBeRequested = serviceDetailsToBeRequested;
}
/**
* Checks if the respective service for installing or updating a certificate is offered by the SECC and
* has been selected by the EVCC.
*
* @param parameterSetID 1 for installing a certificate, 2 for updating a certificate
* @return True, if the respective certificate service is available, false otherwise
*/
public boolean isCertificateServiceAvailable(short parameterSetID) {
for (SelectedServiceType service : getSelectedServices().getSelectedService()) {
if (service.getServiceID() == 2 && // ServiceID 2 refers to the 'Certificate' service
service.getParameterSetID() != null &&
service.getParameterSetID() == parameterSetID)
return true;
}
return false;
}
public ContractCertificateStatus getContractCertStatus() {
return contractCertStatus;
}
public void setContractCertStatus(ContractCertificateStatus contractCertStatus) {
this.contractCertStatus = contractCertStatus;
}
public long getOngoingTimer() {
return ongoingTimer;
}
public void setOngoingTimer(long ongoingTimer) {
this.ongoingTimer = ongoingTimer;
}
public boolean isOngoingTimerActive() {
return isOngoingTimerActive;
}
public void setOngoingTimerActive(boolean isOngoingTimerActive) {
this.isOngoingTimerActive = isOngoingTimerActive;
}
public byte[] getSentGenChallenge() {
return sentGenChallenge;
}
public void setSentGenChallenge(byte[] sentGenChallenge) {
this.sentGenChallenge = sentGenChallenge;
}
}