package de.rwth.idsg.steve.service;
import de.rwth.idsg.steve.ocpp.OcppProtocol;
import de.rwth.idsg.steve.repository.OcppServerRepository;
import de.rwth.idsg.steve.repository.SettingsRepository;
import de.rwth.idsg.steve.repository.dto.InsertConnectorStatusParams;
import de.rwth.idsg.steve.repository.dto.InsertTransactionParams;
import de.rwth.idsg.steve.repository.dto.UpdateChargeboxParams;
import de.rwth.idsg.steve.repository.dto.UpdateTransactionParams;
import lombok.extern.slf4j.Slf4j;
import ocpp.cs._2012._06.AuthorizeRequest;
import ocpp.cs._2012._06.AuthorizeResponse;
import ocpp.cs._2012._06.BootNotificationRequest;
import ocpp.cs._2012._06.BootNotificationResponse;
import ocpp.cs._2012._06.ChargePointStatus;
import ocpp.cs._2012._06.DataTransferRequest;
import ocpp.cs._2012._06.DataTransferResponse;
import ocpp.cs._2012._06.DiagnosticsStatusNotificationRequest;
import ocpp.cs._2012._06.DiagnosticsStatusNotificationResponse;
import ocpp.cs._2012._06.FirmwareStatusNotificationRequest;
import ocpp.cs._2012._06.FirmwareStatusNotificationResponse;
import ocpp.cs._2012._06.HeartbeatRequest;
import ocpp.cs._2012._06.HeartbeatResponse;
import ocpp.cs._2012._06.IdTagInfo;
import ocpp.cs._2012._06.MeterValue;
import ocpp.cs._2012._06.MeterValuesRequest;
import ocpp.cs._2012._06.MeterValuesResponse;
import ocpp.cs._2012._06.RegistrationStatus;
import ocpp.cs._2012._06.StartTransactionRequest;
import ocpp.cs._2012._06.StartTransactionResponse;
import ocpp.cs._2012._06.StatusNotificationRequest;
import ocpp.cs._2012._06.StatusNotificationResponse;
import ocpp.cs._2012._06.StopTransactionRequest;
import ocpp.cs._2012._06.StopTransactionResponse;
import org.joda.time.DateTime;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.stream.Collectors;
/**
* Transport-level agnostic OCPP 1.5 server service which contains the actual business logic.
*
* @author Sevket Goekay <goekay@dbis.rwth-aachen.de>
* @since 13.03.2015
*/
@Slf4j
@Service
public class CentralSystemService15_Service {
@Autowired private OcppServerRepository ocppServerRepository;
@Autowired private OcppTagService ocppTagService;
@Autowired private SettingsRepository settingsRepository;
@Autowired private NotificationService notificationService;
public BootNotificationResponse bootNotification(BootNotificationRequest parameters, String chargeBoxIdentity,
OcppProtocol ocppProtocol) {
log.debug("Executing bootNotification for {}", chargeBoxIdentity);
DateTime now = DateTime.now();
UpdateChargeboxParams params =
UpdateChargeboxParams.builder()
.ocppProtocol(ocppProtocol)
.vendor(parameters.getChargePointVendor())
.model(parameters.getChargePointModel())
.pointSerial(parameters.getChargePointSerialNumber())
.boxSerial(parameters.getChargeBoxSerialNumber())
.fwVersion(parameters.getFirmwareVersion())
.iccid(parameters.getIccid())
.imsi(parameters.getImsi())
.meterType(parameters.getMeterType())
.meterSerial(parameters.getMeterSerialNumber())
.chargeBoxId(chargeBoxIdentity)
.heartbeatTimestamp(now)
.build();
boolean isRegistered = ocppServerRepository.updateChargebox(params);
notificationService.ocppStationBooted(chargeBoxIdentity, isRegistered);
RegistrationStatus status = isRegistered ? RegistrationStatus.ACCEPTED : RegistrationStatus.REJECTED;
return new BootNotificationResponse()
.withStatus(status)
.withCurrentTime(now)
.withHeartbeatInterval(settingsRepository.getHeartbeatIntervalInSeconds());
}
public FirmwareStatusNotificationResponse firmwareStatusNotification(
FirmwareStatusNotificationRequest parameters, String chargeBoxIdentity) {
log.debug("Executing firmwareStatusNotification for {}", chargeBoxIdentity);
String status = parameters.getStatus().value();
ocppServerRepository.updateChargeboxFirmwareStatus(chargeBoxIdentity, status);
return new FirmwareStatusNotificationResponse();
}
public StatusNotificationResponse statusNotification(
StatusNotificationRequest parameters, String chargeBoxIdentity) {
log.debug("Executing statusNotification for {}", chargeBoxIdentity);
// Optional field
DateTime timestamp = parameters.isSetTimestamp() ? parameters.getTimestamp() : DateTime.now();
InsertConnectorStatusParams params =
InsertConnectorStatusParams.builder()
.chargeBoxId(chargeBoxIdentity)
.connectorId(parameters.getConnectorId())
.status(parameters.getStatus().value())
.errorCode(parameters.getErrorCode().value())
.timestamp(timestamp)
.errorInfo(parameters.getInfo())
.vendorId(parameters.getVendorId())
.vendorErrorCode(parameters.getVendorErrorCode())
.build();
ocppServerRepository.insertConnectorStatus(params);
if (parameters.getStatus() == ChargePointStatus.FAULTED) {
notificationService.ocppStationStatusFailure(
chargeBoxIdentity, parameters.getConnectorId(), parameters.getErrorCode().value());
}
return new StatusNotificationResponse();
}
public MeterValuesResponse meterValues(MeterValuesRequest parameters, String chargeBoxIdentity) {
log.debug("Executing meterValues for {}", chargeBoxIdentity);
int connectorId = parameters.getConnectorId();
Integer transactionId = parameters.getTransactionId();
if (parameters.isSetValues()) {
ocppServerRepository.insertMeterValues15(chargeBoxIdentity, connectorId,
parameters.getValues(), transactionId);
}
return new MeterValuesResponse();
}
public DiagnosticsStatusNotificationResponse diagnosticsStatusNotification(
DiagnosticsStatusNotificationRequest parameters, String chargeBoxIdentity) {
log.debug("Executing diagnosticsStatusNotification for {}", chargeBoxIdentity);
String status = parameters.getStatus().value();
ocppServerRepository.updateChargeboxDiagnosticsStatus(chargeBoxIdentity, status);
return new DiagnosticsStatusNotificationResponse();
}
public StartTransactionResponse startTransaction(StartTransactionRequest parameters, String chargeBoxIdentity) {
log.debug("Executing startTransaction for {}", chargeBoxIdentity);
InsertTransactionParams params =
InsertTransactionParams.builder()
.chargeBoxId(chargeBoxIdentity)
.connectorId(parameters.getConnectorId())
.idTag(parameters.getIdTag())
.startTimestamp(parameters.getTimestamp())
.startMeterValue(Integer.toString(parameters.getMeterStart()))
.reservationId(parameters.getReservationId())
.build();
IdTagInfo info = ocppTagService.getIdTagInfoV15(parameters.getIdTag());
Integer transactionId = ocppServerRepository.insertTransaction(params);
return new StartTransactionResponse()
.withIdTagInfo(info)
.withTransactionId(transactionId);
}
public StopTransactionResponse stopTransaction(StopTransactionRequest parameters, String chargeBoxIdentity) {
log.debug("Executing stopTransaction for {}", chargeBoxIdentity);
int transactionId = parameters.getTransactionId();
UpdateTransactionParams params =
UpdateTransactionParams.builder()
.chargeBoxId(chargeBoxIdentity)
.transactionId(transactionId)
.stopTimestamp(parameters.getTimestamp())
.stopMeterValue(Integer.toString(parameters.getMeterStop()))
.build();
ocppServerRepository.updateTransaction(params);
/**
* If TransactionData is included:
*
* Aggregate MeterValues from multiple TransactionData in one big, happy list. TransactionData is just
* a container for MeterValues without any additional data/semantics anyway. This enables us to write
* into DB in one repository call (query), rather than multiple calls as it was before
* (for each TransactionData)
*
* Saved the world again with this micro-optimization.
*/
if (parameters.isSetTransactionData()) {
List<MeterValue> combinedList = parameters.getTransactionData()
.stream()
.flatMap(data -> data.getValues().stream())
.collect(Collectors.toList());
ocppServerRepository.insertMeterValuesOfTransaction(chargeBoxIdentity, transactionId, combinedList);
}
// Get the authorization info of the user
if (parameters.isSetIdTag()) {
IdTagInfo idTagInfo = ocppTagService.getIdTagInfoV15(parameters.getIdTag());
return new StopTransactionResponse().withIdTagInfo(idTagInfo);
} else {
return new StopTransactionResponse();
}
}
public HeartbeatResponse heartbeat(HeartbeatRequest parameters, String chargeBoxIdentity) {
log.debug("Executing heartbeat for {}", chargeBoxIdentity);
DateTime now = DateTime.now();
ocppServerRepository.updateChargeboxHeartbeat(chargeBoxIdentity, now);
return new HeartbeatResponse().withCurrentTime(now);
}
public AuthorizeResponse authorize(AuthorizeRequest parameters, String chargeBoxIdentity) {
log.debug("Executing authorize for {}", chargeBoxIdentity);
// Get the authorization info of the user
String idTag = parameters.getIdTag();
IdTagInfo idTagInfo = ocppTagService.getIdTagInfoV15(idTag);
return new AuthorizeResponse().withIdTagInfo(idTagInfo);
}
// Dummy implementation. This is new in OCPP 1.5. It must be vendor-specific.
public DataTransferResponse dataTransfer(DataTransferRequest parameters, String chargeBoxIdentity) {
log.debug("Executing dataTransfer for {}", chargeBoxIdentity);
log.info("[Data Transfer] Charge point: {}, Vendor Id: {}", chargeBoxIdentity, parameters.getVendorId());
if (parameters.isSetMessageId()) {
log.info("[Data Transfer] Message Id: {}", parameters.getMessageId());
}
if (parameters.isSetData()) {
log.info("[Data Transfer] Data: {}", parameters.getData());
}
return new DataTransferResponse();
}
}