From 0005fa7ceb5e8ffed2edf2bb6f64304a33c88fb7 Mon Sep 17 00:00:00 2001 From: Tester Date: Wed, 2 Aug 2023 19:19:31 +0200 Subject: [PATCH 01/39] StringUtils: add method to check mail address --- .../java/de/rwth/idsg/steve/utils/StringUtils.java | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/main/java/de/rwth/idsg/steve/utils/StringUtils.java b/src/main/java/de/rwth/idsg/steve/utils/StringUtils.java index 7e5cc2413..979ad0884 100644 --- a/src/main/java/de/rwth/idsg/steve/utils/StringUtils.java +++ b/src/main/java/de/rwth/idsg/steve/utils/StringUtils.java @@ -32,6 +32,7 @@ import java.util.Collections; import java.util.LinkedHashSet; import java.util.List; +import java.util.regex.Pattern; /** * @author Sevket Goekay @@ -95,4 +96,14 @@ public static String getLastBitFromUrl(final String input) { return input.substring(index + substring.length()); } } + + // https://www.baeldung.com/java-email-validation-regex + public static boolean isValidAddress(String emailAddress) { + String regexPattern = "^(?=.{1,64}@)[A-Za-z0-9_-]+(\\.[A-Za-z0-9_-]+)*@[^-][A-Za-z0-9-]+(\\.[A-Za-z0-9-]+)*(\\.[A-Za-z]{2,})$"; // Strict Regular Expression Validation + //String regexPattern = "^(?=.{1,64}@)[\\p{L}0-9_-]+(\\.[\\p{L}0-9_-]+)*@[^-][\\p{L}0-9-]+(\\.[\\p{L}0-9-]+)*(\\.[\\p{L}]{2,})$"; //Regular Expression for Validation of Non-Latin or Unicode Characters Email + //String regexPattern = "^[a-zA-Z0-9_!#$%&'*+/=?`{|}~^.-]+@[a-zA-Z0-9.-]+$" // Regular Expression by RFC 5322 for Email Validation + return Pattern.compile(regexPattern) + .matcher(emailAddress) + .matches(); + } } From 4cbea6f40ec4349818f6d245e8904be54c558e0c Mon Sep 17 00:00:00 2001 From: Tester Date: Wed, 2 Aug 2023 19:21:46 +0200 Subject: [PATCH 02/39] UserRepository: add method "User.Details getDetails(String ocppTag)" --- .../idsg/steve/repository/UserRepository.java | 1 + .../repository/impl/UserRepositoryImpl.java | 37 +++++++++++++++++++ 2 files changed, 38 insertions(+) diff --git a/src/main/java/de/rwth/idsg/steve/repository/UserRepository.java b/src/main/java/de/rwth/idsg/steve/repository/UserRepository.java index 840185ecd..63619c5d5 100644 --- a/src/main/java/de/rwth/idsg/steve/repository/UserRepository.java +++ b/src/main/java/de/rwth/idsg/steve/repository/UserRepository.java @@ -31,6 +31,7 @@ public interface UserRepository { List getOverview(UserQueryForm form); User.Details getDetails(int userPk); + User.Details getDetails(String ocppTag); void add(UserForm form); void update(UserForm form); diff --git a/src/main/java/de/rwth/idsg/steve/repository/impl/UserRepositoryImpl.java b/src/main/java/de/rwth/idsg/steve/repository/impl/UserRepositoryImpl.java index b35f69189..cafe551fa 100644 --- a/src/main/java/de/rwth/idsg/steve/repository/impl/UserRepositoryImpl.java +++ b/src/main/java/de/rwth/idsg/steve/repository/impl/UserRepositoryImpl.java @@ -116,6 +116,43 @@ public User.Details getDetails(int userPk) { .build(); } + @Override + public User.Details getDetails(String ocppIdTag) { + + Integer ocppPk = ctx.select(OCPP_TAG.OCPP_TAG_PK) + .from(OCPP_TAG) + .where(OCPP_TAG.ID_TAG.eq(ocppIdTag)) + .fetchOne(OCPP_TAG.OCPP_TAG_PK); + + if (ocppPk == null) { + throw new SteveException("There is no OCPP_Tag: '%s'", ocppIdTag); + } + + // ------------------------------------------------------------------------- + // 1. user table + // ------------------------------------------------------------------------- + + UserRecord ur = ctx.selectFrom(USER) + .where(USER.OCPP_TAG_PK.equal(ocppPk)) + .fetchOne(); + + if (ur == null) { + throw new SteveException("There is no user with OCPP_TAG '%s'", ocppIdTag); + } + + // ------------------------------------------------------------------------- + // 2. address table + // ------------------------------------------------------------------------- + + AddressRecord ar = addressRepository.get(ctx, ur.getAddressPk()); + + return User.Details.builder() + .userRecord(ur) + .address(ar) + .ocppIdTag(Optional.ofNullable(ocppIdTag)) + .build(); + } + @Override public void add(UserForm form) { ctx.transaction(configuration -> { From 75fd4faf8ba2d1ed0c66551e8f9f1d3c640c5aba Mon Sep 17 00:00:00 2001 From: Tester Date: Wed, 2 Aug 2023 19:28:37 +0200 Subject: [PATCH 03/39] TransactionRepository: add method "Transaction getTransaction(int transaction_pk);" --- .../idsg/steve/repository/TransactionRepository.java | 2 ++ .../repository/impl/TransactionRepositoryImpl.java | 10 ++++++++++ 2 files changed, 12 insertions(+) diff --git a/src/main/java/de/rwth/idsg/steve/repository/TransactionRepository.java b/src/main/java/de/rwth/idsg/steve/repository/TransactionRepository.java index dec544eee..2efedb959 100644 --- a/src/main/java/de/rwth/idsg/steve/repository/TransactionRepository.java +++ b/src/main/java/de/rwth/idsg/steve/repository/TransactionRepository.java @@ -30,6 +30,8 @@ * @since 19.08.2014 */ public interface TransactionRepository { + Transaction getTransaction(int transaction_pk); + List getTransactions(TransactionQueryForm form); void writeTransactionsCSV(TransactionQueryForm form, Writer writer); diff --git a/src/main/java/de/rwth/idsg/steve/repository/impl/TransactionRepositoryImpl.java b/src/main/java/de/rwth/idsg/steve/repository/impl/TransactionRepositoryImpl.java index 52847e188..77a083133 100644 --- a/src/main/java/de/rwth/idsg/steve/repository/impl/TransactionRepositoryImpl.java +++ b/src/main/java/de/rwth/idsg/steve/repository/impl/TransactionRepositoryImpl.java @@ -65,6 +65,16 @@ public TransactionRepositoryImpl(DSLContext ctx) { this.ctx = ctx; } + @Override + public Transaction getTransaction(int transaction_pk) { + TransactionQueryForm form = new TransactionQueryForm(); + form.setTransactionPk(transaction_pk); + form.setReturnCSV(false); + form.setType(TransactionQueryForm.QueryType.ALL); + return getInternal(form).fetch() + .map(new TransactionMapper()).get(0); + } + @Override public List getTransactions(TransactionQueryForm form) { return getInternal(form).fetch() From e254786da33346626617902c539f6404965e428a Mon Sep 17 00:00:00 2001 From: fnkbsi <> Date: Wed, 2 Aug 2023 19:45:48 +0200 Subject: [PATCH 04/39] MailService: change method "send(String subject, String body)" to "send(String subject, String body, String RecipientAddresses)" use addresses from setting if RecipientAddresses is empty; adding method "sendAsync(String subject, String body, String RecipientAddresses)" --- .../rwth/idsg/steve/service/MailService.java | 52 +++++++++++++++---- 1 file changed, 43 insertions(+), 9 deletions(-) diff --git a/src/main/java/de/rwth/idsg/steve/service/MailService.java b/src/main/java/de/rwth/idsg/steve/service/MailService.java index ee338ad0f..6cc9876ce 100644 --- a/src/main/java/de/rwth/idsg/steve/service/MailService.java +++ b/src/main/java/de/rwth/idsg/steve/service/MailService.java @@ -40,6 +40,9 @@ import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; +import static de.rwth.idsg.steve.utils.StringUtils.splitByComma; +import static de.rwth.idsg.steve.utils.StringUtils.isValidAddress; +import java.util.List; /** * @author Sevket Goekay @@ -81,7 +84,7 @@ public MailSettings getSettings() { public void sendTestMail() { try { - send("Test", "Test"); + send("Test", "Test",""); } catch (MessagingException e) { throw new SteveException("Failed to send mail", e); } @@ -90,29 +93,60 @@ public void sendTestMail() { public void sendAsync(String subject, String body) { executorService.execute(() -> { try { - send(subject, body); + send(subject, body, ""); } catch (MessagingException e) { log.error("Failed to send mail", e); } }); } - public void send(String subject, String body) throws MessagingException { - MailSettings settings = getSettings(); + public void sendAsync(String subject, String body, String RecipientAddresses) { + executorService.execute(() -> { + try { + send(subject, body, RecipientAddresses); + } catch (MessagingException e) { + log.error("Failed to send mail", e); + } + }); + } + + private void send(String subject, String body, String RecipientAddresses) throws MessagingException { + MailSettings settingsLocal = getSettings(); - Message mail = new MimeMessage(session); - mail.setSubject("[SteVe] " + subject); - mail.setContent(body, "text/plain"); - mail.setFrom(new InternetAddress(settings.getFrom())); + Message mail = new MimeMessage(session); + mail.setSubject("[SteVe] " + subject); + mail.setContent(body, "text/plain"); + mail.setFrom(new InternetAddress(settingsLocal.getFrom())); - for (String rep : settings.getRecipients()) { + List eMailAddresses; + + if (RecipientAddresses.isEmpty()) + { + eMailAddresses = settingsLocal.getRecipients(); + } + else + { + + eMailAddresses = splitByComma(RecipientAddresses); + } + + for (String rep : eMailAddresses) { + if (isValidAddress(rep)){ mail.addRecipient(Message.RecipientType.TO, new InternetAddress(rep)); } + else{ + log.error("Failed to send mail to " + rep + "! Format of the address is invalid."); + } + } try (Transport transport = session.getTransport()) { transport.connect(); transport.sendMessage(mail, mail.getAllRecipients()); } + catch(Exception e) + { + log.error("Failed to send mail(s)! ", e); + } } // ------------------------------------------------------------------------- From 4eadaa8e8979b12cb2b88884245645895948df18 Mon Sep 17 00:00:00 2001 From: fnkbsi Date: Wed, 2 Aug 2023 20:12:34 +0200 Subject: [PATCH 05/39] NotificationService: in method "ocppTransactionEnded(OcppTransactionEnded notification)" adding send mail to user (if email address is in the database), adding createContent method for user mail --- .../steve/service/NotificationService.java | 58 ++++++++++++++++++- 1 file changed, 57 insertions(+), 1 deletion(-) diff --git a/src/main/java/de/rwth/idsg/steve/service/NotificationService.java b/src/main/java/de/rwth/idsg/steve/service/NotificationService.java index 269b42b2a..6368b3808 100644 --- a/src/main/java/de/rwth/idsg/steve/service/NotificationService.java +++ b/src/main/java/de/rwth/idsg/steve/service/NotificationService.java @@ -41,7 +41,11 @@ import static de.rwth.idsg.steve.NotificationFeature.OcppStationWebSocketDisconnected; import static de.rwth.idsg.steve.NotificationFeature.OcppTransactionStarted; import static de.rwth.idsg.steve.NotificationFeature.OcppTransactionEnded; +import de.rwth.idsg.steve.repository.TransactionRepository; +import de.rwth.idsg.steve.repository.UserRepository; +import de.rwth.idsg.steve.repository.dto.Transaction; import static java.lang.String.format; +import jooq.steve.db.tables.records.UserRecord; /** * @author Sevket Goekay @@ -52,6 +56,8 @@ public class NotificationService { @Autowired private MailService mailService; + @Autowired private TransactionRepository transactionRepository; + @Autowired private UserRepository userRepository; @EventListener public void ocppStationBooted(OccpStationBooted notification) { @@ -117,7 +123,24 @@ public void ocppTransactionStarted(OcppTransactionStarted notification) { @EventListener public void ocppTransactionEnded(OcppTransactionEnded notification) { - if (isDisabled(OcppTransactionEnded)) { + Transaction TransActParams = transactionRepository.getTransaction(notification.getParams().getTransactionId()); + + TransActParams.getOcppTagPk(); + UserRecord userRecord = userRepository.getDetails(TransActParams.getOcppIdTag()).getUserRecord(); + String eMailAddress = userRecord.getEMail(); + + // mail to user + if (!Strings.isNullOrEmpty(eMailAddress)) { + String subjectUserMail = format("Transaction '%s' has ended on charging station '%s'", TransActParams.getId(), TransActParams.getChargeBoxId()); + + // if the Transactionstop is received within the first 1 Minute don't send an E-Mail + if (TransActParams.getStopTimestamp().isAfter(TransActParams.getStartTimestamp().plusMinutes(1))){ + mailService.sendAsync(subjectUserMail, addTimestamp(createContent(TransActParams, userRecord)), eMailAddress); + } + } + + /* mail defined in settings */ + if (isDisabled(OcppTransactionEnded)) { return; } @@ -156,6 +179,39 @@ private static String createContent(UpdateTransactionParams params) { .toString(); } +private static String createContent(Transaction params, UserRecord userRecord) { + Double meterValueDiff; + Integer meterValueStop; + Integer meterValueStart; + String str_meterValueDiff = "-"; + try + { + meterValueStop = Integer.valueOf(params.getStopValue()); + meterValueStart = Integer.valueOf(params.getStartValue()); + meterValueDiff = (meterValueStop - meterValueStart)/1000.0; // --> kWh + str_meterValueDiff = meterValueDiff.toString() + " kWh"; + } + catch(NumberFormatException e) + { + log.error("Failed to calculate charged energy! ", e); + } + + return new StringBuilder("User: ") + .append(userRecord.getFirstName()).append(" ").append(userRecord.getLastName()) + .append(System.lineSeparator()) + .append(System.lineSeparator()) + .append("Details:").append(System.lineSeparator()) + .append("- chargeBoxId: ").append(params.getChargeBoxId()).append(System.lineSeparator()) + .append("- connectorId: ").append(params.getConnectorId()).append(System.lineSeparator()) + .append("- transactionId: ").append(params.getId()).append(System.lineSeparator()) // getTransactionId() + .append("- startTimestamp (UTC): ").append(params.getStartTimestamp()).append(System.lineSeparator()) + .append("- startMeterValue: ").append(params.getStartValue()).append(System.lineSeparator()) + .append("- stopTimestamp (UTC): ").append(params.getStopTimestamp()).append(System.lineSeparator()) + .append("- stopMeterValue: ").append(params.getStopValue()).append(System.lineSeparator()) + .append("- stopReason: ").append(params.getStopReason()).append(System.lineSeparator()) + .append("- charged energy: ").append(str_meterValueDiff).append(System.lineSeparator()) + .toString(); + } private boolean isDisabled(NotificationFeature f) { MailSettings settings = mailService.getSettings(); From 1b366b4e0e30ac13b03a04b263df63e3bef631c2 Mon Sep 17 00:00:00 2001 From: fnkbsi Date: Wed, 2 Aug 2023 21:14:33 +0200 Subject: [PATCH 06/39] OcppStationStatusSuspendedEV.java added --- .../OcppStationStatusSuspendedEV.java | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 src/main/java/de/rwth/idsg/steve/service/notification/OcppStationStatusSuspendedEV.java diff --git a/src/main/java/de/rwth/idsg/steve/service/notification/OcppStationStatusSuspendedEV.java b/src/main/java/de/rwth/idsg/steve/service/notification/OcppStationStatusSuspendedEV.java new file mode 100644 index 000000000..ce6906feb --- /dev/null +++ b/src/main/java/de/rwth/idsg/steve/service/notification/OcppStationStatusSuspendedEV.java @@ -0,0 +1,33 @@ +/* + * SteVe - SteckdosenVerwaltung - https://github.com/steve-community/steve + * Copyright (C) 2013-2023 SteVe Community Team + * All Rights Reserved. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package de.rwth.idsg.steve.service.notification; + +import lombok.Data; + +/** + * @author Frank Brosi + * @since 12.10.2022 + * + */ +@Data +public class OcppStationStatusSuspendedEV { + + private final String chargeBoxId; + private final int connectorId; +} From e38a1084512765dcd698ddb00869bf1c3480407c Mon Sep 17 00:00:00 2001 From: fnkbsi Date: Wed, 2 Aug 2023 21:16:12 +0200 Subject: [PATCH 07/39] NotificationFeature.java: added NotificationFeature "OcppStationStatusSuspendedEV" --- src/main/java/de/rwth/idsg/steve/NotificationFeature.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/de/rwth/idsg/steve/NotificationFeature.java b/src/main/java/de/rwth/idsg/steve/NotificationFeature.java index a5fd343d0..ba43fc6f3 100644 --- a/src/main/java/de/rwth/idsg/steve/NotificationFeature.java +++ b/src/main/java/de/rwth/idsg/steve/NotificationFeature.java @@ -35,6 +35,7 @@ public enum NotificationFeature { OcppStationWebSocketConnected(" a JSON charging station connects"), OcppStationWebSocketDisconnected(" a JSON charging station disconnects"), OcppTransactionStarted(" a charging station starts a transaction"), + OcppStationStatusSuspendedEV(" a EV suspended charging"), OcppTransactionEnded(" a charging station ends a transaction"); From f51a3daea538dc70bd72f425414b61c203c94318 Mon Sep 17 00:00:00 2001 From: fnkbsi <> Date: Wed, 2 Aug 2023 21:20:46 +0200 Subject: [PATCH 08/39] CentralSystemService16_Service.java: on a statusNotification, added publishing a event if the status is "SuspendedEV" --- .../idsg/steve/service/CentralSystemService16_Service.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/main/java/de/rwth/idsg/steve/service/CentralSystemService16_Service.java b/src/main/java/de/rwth/idsg/steve/service/CentralSystemService16_Service.java index a55e9ca67..09916db6e 100644 --- a/src/main/java/de/rwth/idsg/steve/service/CentralSystemService16_Service.java +++ b/src/main/java/de/rwth/idsg/steve/service/CentralSystemService16_Service.java @@ -27,6 +27,7 @@ import de.rwth.idsg.steve.repository.dto.UpdateTransactionParams; import de.rwth.idsg.steve.service.notification.OccpStationBooted; import de.rwth.idsg.steve.service.notification.OcppStationStatusFailure; +import de.rwth.idsg.steve.service.notification.OcppStationStatusSuspendedEV; import de.rwth.idsg.steve.service.notification.OcppTransactionEnded; import de.rwth.idsg.steve.service.notification.OcppTransactionStarted; import jooq.steve.db.enums.TransactionStopEventActor; @@ -147,6 +148,11 @@ public StatusNotificationResponse statusNotification( chargeBoxIdentity, parameters.getConnectorId(), parameters.getErrorCode().value())); } + if (parameters.getStatus() == ChargePointStatus.SUSPENDED_EV) { + applicationEventPublisher.publishEvent(new OcppStationStatusSuspendedEV( + chargeBoxIdentity, parameters.getConnectorId())); //, connPk)); + } + return new StatusNotificationResponse(); } From df0b5c4fc53dc2d998f6586cb917f2e11be4ce9a Mon Sep 17 00:00:00 2001 From: fnkbsi Date: Wed, 2 Aug 2023 21:22:28 +0200 Subject: [PATCH 09/39] OcppServerRepository: add method getConnectorPK --- .../rwth/idsg/steve/repository/OcppServerRepository.java | 1 + .../steve/repository/impl/OcppServerRepositoryImpl.java | 9 +++++++++ 2 files changed, 10 insertions(+) diff --git a/src/main/java/de/rwth/idsg/steve/repository/OcppServerRepository.java b/src/main/java/de/rwth/idsg/steve/repository/OcppServerRepository.java index 4d9d86f46..968bd5e9d 100644 --- a/src/main/java/de/rwth/idsg/steve/repository/OcppServerRepository.java +++ b/src/main/java/de/rwth/idsg/steve/repository/OcppServerRepository.java @@ -42,6 +42,7 @@ public interface OcppServerRepository { void updateChargeboxHeartbeat(String chargeBoxIdentity, DateTime ts); void insertConnectorStatus(InsertConnectorStatusParams params); + public Integer getConnectorPk(String chargeBoxId, int connectorId); void insertMeterValues(String chargeBoxIdentity, List list, int connectorId, Integer transactionId); void insertMeterValues(String chargeBoxIdentity, List list, int transactionId); diff --git a/src/main/java/de/rwth/idsg/steve/repository/impl/OcppServerRepositoryImpl.java b/src/main/java/de/rwth/idsg/steve/repository/impl/OcppServerRepositoryImpl.java index 8ec2542c8..651af9534 100644 --- a/src/main/java/de/rwth/idsg/steve/repository/impl/OcppServerRepositoryImpl.java +++ b/src/main/java/de/rwth/idsg/steve/repository/impl/OcppServerRepositoryImpl.java @@ -165,6 +165,15 @@ public void insertConnectorStatus(InsertConnectorStatusParams p) { }); } + @Override + public Integer getConnectorPk(String chargeBoxId, int connectorId){ + return ctx.select(CONNECTOR.CONNECTOR_PK) + .from(CONNECTOR) + .where(CONNECTOR.CHARGE_BOX_ID.equal(chargeBoxId)) + .and(CONNECTOR.CONNECTOR_ID.equal(connectorId)) + .fetchOne().value1(); + } + @Override public void insertMeterValues(String chargeBoxIdentity, List list, int connectorId, Integer transactionId) { if (CollectionUtils.isEmpty(list)) { From 7f73ecdb4056a859e2418a0295e313d2bbb48237 Mon Sep 17 00:00:00 2001 From: fnkbsi Date: Wed, 2 Aug 2023 21:25:43 +0200 Subject: [PATCH 10/39] TransactionRepository: add method getOcppTagOfActiveTransaction(integer connectorPK) --- .../idsg/steve/repository/TransactionRepository.java | 2 ++ .../steve/repository/impl/TransactionRepositoryImpl.java | 9 +++++++++ 2 files changed, 11 insertions(+) diff --git a/src/main/java/de/rwth/idsg/steve/repository/TransactionRepository.java b/src/main/java/de/rwth/idsg/steve/repository/TransactionRepository.java index 2efedb959..70e325968 100644 --- a/src/main/java/de/rwth/idsg/steve/repository/TransactionRepository.java +++ b/src/main/java/de/rwth/idsg/steve/repository/TransactionRepository.java @@ -38,6 +38,8 @@ public interface TransactionRepository { List getActiveTransactionIds(String chargeBoxId); + String getOcppTagOfActiveTransaction(Integer connectorPk); + TransactionDetails getDetails(int transactionPk, boolean firstArrivingMeterValueIfMultiple); default TransactionDetails getDetails(int transactionPk) { diff --git a/src/main/java/de/rwth/idsg/steve/repository/impl/TransactionRepositoryImpl.java b/src/main/java/de/rwth/idsg/steve/repository/impl/TransactionRepositoryImpl.java index 77a083133..a2402168b 100644 --- a/src/main/java/de/rwth/idsg/steve/repository/impl/TransactionRepositoryImpl.java +++ b/src/main/java/de/rwth/idsg/steve/repository/impl/TransactionRepositoryImpl.java @@ -98,6 +98,15 @@ public List getActiveTransactionIds(String chargeBoxId) { .fetch(TRANSACTION.TRANSACTION_PK); } + @Override + public String getOcppTagOfActiveTransaction(Integer connectorPk) { + return ctx.select(TRANSACTION.ID_TAG) + .from(TRANSACTION) + .where(TRANSACTION.CONNECTOR_PK.eq(connectorPk)) + .and(TRANSACTION.STOP_VALUE.isNull()) + .fetchAny(TRANSACTION.ID_TAG); + } + @Override public TransactionDetails getDetails(int transactionPk, boolean firstArrivingMeterValueIfMultiple) { From 4ca23a6e2814b4131da2280cb0ab640ac2fe2b2b Mon Sep 17 00:00:00 2001 From: fnkbsi <> Date: Wed, 2 Aug 2023 21:29:36 +0200 Subject: [PATCH 11/39] NotificationService: added Event ocppStationStatusSuspendedEV(OcppStationStatusSuspendedEV notification) to send a mail to the user --- .../steve/service/NotificationService.java | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/src/main/java/de/rwth/idsg/steve/service/NotificationService.java b/src/main/java/de/rwth/idsg/steve/service/NotificationService.java index 6368b3808..617166f8c 100644 --- a/src/main/java/de/rwth/idsg/steve/service/NotificationService.java +++ b/src/main/java/de/rwth/idsg/steve/service/NotificationService.java @@ -41,11 +41,14 @@ import static de.rwth.idsg.steve.NotificationFeature.OcppStationWebSocketDisconnected; import static de.rwth.idsg.steve.NotificationFeature.OcppTransactionStarted; import static de.rwth.idsg.steve.NotificationFeature.OcppTransactionEnded; +import de.rwth.idsg.steve.repository.OcppServerRepository; import de.rwth.idsg.steve.repository.TransactionRepository; import de.rwth.idsg.steve.repository.UserRepository; import de.rwth.idsg.steve.repository.dto.Transaction; +import de.rwth.idsg.steve.service.notification.OcppStationStatusSuspendedEV; import static java.lang.String.format; import jooq.steve.db.tables.records.UserRecord; +import org.springframework.scheduling.annotation.Async; /** * @author Sevket Goekay @@ -58,6 +61,7 @@ public class NotificationService { @Autowired private MailService mailService; @Autowired private TransactionRepository transactionRepository; @Autowired private UserRepository userRepository; + @Autowired private OcppServerRepository ocppServerRepository; @EventListener public void ocppStationBooted(OccpStationBooted notification) { @@ -122,6 +126,28 @@ public void ocppTransactionStarted(OcppTransactionStarted notification) { } @EventListener + @Async + public void ocppStationStatusSuspendedEV(OcppStationStatusSuspendedEV notification){ + // Connector_pk + Integer connectorPk = ocppServerRepository.getConnectorPk(notification.getChargeBoxId(), notification.getConnectorId()); + String ocppTag = transactionRepository.getOcppTagOfActiveTransaction(connectorPk); + if (ocppTag == null){return;} + UserRecord userRecord = userRepository.getDetails(ocppTag).getUserRecord(); + String eMailAddy = userRecord.getEMail(); //userRepository.getMailAddy(OCPP_TAG); + if (Strings.isNullOrEmpty(eMailAddy)) + {return;} + + String subject = format("EV stopped charging at charging station %s", notification.getChargeBoxId()); + + //String body = format("Connector '%s' of charging station '%s' notifies Suspended_EV", connectorId, chargeBoxId); + String body = "User: " + userRecord.getFirstName() + " " + userRecord.getLastName() + System.lineSeparator() + System.lineSeparator() + + "Connector " + notification.getConnectorId() + " of charging station " + notification.getChargeBoxId() + " notifies Suspended_EV"; + + mailService.sendAsync( subject, addTimestamp(body), eMailAddy); + } + + @EventListener + @Async public void ocppTransactionEnded(OcppTransactionEnded notification) { Transaction TransActParams = transactionRepository.getTransaction(notification.getParams().getTransactionId()); From 64707eeed3eafdd0a7cb54086c857f7610b9a78f Mon Sep 17 00:00:00 2001 From: fnkbsi <> Date: Fri, 4 Aug 2023 16:20:22 +0200 Subject: [PATCH 12/39] NotificationService & CentralSystemService16_Service: Format and Comment improvements --- .../CentralSystemService16_Service.java | 2 +- .../steve/service/NotificationService.java | 21 ++++++++++--------- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/src/main/java/de/rwth/idsg/steve/service/CentralSystemService16_Service.java b/src/main/java/de/rwth/idsg/steve/service/CentralSystemService16_Service.java index 09916db6e..6b60818a8 100644 --- a/src/main/java/de/rwth/idsg/steve/service/CentralSystemService16_Service.java +++ b/src/main/java/de/rwth/idsg/steve/service/CentralSystemService16_Service.java @@ -150,7 +150,7 @@ public StatusNotificationResponse statusNotification( if (parameters.getStatus() == ChargePointStatus.SUSPENDED_EV) { applicationEventPublisher.publishEvent(new OcppStationStatusSuspendedEV( - chargeBoxIdentity, parameters.getConnectorId())); //, connPk)); + chargeBoxIdentity, parameters.getConnectorId())); } return new StatusNotificationResponse(); diff --git a/src/main/java/de/rwth/idsg/steve/service/NotificationService.java b/src/main/java/de/rwth/idsg/steve/service/NotificationService.java index 617166f8c..f802d6109 100644 --- a/src/main/java/de/rwth/idsg/steve/service/NotificationService.java +++ b/src/main/java/de/rwth/idsg/steve/service/NotificationService.java @@ -128,18 +128,20 @@ public void ocppTransactionStarted(OcppTransactionStarted notification) { @EventListener @Async public void ocppStationStatusSuspendedEV(OcppStationStatusSuspendedEV notification){ - // Connector_pk Integer connectorPk = ocppServerRepository.getConnectorPk(notification.getChargeBoxId(), notification.getConnectorId()); String ocppTag = transactionRepository.getOcppTagOfActiveTransaction(connectorPk); - if (ocppTag == null){return;} + + if (ocppTag == null){ + return; + } + UserRecord userRecord = userRepository.getDetails(ocppTag).getUserRecord(); - String eMailAddy = userRecord.getEMail(); //userRepository.getMailAddy(OCPP_TAG); - if (Strings.isNullOrEmpty(eMailAddy)) - {return;} + String eMailAddy = userRecord.getEMail(); + if (Strings.isNullOrEmpty(eMailAddy)){ + return; + } String subject = format("EV stopped charging at charging station %s", notification.getChargeBoxId()); - - //String body = format("Connector '%s' of charging station '%s' notifies Suspended_EV", connectorId, chargeBoxId); String body = "User: " + userRecord.getFirstName() + " " + userRecord.getLastName() + System.lineSeparator() + System.lineSeparator() + "Connector " + notification.getConnectorId() + " of charging station " + notification.getChargeBoxId() + " notifies Suspended_EV"; @@ -159,7 +161,7 @@ public void ocppTransactionEnded(OcppTransactionEnded notification) { if (!Strings.isNullOrEmpty(eMailAddress)) { String subjectUserMail = format("Transaction '%s' has ended on charging station '%s'", TransActParams.getId(), TransActParams.getChargeBoxId()); - // if the Transactionstop is received within the first 1 Minute don't send an E-Mail + // if the Transactionstop is received within the first Minute don't send an E-Mail if (TransActParams.getStopTimestamp().isAfter(TransActParams.getStartTimestamp().plusMinutes(1))){ mailService.sendAsync(subjectUserMail, addTimestamp(createContent(TransActParams, userRecord)), eMailAddress); } @@ -179,7 +181,6 @@ public void ocppTransactionEnded(OcppTransactionEnded notification) { // Private helpers // ------------------------------------------------------------------------- - private static String createContent(InsertTransactionParams params) { StringBuilder sb = new StringBuilder("Details:").append(System.lineSeparator()) .append("- chargeBoxId: ").append(params.getChargeBoxId()).append(System.lineSeparator()) @@ -229,7 +230,7 @@ private static String createContent(Transaction params, UserRecord userRecord) { .append("Details:").append(System.lineSeparator()) .append("- chargeBoxId: ").append(params.getChargeBoxId()).append(System.lineSeparator()) .append("- connectorId: ").append(params.getConnectorId()).append(System.lineSeparator()) - .append("- transactionId: ").append(params.getId()).append(System.lineSeparator()) // getTransactionId() + .append("- transactionId: ").append(params.getId()).append(System.lineSeparator()) .append("- startTimestamp (UTC): ").append(params.getStartTimestamp()).append(System.lineSeparator()) .append("- startMeterValue: ").append(params.getStartValue()).append(System.lineSeparator()) .append("- stopTimestamp (UTC): ").append(params.getStopTimestamp()).append(System.lineSeparator()) From 00853ffdd7e8903a37eb7c7fcffe187ad1992a75 Mon Sep 17 00:00:00 2001 From: fnkbsi Date: Sat, 5 Aug 2023 14:25:31 +0200 Subject: [PATCH 13/39] NotificationService: added mail to "admin" in OcppStationStatusSuspendedEV --- .../de/rwth/idsg/steve/service/NotificationService.java | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/main/java/de/rwth/idsg/steve/service/NotificationService.java b/src/main/java/de/rwth/idsg/steve/service/NotificationService.java index f802d6109..6b289cc2a 100644 --- a/src/main/java/de/rwth/idsg/steve/service/NotificationService.java +++ b/src/main/java/de/rwth/idsg/steve/service/NotificationService.java @@ -28,6 +28,7 @@ import de.rwth.idsg.steve.service.notification.OcppStationWebSocketConnected; import de.rwth.idsg.steve.service.notification.OcppStationWebSocketDisconnected; import de.rwth.idsg.steve.service.notification.OcppTransactionEnded; +import de.rwth.idsg.steve.service.notification.OcppStationStatusSuspendedEV; import de.rwth.idsg.steve.service.notification.OcppTransactionStarted; import lombok.extern.slf4j.Slf4j; import org.joda.time.DateTime; @@ -40,12 +41,12 @@ import static de.rwth.idsg.steve.NotificationFeature.OcppStationWebSocketConnected; import static de.rwth.idsg.steve.NotificationFeature.OcppStationWebSocketDisconnected; import static de.rwth.idsg.steve.NotificationFeature.OcppTransactionStarted; +import static de.rwth.idsg.steve.NotificationFeature.OcppStationStatusSuspendedEV; import static de.rwth.idsg.steve.NotificationFeature.OcppTransactionEnded; import de.rwth.idsg.steve.repository.OcppServerRepository; import de.rwth.idsg.steve.repository.TransactionRepository; import de.rwth.idsg.steve.repository.UserRepository; import de.rwth.idsg.steve.repository.dto.Transaction; -import de.rwth.idsg.steve.service.notification.OcppStationStatusSuspendedEV; import static java.lang.String.format; import jooq.steve.db.tables.records.UserRecord; import org.springframework.scheduling.annotation.Async; @@ -146,6 +147,12 @@ public void ocppStationStatusSuspendedEV(OcppStationStatusSuspendedEV notificati + "Connector " + notification.getConnectorId() + " of charging station " + notification.getChargeBoxId() + " notifies Suspended_EV"; mailService.sendAsync( subject, addTimestamp(body), eMailAddy); + + /* mail defined in settings */ + if (isDisabled(OcppStationStatusSuspendedEV)) { + return; + } + mailService.sendAsync( subject, addTimestamp(body), ""); } @EventListener From bf113abd95939915909eadbe2716e3dd3d8fa74a Mon Sep 17 00:00:00 2001 From: fnkbsi Date: Sat, 5 Aug 2023 15:11:58 +0200 Subject: [PATCH 14/39] NotificationService: formated mail text in OcppStationStatusSuspendedEV, rearange the if clauses --- .../steve/service/NotificationService.java | 36 +++++++++++-------- 1 file changed, 22 insertions(+), 14 deletions(-) diff --git a/src/main/java/de/rwth/idsg/steve/service/NotificationService.java b/src/main/java/de/rwth/idsg/steve/service/NotificationService.java index 6b289cc2a..118e05923 100644 --- a/src/main/java/de/rwth/idsg/steve/service/NotificationService.java +++ b/src/main/java/de/rwth/idsg/steve/service/NotificationService.java @@ -132,26 +132,34 @@ public void ocppStationStatusSuspendedEV(OcppStationStatusSuspendedEV notificati Integer connectorPk = ocppServerRepository.getConnectorPk(notification.getChargeBoxId(), notification.getConnectorId()); String ocppTag = transactionRepository.getOcppTagOfActiveTransaction(connectorPk); - if (ocppTag == null){ - return; - } - - UserRecord userRecord = userRepository.getDetails(ocppTag).getUserRecord(); - String eMailAddy = userRecord.getEMail(); - if (Strings.isNullOrEmpty(eMailAddy)){ - return; + String subject = format("EV stopped charging at charging station %s, Connector %d", + notification.getChargeBoxId(), + notification.getConnectorId()); + if (ocppTag != null){ + + UserRecord userRecord = userRepository.getDetails(ocppTag).getUserRecord(); + String eMailAddy = userRecord.getEMail(); + if (!Strings.isNullOrEmpty(eMailAddy)){ + //String bodyUserMail = "User: " + userRecord.getFirstName() + " " + userRecord.getLastName() + System.lineSeparator() + System.lineSeparator() + // + "Connector " + notification.getConnectorId() + " of charging station " + notification.getChargeBoxId() + " notifies Suspended_EV"; + + String bodyUserMail = format("User: %s %s \n\n Connector %d of charging station %s notifies Suspended_EV", + userRecord.getFirstName(), + userRecord.getLastName(), + notification.getConnectorId(), + notification.getChargeBoxId()); + + mailService.sendAsync( subject, addTimestamp(bodyUserMail), eMailAddy); + } } - String subject = format("EV stopped charging at charging station %s", notification.getChargeBoxId()); - String body = "User: " + userRecord.getFirstName() + " " + userRecord.getLastName() + System.lineSeparator() + System.lineSeparator() - + "Connector " + notification.getConnectorId() + " of charging station " + notification.getChargeBoxId() + " notifies Suspended_EV"; - - mailService.sendAsync( subject, addTimestamp(body), eMailAddy); - /* mail defined in settings */ if (isDisabled(OcppStationStatusSuspendedEV)) { return; } + String body = format("Connector %d of charging station %s notifies Suspended_EV", + notification.getConnectorId(), + notification.getChargeBoxId()); mailService.sendAsync( subject, addTimestamp(body), ""); } From 43922964455b49de38e6cef32b508c1d26b7885d Mon Sep 17 00:00:00 2001 From: fnkbsi Date: Thu, 12 Oct 2023 12:28:44 +0200 Subject: [PATCH 15/39] NotificationService.java & OcppStationStatusSuspend: commeting and format changes --- .../steve/service/NotificationService.java | 34 +++++++++++++------ .../OcppStationStatusSuspendedEV.java | 2 +- 2 files changed, 24 insertions(+), 12 deletions(-) diff --git a/src/main/java/de/rwth/idsg/steve/service/NotificationService.java b/src/main/java/de/rwth/idsg/steve/service/NotificationService.java index 118e05923..ba646608e 100644 --- a/src/main/java/de/rwth/idsg/steve/service/NotificationService.java +++ b/src/main/java/de/rwth/idsg/steve/service/NotificationService.java @@ -109,7 +109,10 @@ public void ocppStationStatusFailure(OcppStationStatusFailure notification) { return; } - String subject = format("Connector '%s' of charging station '%s' is FAULTED", notification.getConnectorId(), notification.getChargeBoxId()); + String subject = format("Connector '%s' of charging station '%s' is FAULTED", + notification.getConnectorId(), + notification.getChargeBoxId() + ); String body = format("Status Error Code: '%s'", notification.getErrorCode()); mailService.sendAsync(subject, addTimestamp(body)); @@ -121,7 +124,11 @@ public void ocppTransactionStarted(OcppTransactionStarted notification) { return; } - String subject = format("Transaction '%s' has started on charging station '%s' on connector '%s'", notification.getTransactionId(), notification.getParams().getChargeBoxId(), notification.getParams().getConnectorId()); + String subject = format("Transaction '%s' has started on charging station '%s' on connector '%s'", + notification.getTransactionId(), + notification.getParams().getChargeBoxId(), + notification.getParams().getConnectorId() + ); mailService.sendAsync(subject, addTimestamp(createContent(notification.getParams()))); } @@ -139,15 +146,14 @@ public void ocppStationStatusSuspendedEV(OcppStationStatusSuspendedEV notificati UserRecord userRecord = userRepository.getDetails(ocppTag).getUserRecord(); String eMailAddy = userRecord.getEMail(); + // send email if user with eMail address found if (!Strings.isNullOrEmpty(eMailAddy)){ - //String bodyUserMail = "User: " + userRecord.getFirstName() + " " + userRecord.getLastName() + System.lineSeparator() + System.lineSeparator() - // + "Connector " + notification.getConnectorId() + " of charging station " + notification.getChargeBoxId() + " notifies Suspended_EV"; - String bodyUserMail = format("User: %s %s \n\n Connector %d of charging station %s notifies Suspended_EV", - userRecord.getFirstName(), - userRecord.getLastName(), - notification.getConnectorId(), - notification.getChargeBoxId()); + userRecord.getFirstName(), + userRecord.getLastName(), + notification.getConnectorId(), + notification.getChargeBoxId() + ); mailService.sendAsync( subject, addTimestamp(bodyUserMail), eMailAddy); } @@ -174,7 +180,10 @@ public void ocppTransactionEnded(OcppTransactionEnded notification) { // mail to user if (!Strings.isNullOrEmpty(eMailAddress)) { - String subjectUserMail = format("Transaction '%s' has ended on charging station '%s'", TransActParams.getId(), TransActParams.getChargeBoxId()); + String subjectUserMail = format("Transaction '%s' has ended on charging station '%s'", + TransActParams.getId(), + TransActParams.getChargeBoxId() + ); // if the Transactionstop is received within the first Minute don't send an E-Mail if (TransActParams.getStopTimestamp().isAfter(TransActParams.getStartTimestamp().plusMinutes(1))){ @@ -187,7 +196,10 @@ public void ocppTransactionEnded(OcppTransactionEnded notification) { return; } - String subject = format("Transaction '%s' has ended on charging station '%s'", notification.getParams().getTransactionId(), notification.getParams().getChargeBoxId()); + String subject = format("Transaction '%s' has ended on charging station '%s'", + notification.getParams().getTransactionId(), + notification.getParams().getChargeBoxId() + ); mailService.sendAsync(subject, addTimestamp(createContent(notification.getParams()))); } diff --git a/src/main/java/de/rwth/idsg/steve/service/notification/OcppStationStatusSuspendedEV.java b/src/main/java/de/rwth/idsg/steve/service/notification/OcppStationStatusSuspendedEV.java index ce6906feb..8a984a004 100644 --- a/src/main/java/de/rwth/idsg/steve/service/notification/OcppStationStatusSuspendedEV.java +++ b/src/main/java/de/rwth/idsg/steve/service/notification/OcppStationStatusSuspendedEV.java @@ -21,7 +21,7 @@ import lombok.Data; /** - * @author Frank Brosi + * @author fnkbsi * @since 12.10.2022 * */ From 8e44e3891f12ae3ce7c5941aab3ec00e3e4c8850 Mon Sep 17 00:00:00 2001 From: fnkbsi <> Date: Thu, 12 Oct 2023 13:54:17 +0200 Subject: [PATCH 16/39] style correction --- .../repository/OcppServerRepository.java | 2 +- .../repository/TransactionRepository.java | 2 +- .../impl/OcppServerRepositoryImpl.java | 4 +- .../impl/TransactionRepositoryImpl.java | 4 +- .../repository/impl/UserRepositoryImpl.java | 3 +- .../steve/service/NotificationService.java | 76 ++++++++++--------- .../OcppStationStatusSuspendedEV.java | 2 +- .../de/rwth/idsg/steve/utils/StringUtils.java | 11 ++- 8 files changed, 57 insertions(+), 47 deletions(-) diff --git a/src/main/java/de/rwth/idsg/steve/repository/OcppServerRepository.java b/src/main/java/de/rwth/idsg/steve/repository/OcppServerRepository.java index 968bd5e9d..e0f2e451c 100644 --- a/src/main/java/de/rwth/idsg/steve/repository/OcppServerRepository.java +++ b/src/main/java/de/rwth/idsg/steve/repository/OcppServerRepository.java @@ -42,7 +42,7 @@ public interface OcppServerRepository { void updateChargeboxHeartbeat(String chargeBoxIdentity, DateTime ts); void insertConnectorStatus(InsertConnectorStatusParams params); - public Integer getConnectorPk(String chargeBoxId, int connectorId); + Integer getConnectorPk(String chargeBoxId, int connectorId); void insertMeterValues(String chargeBoxIdentity, List list, int connectorId, Integer transactionId); void insertMeterValues(String chargeBoxIdentity, List list, int transactionId); diff --git a/src/main/java/de/rwth/idsg/steve/repository/TransactionRepository.java b/src/main/java/de/rwth/idsg/steve/repository/TransactionRepository.java index 70e325968..9375e46b5 100644 --- a/src/main/java/de/rwth/idsg/steve/repository/TransactionRepository.java +++ b/src/main/java/de/rwth/idsg/steve/repository/TransactionRepository.java @@ -30,7 +30,7 @@ * @since 19.08.2014 */ public interface TransactionRepository { - Transaction getTransaction(int transaction_pk); + Transaction getTransaction(int transactionPk); List getTransactions(TransactionQueryForm form); diff --git a/src/main/java/de/rwth/idsg/steve/repository/impl/OcppServerRepositoryImpl.java b/src/main/java/de/rwth/idsg/steve/repository/impl/OcppServerRepositoryImpl.java index 651af9534..3042475e9 100644 --- a/src/main/java/de/rwth/idsg/steve/repository/impl/OcppServerRepositoryImpl.java +++ b/src/main/java/de/rwth/idsg/steve/repository/impl/OcppServerRepositoryImpl.java @@ -166,12 +166,12 @@ public void insertConnectorStatus(InsertConnectorStatusParams p) { } @Override - public Integer getConnectorPk(String chargeBoxId, int connectorId){ + public Integer getConnectorPk(String chargeBoxId, int connectorId) { return ctx.select(CONNECTOR.CONNECTOR_PK) .from(CONNECTOR) .where(CONNECTOR.CHARGE_BOX_ID.equal(chargeBoxId)) .and(CONNECTOR.CONNECTOR_ID.equal(connectorId)) - .fetchOne().value1(); + .fetchOne().value1(); } @Override diff --git a/src/main/java/de/rwth/idsg/steve/repository/impl/TransactionRepositoryImpl.java b/src/main/java/de/rwth/idsg/steve/repository/impl/TransactionRepositoryImpl.java index a2402168b..badf6d71d 100644 --- a/src/main/java/de/rwth/idsg/steve/repository/impl/TransactionRepositoryImpl.java +++ b/src/main/java/de/rwth/idsg/steve/repository/impl/TransactionRepositoryImpl.java @@ -66,9 +66,9 @@ public TransactionRepositoryImpl(DSLContext ctx) { } @Override - public Transaction getTransaction(int transaction_pk) { + public Transaction getTransaction(int transactionPk) { TransactionQueryForm form = new TransactionQueryForm(); - form.setTransactionPk(transaction_pk); + form.setTransactionPk(transactionPk); form.setReturnCSV(false); form.setType(TransactionQueryForm.QueryType.ALL); return getInternal(form).fetch() diff --git a/src/main/java/de/rwth/idsg/steve/repository/impl/UserRepositoryImpl.java b/src/main/java/de/rwth/idsg/steve/repository/impl/UserRepositoryImpl.java index cafe551fa..dd255b707 100644 --- a/src/main/java/de/rwth/idsg/steve/repository/impl/UserRepositoryImpl.java +++ b/src/main/java/de/rwth/idsg/steve/repository/impl/UserRepositoryImpl.java @@ -118,12 +118,11 @@ public User.Details getDetails(int userPk) { @Override public User.Details getDetails(String ocppIdTag) { - Integer ocppPk = ctx.select(OCPP_TAG.OCPP_TAG_PK) .from(OCPP_TAG) .where(OCPP_TAG.ID_TAG.eq(ocppIdTag)) .fetchOne(OCPP_TAG.OCPP_TAG_PK); - + if (ocppPk == null) { throw new SteveException("There is no OCPP_Tag: '%s'", ocppIdTag); } diff --git a/src/main/java/de/rwth/idsg/steve/service/NotificationService.java b/src/main/java/de/rwth/idsg/steve/service/NotificationService.java index ba646608e..0cd4fb4b5 100644 --- a/src/main/java/de/rwth/idsg/steve/service/NotificationService.java +++ b/src/main/java/de/rwth/idsg/steve/service/NotificationService.java @@ -109,7 +109,7 @@ public void ocppStationStatusFailure(OcppStationStatusFailure notification) { return; } - String subject = format("Connector '%s' of charging station '%s' is FAULTED", + String subject = format("Connector '%s' of charging station '%s' is FAULTED", notification.getConnectorId(), notification.getChargeBoxId() ); @@ -135,27 +135,31 @@ public void ocppTransactionStarted(OcppTransactionStarted notification) { @EventListener @Async - public void ocppStationStatusSuspendedEV(OcppStationStatusSuspendedEV notification){ - Integer connectorPk = ocppServerRepository.getConnectorPk(notification.getChargeBoxId(), notification.getConnectorId()); + public void ocppStationStatusSuspendedEV(OcppStationStatusSuspendedEV notification) { + Integer connectorPk = ocppServerRepository.getConnectorPk(notification.getChargeBoxId(), + notification.getConnectorId() + ); String ocppTag = transactionRepository.getOcppTagOfActiveTransaction(connectorPk); - String subject = format("EV stopped charging at charging station %s, Connector %d", - notification.getChargeBoxId(), - notification.getConnectorId()); + String subject = format("EV stopped charging at charging station %s, Connector %d", + notification.getChargeBoxId(), + notification.getConnectorId() + ); if (ocppTag != null){ UserRecord userRecord = userRepository.getDetails(ocppTag).getUserRecord(); String eMailAddy = userRecord.getEMail(); // send email if user with eMail address found - if (!Strings.isNullOrEmpty(eMailAddy)){ - String bodyUserMail = format("User: %s %s \n\n Connector %d of charging station %s notifies Suspended_EV", - userRecord.getFirstName(), - userRecord.getLastName(), - notification.getConnectorId(), - notification.getChargeBoxId() - ); - - mailService.sendAsync( subject, addTimestamp(bodyUserMail), eMailAddy); + if (!Strings.isNullOrEmpty(eMailAddy)) { + String bodyUserMail = + format("User: %s %s \n\n Connector %d of charging station %s notifies Suspended_EV", + userRecord.getFirstName(), + userRecord.getLastName(), + notification.getConnectorId(), + notification.getChargeBoxId() + ); + + mailService.sendAsync(subject, addTimestamp(bodyUserMail), eMailAddy); } } @@ -163,31 +167,35 @@ public void ocppStationStatusSuspendedEV(OcppStationStatusSuspendedEV notificati if (isDisabled(OcppStationStatusSuspendedEV)) { return; } - String body = format("Connector %d of charging station %s notifies Suspended_EV", + String body = format("Connector %d of charging station %s notifies Suspended_EV", notification.getConnectorId(), - notification.getChargeBoxId()); - mailService.sendAsync( subject, addTimestamp(body), ""); + notification.getChargeBoxId() + ); + mailService.sendAsync(subject, addTimestamp(body), ""); } @EventListener @Async public void ocppTransactionEnded(OcppTransactionEnded notification) { - Transaction TransActParams = transactionRepository.getTransaction(notification.getParams().getTransactionId()); + Transaction transActParams = transactionRepository.getTransaction(notification.getParams().getTransactionId()); - TransActParams.getOcppTagPk(); - UserRecord userRecord = userRepository.getDetails(TransActParams.getOcppIdTag()).getUserRecord(); + transActParams.getOcppTagPk(); + UserRecord userRecord = userRepository.getDetails(transActParams.getOcppIdTag()).getUserRecord(); String eMailAddress = userRecord.getEMail(); // mail to user if (!Strings.isNullOrEmpty(eMailAddress)) { - String subjectUserMail = format("Transaction '%s' has ended on charging station '%s'", - TransActParams.getId(), - TransActParams.getChargeBoxId() + String subjectUserMail = format("Transaction '%s' has ended on charging station '%s'", + transActParams.getId(), + transActParams.getChargeBoxId() ); // if the Transactionstop is received within the first Minute don't send an E-Mail - if (TransActParams.getStopTimestamp().isAfter(TransActParams.getStartTimestamp().plusMinutes(1))){ - mailService.sendAsync(subjectUserMail, addTimestamp(createContent(TransActParams, userRecord)), eMailAddress); + if (transActParams.getStopTimestamp().isAfter(transActParams.getStartTimestamp().plusMinutes(1))) { + mailService.sendAsync(subjectUserMail, + addTimestamp(createContent(transActParams, userRecord)), + eMailAddress + ); } } @@ -233,20 +241,18 @@ private static String createContent(UpdateTransactionParams params) { .toString(); } -private static String createContent(Transaction params, UserRecord userRecord) { + private static String createContent(Transaction params, UserRecord userRecord) { Double meterValueDiff; Integer meterValueStop; Integer meterValueStart; - String str_meterValueDiff = "-"; - try - { + String strMeterValueDiff = "-"; + try { meterValueStop = Integer.valueOf(params.getStopValue()); meterValueStart = Integer.valueOf(params.getStartValue()); - meterValueDiff = (meterValueStop - meterValueStart)/1000.0; // --> kWh - str_meterValueDiff = meterValueDiff.toString() + " kWh"; + meterValueDiff = (meterValueStop - meterValueStart) / 1000.0; // --> kWh + strMeterValueDiff = meterValueDiff.toString() + " kWh"; } - catch(NumberFormatException e) - { + catch(NumberFormatException e) { log.error("Failed to calculate charged energy! ", e); } @@ -263,7 +269,7 @@ private static String createContent(Transaction params, UserRecord userRecord) { .append("- stopTimestamp (UTC): ").append(params.getStopTimestamp()).append(System.lineSeparator()) .append("- stopMeterValue: ").append(params.getStopValue()).append(System.lineSeparator()) .append("- stopReason: ").append(params.getStopReason()).append(System.lineSeparator()) - .append("- charged energy: ").append(str_meterValueDiff).append(System.lineSeparator()) + .append("- charged energy: ").append(strMeterValueDiff).append(System.lineSeparator()) .toString(); } diff --git a/src/main/java/de/rwth/idsg/steve/service/notification/OcppStationStatusSuspendedEV.java b/src/main/java/de/rwth/idsg/steve/service/notification/OcppStationStatusSuspendedEV.java index 8a984a004..487380206 100644 --- a/src/main/java/de/rwth/idsg/steve/service/notification/OcppStationStatusSuspendedEV.java +++ b/src/main/java/de/rwth/idsg/steve/service/notification/OcppStationStatusSuspendedEV.java @@ -23,8 +23,8 @@ /** * @author fnkbsi * @since 12.10.2022 - * */ + @Data public class OcppStationStatusSuspendedEV { diff --git a/src/main/java/de/rwth/idsg/steve/utils/StringUtils.java b/src/main/java/de/rwth/idsg/steve/utils/StringUtils.java index 979ad0884..fe3d38496 100644 --- a/src/main/java/de/rwth/idsg/steve/utils/StringUtils.java +++ b/src/main/java/de/rwth/idsg/steve/utils/StringUtils.java @@ -99,9 +99,14 @@ public static String getLastBitFromUrl(final String input) { // https://www.baeldung.com/java-email-validation-regex public static boolean isValidAddress(String emailAddress) { - String regexPattern = "^(?=.{1,64}@)[A-Za-z0-9_-]+(\\.[A-Za-z0-9_-]+)*@[^-][A-Za-z0-9-]+(\\.[A-Za-z0-9-]+)*(\\.[A-Za-z]{2,})$"; // Strict Regular Expression Validation - //String regexPattern = "^(?=.{1,64}@)[\\p{L}0-9_-]+(\\.[\\p{L}0-9_-]+)*@[^-][\\p{L}0-9-]+(\\.[\\p{L}0-9-]+)*(\\.[\\p{L}]{2,})$"; //Regular Expression for Validation of Non-Latin or Unicode Characters Email - //String regexPattern = "^[a-zA-Z0-9_!#$%&'*+/=?`{|}~^.-]+@[a-zA-Z0-9.-]+$" // Regular Expression by RFC 5322 for Email Validation + // Strict Regular Expression Validation + String regexPattern = + "^(?=.{1,64}@)[A-Za-z0-9_-]+(\\.[A-Za-z0-9_-]+)*@[^-][A-Za-z0-9-]+(\\.[A-Za-z0-9-]+)*(\\.[A-Za-z]{2,})$"; + //Regular Expression for Validation of Non-Latin or Unicode Characters Email + //String regexPattern = + // "^(?=.{1,64}@)[\\p{L}0-9_-]+(\\.[\\p{L}0-9_-]+)*@[^-][\\p{L}0-9-]+(\\.[\\p{L}0-9-]+)*(\\.[\\p{L}]{2,})$"; + // Regular Expression by RFC 5322 for Email Validation + //String regexPattern = "^[a-zA-Z0-9_!#$%&'*+/=?`{|}~^.-]+@[a-zA-Z0-9.-]+$" return Pattern.compile(regexPattern) .matcher(emailAddress) .matches(); From 472b75dea6ae921bd718157103e6db09e2208d3e Mon Sep 17 00:00:00 2001 From: fnkbsi <> Date: Tue, 17 Oct 2023 13:58:04 +0200 Subject: [PATCH 17/39] style correction --- .../rwth/idsg/steve/service/MailService.java | 50 ++++++++----------- .../steve/service/NotificationService.java | 16 +++--- 2 files changed, 29 insertions(+), 37 deletions(-) diff --git a/src/main/java/de/rwth/idsg/steve/service/MailService.java b/src/main/java/de/rwth/idsg/steve/service/MailService.java index 6cc9876ce..864dc0f1a 100644 --- a/src/main/java/de/rwth/idsg/steve/service/MailService.java +++ b/src/main/java/de/rwth/idsg/steve/service/MailService.java @@ -84,7 +84,7 @@ public MailSettings getSettings() { public void sendTestMail() { try { - send("Test", "Test",""); + send("Test", "Test", ""); } catch (MessagingException e) { throw new SteveException("Failed to send mail", e); } @@ -100,51 +100,45 @@ public void sendAsync(String subject, String body) { }); } - public void sendAsync(String subject, String body, String RecipientAddresses) { + public void sendAsync(String subject, String body, String recipientAddresses) { executorService.execute(() -> { try { - send(subject, body, RecipientAddresses); + send(subject, body, recipientAddresses); } catch (MessagingException e) { log.error("Failed to send mail", e); } }); } - private void send(String subject, String body, String RecipientAddresses) throws MessagingException { - MailSettings settingsLocal = getSettings(); + private void send(String subject, String body, String recipientAddresses) throws MessagingException { + MailSettings settingsLocal = getSettings(); - Message mail = new MimeMessage(session); - mail.setSubject("[SteVe] " + subject); - mail.setContent(body, "text/plain"); - mail.setFrom(new InternetAddress(settingsLocal.getFrom())); + Message mail = new MimeMessage(session); + mail.setSubject("[SteVe] " + subject); + mail.setContent(body, "text/plain"); + mail.setFrom(new InternetAddress(settingsLocal.getFrom())); - List eMailAddresses; + List eMailAddresses; - if (RecipientAddresses.isEmpty()) - { - eMailAddresses = settingsLocal.getRecipients(); - } - else - { - - eMailAddresses = splitByComma(RecipientAddresses); - } - - for (String rep : eMailAddresses) { - if (isValidAddress(rep)){ - mail.addRecipient(Message.RecipientType.TO, new InternetAddress(rep)); + if (recipientAddresses.isEmpty()) { + eMailAddresses = settingsLocal.getRecipients(); + } else { + eMailAddresses = splitByComma(recipientAddresses); } - else{ - log.error("Failed to send mail to " + rep + "! Format of the address is invalid."); + + for (String rep : eMailAddresses) { + if (isValidAddress(rep)) { + mail.addRecipient(Message.RecipientType.TO, new InternetAddress(rep)); + } else { + log.error("Failed to send mail to " + rep + "! Format of the address is invalid."); + } } - } try (Transport transport = session.getTransport()) { transport.connect(); transport.sendMessage(mail, mail.getAllRecipients()); } - catch(Exception e) - { + catch (Exception e) { log.error("Failed to send mail(s)! ", e); } } diff --git a/src/main/java/de/rwth/idsg/steve/service/NotificationService.java b/src/main/java/de/rwth/idsg/steve/service/NotificationService.java index 0cd4fb4b5..faba8a2d5 100644 --- a/src/main/java/de/rwth/idsg/steve/service/NotificationService.java +++ b/src/main/java/de/rwth/idsg/steve/service/NotificationService.java @@ -136,7 +136,7 @@ public void ocppTransactionStarted(OcppTransactionStarted notification) { @EventListener @Async public void ocppStationStatusSuspendedEV(OcppStationStatusSuspendedEV notification) { - Integer connectorPk = ocppServerRepository.getConnectorPk(notification.getChargeBoxId(), + Integer connectorPk = ocppServerRepository.getConnectorPk(notification.getChargeBoxId(), notification.getConnectorId() ); String ocppTag = transactionRepository.getOcppTagOfActiveTransaction(connectorPk); @@ -145,13 +145,12 @@ public void ocppStationStatusSuspendedEV(OcppStationStatusSuspendedEV notificati notification.getChargeBoxId(), notification.getConnectorId() ); - if (ocppTag != null){ - + if (ocppTag != null) { UserRecord userRecord = userRepository.getDetails(ocppTag).getUserRecord(); String eMailAddy = userRecord.getEMail(); // send email if user with eMail address found if (!Strings.isNullOrEmpty(eMailAddy)) { - String bodyUserMail = + String bodyUserMail = format("User: %s %s \n\n Connector %d of charging station %s notifies Suspended_EV", userRecord.getFirstName(), userRecord.getLastName(), @@ -163,7 +162,7 @@ public void ocppStationStatusSuspendedEV(OcppStationStatusSuspendedEV notificati } } - /* mail defined in settings */ + /* mail defined in settings */ if (isDisabled(OcppStationStatusSuspendedEV)) { return; } @@ -199,7 +198,7 @@ public void ocppTransactionEnded(OcppTransactionEnded notification) { } } - /* mail defined in settings */ + /* mail defined in settings */ if (isDisabled(OcppTransactionEnded)) { return; } @@ -251,11 +250,10 @@ private static String createContent(Transaction params, UserRecord userRecord) { meterValueStart = Integer.valueOf(params.getStartValue()); meterValueDiff = (meterValueStop - meterValueStart) / 1000.0; // --> kWh strMeterValueDiff = meterValueDiff.toString() + " kWh"; - } - catch(NumberFormatException e) { + } catch (NumberFormatException e) { log.error("Failed to calculate charged energy! ", e); } - + return new StringBuilder("User: ") .append(userRecord.getFirstName()).append(" ").append(userRecord.getLastName()) .append(System.lineSeparator()) From 953abb4937af129aab2ddea9cfc6581baf87bcc6 Mon Sep 17 00:00:00 2001 From: fnkbsi <> Date: Sat, 23 Dec 2023 21:33:59 +0100 Subject: [PATCH 18/39] TransactionRepositoryImpl, method getOcppTagOfActiveTransaction: order by transactionPk desc, to avoid fetching ghost transactions --- .../idsg/steve/repository/impl/TransactionRepositoryImpl.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/de/rwth/idsg/steve/repository/impl/TransactionRepositoryImpl.java b/src/main/java/de/rwth/idsg/steve/repository/impl/TransactionRepositoryImpl.java index badf6d71d..4d344b99f 100644 --- a/src/main/java/de/rwth/idsg/steve/repository/impl/TransactionRepositoryImpl.java +++ b/src/main/java/de/rwth/idsg/steve/repository/impl/TransactionRepositoryImpl.java @@ -104,6 +104,7 @@ public String getOcppTagOfActiveTransaction(Integer connectorPk) { .from(TRANSACTION) .where(TRANSACTION.CONNECTOR_PK.eq(connectorPk)) .and(TRANSACTION.STOP_VALUE.isNull()) + .orderBy(TRANSACTION.TRANSACTION_PK.desc()) // to avoid fetching ghost transactions, fetch the latest .fetchAny(TRANSACTION.ID_TAG); } From 48d7a68a929a1ccca8cf7a2649f9688ad43cb423 Mon Sep 17 00:00:00 2001 From: fnkbsi <> Date: Sat, 23 Dec 2023 21:38:41 +0100 Subject: [PATCH 19/39] NotificationService, SuspendedEV & TransactionStop: catch exception in case of no user is found by the OcppTag --- .../steve/service/NotificationService.java | 27 ++++++++++++++----- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/src/main/java/de/rwth/idsg/steve/service/NotificationService.java b/src/main/java/de/rwth/idsg/steve/service/NotificationService.java index faba8a2d5..6466242a5 100644 --- a/src/main/java/de/rwth/idsg/steve/service/NotificationService.java +++ b/src/main/java/de/rwth/idsg/steve/service/NotificationService.java @@ -146,10 +146,16 @@ public void ocppStationStatusSuspendedEV(OcppStationStatusSuspendedEV notificati notification.getConnectorId() ); if (ocppTag != null) { - UserRecord userRecord = userRepository.getDetails(ocppTag).getUserRecord(); - String eMailAddy = userRecord.getEMail(); + String eMailAddress = null; + UserRecord userRecord = new UserRecord(); + try { + userRecord = userRepository.getDetails(ocppTag).getUserRecord(); + eMailAddress = userRecord.getEMail(); + } catch (Exception e) { + log.error("Failed to send email (SuspendedEV). User not found! ", e); + } // send email if user with eMail address found - if (!Strings.isNullOrEmpty(eMailAddy)) { + if (!Strings.isNullOrEmpty(eMailAddress)) { String bodyUserMail = format("User: %s %s \n\n Connector %d of charging station %s notifies Suspended_EV", userRecord.getFirstName(), @@ -158,7 +164,7 @@ public void ocppStationStatusSuspendedEV(OcppStationStatusSuspendedEV notificati notification.getChargeBoxId() ); - mailService.sendAsync(subject, addTimestamp(bodyUserMail), eMailAddy); + mailService.sendAsync(subject, addTimestamp(bodyUserMail), eMailAddress); } } @@ -170,17 +176,24 @@ public void ocppStationStatusSuspendedEV(OcppStationStatusSuspendedEV notificati notification.getConnectorId(), notification.getChargeBoxId() ); - mailService.sendAsync(subject, addTimestamp(body), ""); + mailService.sendAsync(subject, addTimestamp(body)); } @EventListener @Async public void ocppTransactionEnded(OcppTransactionEnded notification) { + String eMailAddress = null; + UserRecord userRecord = new UserRecord(); + Transaction transActParams = transactionRepository.getTransaction(notification.getParams().getTransactionId()); transActParams.getOcppTagPk(); - UserRecord userRecord = userRepository.getDetails(transActParams.getOcppIdTag()).getUserRecord(); - String eMailAddress = userRecord.getEMail(); + try { + userRecord = userRepository.getDetails(transActParams.getOcppIdTag()).getUserRecord(); + eMailAddress = userRecord.getEMail(); + } catch (Exception e) { + log.error("Failed to send email (TransactionStop). User not found! ", e); + } // mail to user if (!Strings.isNullOrEmpty(eMailAddress)) { From 6a2a70234ed6ed148666ff2cb18c2b554c19ae44 Mon Sep 17 00:00:00 2001 From: fnkbsi <> Date: Mon, 25 Dec 2023 18:44:04 +0100 Subject: [PATCH 20/39] Removed unnecessary code --- .../java/de/rwth/idsg/steve/service/NotificationService.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/de/rwth/idsg/steve/service/NotificationService.java b/src/main/java/de/rwth/idsg/steve/service/NotificationService.java index 6466242a5..bb9b82401 100644 --- a/src/main/java/de/rwth/idsg/steve/service/NotificationService.java +++ b/src/main/java/de/rwth/idsg/steve/service/NotificationService.java @@ -187,7 +187,6 @@ public void ocppTransactionEnded(OcppTransactionEnded notification) { Transaction transActParams = transactionRepository.getTransaction(notification.getParams().getTransactionId()); - transActParams.getOcppTagPk(); try { userRecord = userRepository.getDetails(transActParams.getOcppIdTag()).getUserRecord(); eMailAddress = userRecord.getEMail(); From eb64190779ba198307ac48d031d0688ee8d7caf8 Mon Sep 17 00:00:00 2001 From: fnkbsi <> Date: Mon, 25 Dec 2023 22:34:43 +0100 Subject: [PATCH 21/39] NotificationService: shorten the error log message (no stack info) --- .../de/rwth/idsg/steve/service/NotificationService.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/de/rwth/idsg/steve/service/NotificationService.java b/src/main/java/de/rwth/idsg/steve/service/NotificationService.java index bb9b82401..0f9289e7b 100644 --- a/src/main/java/de/rwth/idsg/steve/service/NotificationService.java +++ b/src/main/java/de/rwth/idsg/steve/service/NotificationService.java @@ -152,7 +152,7 @@ public void ocppStationStatusSuspendedEV(OcppStationStatusSuspendedEV notificati userRecord = userRepository.getDetails(ocppTag).getUserRecord(); eMailAddress = userRecord.getEMail(); } catch (Exception e) { - log.error("Failed to send email (SuspendedEV). User not found! ", e); + log.error("Failed to send email (SuspendedEV). User not found! " + e.getMessage()); } // send email if user with eMail address found if (!Strings.isNullOrEmpty(eMailAddress)) { @@ -184,14 +184,14 @@ public void ocppStationStatusSuspendedEV(OcppStationStatusSuspendedEV notificati public void ocppTransactionEnded(OcppTransactionEnded notification) { String eMailAddress = null; UserRecord userRecord = new UserRecord(); - + Transaction transActParams = transactionRepository.getTransaction(notification.getParams().getTransactionId()); try { userRecord = userRepository.getDetails(transActParams.getOcppIdTag()).getUserRecord(); eMailAddress = userRecord.getEMail(); } catch (Exception e) { - log.error("Failed to send email (TransactionStop). User not found! ", e); + log.error("Failed to send email (TransactionStop). User not found! " + e.getMessage()); } // mail to user From 102ea169d594c5d168d5d727529ce4ad23a44065 Mon Sep 17 00:00:00 2001 From: fnkbsi <> Date: Mon, 25 Dec 2023 22:41:42 +0100 Subject: [PATCH 22/39] BeanConfiguration: add @EnableAsync to activate @Async in NotificationService --- src/main/java/de/rwth/idsg/steve/config/BeanConfiguration.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/java/de/rwth/idsg/steve/config/BeanConfiguration.java b/src/main/java/de/rwth/idsg/steve/config/BeanConfiguration.java index 234c2caad..dc50ff9cc 100644 --- a/src/main/java/de/rwth/idsg/steve/config/BeanConfiguration.java +++ b/src/main/java/de/rwth/idsg/steve/config/BeanConfiguration.java @@ -48,6 +48,7 @@ import org.springframework.http.converter.HttpMessageConverter; import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; import org.springframework.scheduling.annotation.EnableScheduling; +import org.springframework.scheduling.annotation.EnableAsync; import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean; import org.springframework.web.accept.ContentNegotiationManager; import org.springframework.web.servlet.config.annotation.EnableWebMvc; @@ -79,6 +80,7 @@ @Configuration @EnableWebMvc @EnableScheduling +@EnableAsync @ComponentScan("de.rwth.idsg.steve") public class BeanConfiguration implements WebMvcConfigurer { From 1798ba913fa4ca2b6fd3ee6922f5d629be688116 Mon Sep 17 00:00:00 2001 From: fnkbsi Date: Tue, 26 Dec 2023 01:18:41 +0100 Subject: [PATCH 23/39] BeanConfiguration: removed @EnableAsync because it's notcompiling under Java 17 --- src/main/java/de/rwth/idsg/steve/config/BeanConfiguration.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/main/java/de/rwth/idsg/steve/config/BeanConfiguration.java b/src/main/java/de/rwth/idsg/steve/config/BeanConfiguration.java index dc50ff9cc..234c2caad 100644 --- a/src/main/java/de/rwth/idsg/steve/config/BeanConfiguration.java +++ b/src/main/java/de/rwth/idsg/steve/config/BeanConfiguration.java @@ -48,7 +48,6 @@ import org.springframework.http.converter.HttpMessageConverter; import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; import org.springframework.scheduling.annotation.EnableScheduling; -import org.springframework.scheduling.annotation.EnableAsync; import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean; import org.springframework.web.accept.ContentNegotiationManager; import org.springframework.web.servlet.config.annotation.EnableWebMvc; @@ -80,7 +79,6 @@ @Configuration @EnableWebMvc @EnableScheduling -@EnableAsync @ComponentScan("de.rwth.idsg.steve") public class BeanConfiguration implements WebMvcConfigurer { From 14ac662c7d939264029a4349c3714020b4322c50 Mon Sep 17 00:00:00 2001 From: fnkbsi Date: Tue, 26 Dec 2023 01:23:17 +0100 Subject: [PATCH 24/39] NotificationService: removing @Async annotations. Realizing async instead with ScheduledExecutorService, so response to the chargepoint is not blocked. --- .../steve/service/NotificationService.java | 25 ++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/src/main/java/de/rwth/idsg/steve/service/NotificationService.java b/src/main/java/de/rwth/idsg/steve/service/NotificationService.java index 0f9289e7b..0b44d4b70 100644 --- a/src/main/java/de/rwth/idsg/steve/service/NotificationService.java +++ b/src/main/java/de/rwth/idsg/steve/service/NotificationService.java @@ -49,7 +49,7 @@ import de.rwth.idsg.steve.repository.dto.Transaction; import static java.lang.String.format; import jooq.steve.db.tables.records.UserRecord; -import org.springframework.scheduling.annotation.Async; +import java.util.concurrent.ScheduledExecutorService; /** * @author Sevket Goekay @@ -63,6 +63,7 @@ public class NotificationService { @Autowired private TransactionRepository transactionRepository; @Autowired private UserRepository userRepository; @Autowired private OcppServerRepository ocppServerRepository; + @Autowired private ScheduledExecutorService executorService; @EventListener public void ocppStationBooted(OccpStationBooted notification) { @@ -134,8 +135,17 @@ public void ocppTransactionStarted(OcppTransactionStarted notification) { } @EventListener - @Async public void ocppStationStatusSuspendedEV(OcppStationStatusSuspendedEV notification) { + executorService.execute(() -> { + try { + notificationActionSuspendedEV(notification); + } catch (Exception e) { + log.error("Failed to execute the notification of SuspendedEV", e); + } + }); + } + + private void notificationActionSuspendedEV(OcppStationStatusSuspendedEV notification) { Integer connectorPk = ocppServerRepository.getConnectorPk(notification.getChargeBoxId(), notification.getConnectorId() ); @@ -180,8 +190,17 @@ public void ocppStationStatusSuspendedEV(OcppStationStatusSuspendedEV notificati } @EventListener - @Async public void ocppTransactionEnded(OcppTransactionEnded notification) { + executorService.execute(() -> { + try { + notificationActionTransactionEnded(notification); + } catch (Exception e) { + log.error("Failed to execute the notification of SuspendedEV", e); + } + }); + } + + private void notificationActionTransactionEnded(OcppTransactionEnded notification) { String eMailAddress = null; UserRecord userRecord = new UserRecord(); From 6a9091c2aea761de470b8bb85b151af4fbbe3dd0 Mon Sep 17 00:00:00 2001 From: fnkbsi Date: Tue, 23 Jan 2024 12:45:52 +0100 Subject: [PATCH 25/39] OcppServerRepository remove method getConnectorPk --- .../idsg/steve/repository/OcppServerRepository.java | 3 +-- .../repository/impl/OcppServerRepositoryImpl.java | 11 +---------- 2 files changed, 2 insertions(+), 12 deletions(-) diff --git a/src/main/java/de/rwth/idsg/steve/repository/OcppServerRepository.java b/src/main/java/de/rwth/idsg/steve/repository/OcppServerRepository.java index e0f2e451c..7bb7ece35 100644 --- a/src/main/java/de/rwth/idsg/steve/repository/OcppServerRepository.java +++ b/src/main/java/de/rwth/idsg/steve/repository/OcppServerRepository.java @@ -1,6 +1,6 @@ /* * SteVe - SteckdosenVerwaltung - https://github.com/steve-community/steve - * Copyright (C) 2013-2023 SteVe Community Team + * Copyright (C) 2013-2024 SteVe Community Team * All Rights Reserved. * * This program is free software: you can redistribute it and/or modify @@ -42,7 +42,6 @@ public interface OcppServerRepository { void updateChargeboxHeartbeat(String chargeBoxIdentity, DateTime ts); void insertConnectorStatus(InsertConnectorStatusParams params); - Integer getConnectorPk(String chargeBoxId, int connectorId); void insertMeterValues(String chargeBoxIdentity, List list, int connectorId, Integer transactionId); void insertMeterValues(String chargeBoxIdentity, List list, int transactionId); diff --git a/src/main/java/de/rwth/idsg/steve/repository/impl/OcppServerRepositoryImpl.java b/src/main/java/de/rwth/idsg/steve/repository/impl/OcppServerRepositoryImpl.java index c290261e8..14530fad1 100644 --- a/src/main/java/de/rwth/idsg/steve/repository/impl/OcppServerRepositoryImpl.java +++ b/src/main/java/de/rwth/idsg/steve/repository/impl/OcppServerRepositoryImpl.java @@ -1,6 +1,6 @@ /* * SteVe - SteckdosenVerwaltung - https://github.com/steve-community/steve - * Copyright (C) 2013-2023 SteVe Community Team + * Copyright (C) 2013-2024 SteVe Community Team * All Rights Reserved. * * This program is free software: you can redistribute it and/or modify @@ -165,15 +165,6 @@ public void insertConnectorStatus(InsertConnectorStatusParams p) { }); } - @Override - public Integer getConnectorPk(String chargeBoxId, int connectorId) { - return ctx.select(CONNECTOR.CONNECTOR_PK) - .from(CONNECTOR) - .where(CONNECTOR.CHARGE_BOX_ID.equal(chargeBoxId)) - .and(CONNECTOR.CONNECTOR_ID.equal(connectorId)) - .fetchOne().value1(); - } - @Override public void insertMeterValues(String chargeBoxIdentity, List list, int connectorId, Integer transactionId) { if (CollectionUtils.isEmpty(list)) { From 3d26aa225e2413d75f7973c443dcc17f934dcafd Mon Sep 17 00:00:00 2001 From: fnkbsi Date: Tue, 23 Jan 2024 12:54:22 +0100 Subject: [PATCH 26/39] TransactionRepository: add method getActiveTransactionId(String chagerBox, Integer connectorId); remove method getOcppTagOfActiveTransaction (Integer connectorPk) --- .../repository/TransactionRepository.java | 5 ++--- .../impl/TransactionRepositoryImpl.java | 18 ++++++++++-------- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/src/main/java/de/rwth/idsg/steve/repository/TransactionRepository.java b/src/main/java/de/rwth/idsg/steve/repository/TransactionRepository.java index 9375e46b5..835ce8665 100644 --- a/src/main/java/de/rwth/idsg/steve/repository/TransactionRepository.java +++ b/src/main/java/de/rwth/idsg/steve/repository/TransactionRepository.java @@ -1,6 +1,6 @@ /* * SteVe - SteckdosenVerwaltung - https://github.com/steve-community/steve - * Copyright (C) 2013-2023 SteVe Community Team + * Copyright (C) 2013-2024 SteVe Community Team * All Rights Reserved. * * This program is free software: you can redistribute it and/or modify @@ -37,8 +37,7 @@ public interface TransactionRepository { void writeTransactionsCSV(TransactionQueryForm form, Writer writer); List getActiveTransactionIds(String chargeBoxId); - - String getOcppTagOfActiveTransaction(Integer connectorPk); + Integer getActiveTransactionId(String chargeBoxId, Integer connectorId); TransactionDetails getDetails(int transactionPk, boolean firstArrivingMeterValueIfMultiple); diff --git a/src/main/java/de/rwth/idsg/steve/repository/impl/TransactionRepositoryImpl.java b/src/main/java/de/rwth/idsg/steve/repository/impl/TransactionRepositoryImpl.java index 4d344b99f..70ec7a739 100644 --- a/src/main/java/de/rwth/idsg/steve/repository/impl/TransactionRepositoryImpl.java +++ b/src/main/java/de/rwth/idsg/steve/repository/impl/TransactionRepositoryImpl.java @@ -1,6 +1,6 @@ /* * SteVe - SteckdosenVerwaltung - https://github.com/steve-community/steve - * Copyright (C) 2013-2023 SteVe Community Team + * Copyright (C) 2013-2024 SteVe Community Team * All Rights Reserved. * * This program is free software: you can redistribute it and/or modify @@ -99,13 +99,15 @@ public List getActiveTransactionIds(String chargeBoxId) { } @Override - public String getOcppTagOfActiveTransaction(Integer connectorPk) { - return ctx.select(TRANSACTION.ID_TAG) - .from(TRANSACTION) - .where(TRANSACTION.CONNECTOR_PK.eq(connectorPk)) - .and(TRANSACTION.STOP_VALUE.isNull()) - .orderBy(TRANSACTION.TRANSACTION_PK.desc()) // to avoid fetching ghost transactions, fetch the latest - .fetchAny(TRANSACTION.ID_TAG); + public Integer getActiveTransactionId(String chargeBoxId, Integer connectorId) { + return ctx.select(TRANSACTION.TRANSACTION_PK) + .from(TRANSACTION) + .join(CONNECTOR) + .on(TRANSACTION.CONNECTOR_PK.equal(CONNECTOR.CONNECTOR_PK)) + .and(CONNECTOR.CHARGE_BOX_ID.equal(chargeBoxId)) + .where(TRANSACTION.STOP_TIMESTAMP.isNull()) + .and(CONNECTOR.CONNECTOR_ID.equal(connectorId)) + .fetchAny(TRANSACTION.TRANSACTION_PK); } @Override From 4c66f000db23e700a8c9b94f0b630f4e6c76f8c1 Mon Sep 17 00:00:00 2001 From: fnkbsi Date: Tue, 23 Jan 2024 12:56:59 +0100 Subject: [PATCH 27/39] add timestamp to SUSPENDED_EV notification --- .../idsg/steve/service/CentralSystemService16_Service.java | 4 ++-- .../service/notification/OcppStationStatusSuspendedEV.java | 4 +++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/main/java/de/rwth/idsg/steve/service/CentralSystemService16_Service.java b/src/main/java/de/rwth/idsg/steve/service/CentralSystemService16_Service.java index 6b60818a8..e337a9883 100644 --- a/src/main/java/de/rwth/idsg/steve/service/CentralSystemService16_Service.java +++ b/src/main/java/de/rwth/idsg/steve/service/CentralSystemService16_Service.java @@ -1,6 +1,6 @@ /* * SteVe - SteckdosenVerwaltung - https://github.com/steve-community/steve - * Copyright (C) 2013-2023 SteVe Community Team + * Copyright (C) 2013-2024 SteVe Community Team * All Rights Reserved. * * This program is free software: you can redistribute it and/or modify @@ -150,7 +150,7 @@ public StatusNotificationResponse statusNotification( if (parameters.getStatus() == ChargePointStatus.SUSPENDED_EV) { applicationEventPublisher.publishEvent(new OcppStationStatusSuspendedEV( - chargeBoxIdentity, parameters.getConnectorId())); + chargeBoxIdentity, parameters.getConnectorId(), parameters.getTimestamp())); } return new StatusNotificationResponse(); diff --git a/src/main/java/de/rwth/idsg/steve/service/notification/OcppStationStatusSuspendedEV.java b/src/main/java/de/rwth/idsg/steve/service/notification/OcppStationStatusSuspendedEV.java index 487380206..fed6cbdd0 100644 --- a/src/main/java/de/rwth/idsg/steve/service/notification/OcppStationStatusSuspendedEV.java +++ b/src/main/java/de/rwth/idsg/steve/service/notification/OcppStationStatusSuspendedEV.java @@ -1,6 +1,6 @@ /* * SteVe - SteckdosenVerwaltung - https://github.com/steve-community/steve - * Copyright (C) 2013-2023 SteVe Community Team + * Copyright (C) 2013-2024 SteVe Community Team * All Rights Reserved. * * This program is free software: you can redistribute it and/or modify @@ -19,6 +19,7 @@ package de.rwth.idsg.steve.service.notification; import lombok.Data; +import org.joda.time.DateTime; /** * @author fnkbsi @@ -30,4 +31,5 @@ public class OcppStationStatusSuspendedEV { private final String chargeBoxId; private final int connectorId; + private final DateTime timestamp; } From 4d2963abfc8c3820fb0e4aeede241b4106033c75 Mon Sep 17 00:00:00 2001 From: fnkbsi Date: Tue, 23 Jan 2024 13:08:39 +0100 Subject: [PATCH 28/39] adapt methode notificationActionSuspendedEV -> removed ocppServerRepository and suppress sending a mail in the first minute --- .../steve/service/NotificationService.java | 58 ++++++++++--------- 1 file changed, 30 insertions(+), 28 deletions(-) diff --git a/src/main/java/de/rwth/idsg/steve/service/NotificationService.java b/src/main/java/de/rwth/idsg/steve/service/NotificationService.java index 0b44d4b70..f2a6d5560 100644 --- a/src/main/java/de/rwth/idsg/steve/service/NotificationService.java +++ b/src/main/java/de/rwth/idsg/steve/service/NotificationService.java @@ -1,6 +1,6 @@ /* * SteVe - SteckdosenVerwaltung - https://github.com/steve-community/steve - * Copyright (C) 2013-2023 SteVe Community Team + * Copyright (C) 2013-2024 SteVe Community Team * All Rights Reserved. * * This program is free software: you can redistribute it and/or modify @@ -43,7 +43,6 @@ import static de.rwth.idsg.steve.NotificationFeature.OcppTransactionStarted; import static de.rwth.idsg.steve.NotificationFeature.OcppStationStatusSuspendedEV; import static de.rwth.idsg.steve.NotificationFeature.OcppTransactionEnded; -import de.rwth.idsg.steve.repository.OcppServerRepository; import de.rwth.idsg.steve.repository.TransactionRepository; import de.rwth.idsg.steve.repository.UserRepository; import de.rwth.idsg.steve.repository.dto.Transaction; @@ -62,7 +61,6 @@ public class NotificationService { @Autowired private MailService mailService; @Autowired private TransactionRepository transactionRepository; @Autowired private UserRepository userRepository; - @Autowired private OcppServerRepository ocppServerRepository; @Autowired private ScheduledExecutorService executorService; @EventListener @@ -146,35 +144,39 @@ public void ocppStationStatusSuspendedEV(OcppStationStatusSuspendedEV notificati } private void notificationActionSuspendedEV(OcppStationStatusSuspendedEV notification) { - Integer connectorPk = ocppServerRepository.getConnectorPk(notification.getChargeBoxId(), - notification.getConnectorId() - ); - String ocppTag = transactionRepository.getOcppTagOfActiveTransaction(connectorPk); - String subject = format("EV stopped charging at charging station %s, Connector %d", notification.getChargeBoxId(), notification.getConnectorId() ); - if (ocppTag != null) { - String eMailAddress = null; - UserRecord userRecord = new UserRecord(); - try { - userRecord = userRepository.getDetails(ocppTag).getUserRecord(); - eMailAddress = userRecord.getEMail(); - } catch (Exception e) { - log.error("Failed to send email (SuspendedEV). User not found! " + e.getMessage()); - } - // send email if user with eMail address found - if (!Strings.isNullOrEmpty(eMailAddress)) { - String bodyUserMail = - format("User: %s %s \n\n Connector %d of charging station %s notifies Suspended_EV", - userRecord.getFirstName(), - userRecord.getLastName(), - notification.getConnectorId(), - notification.getChargeBoxId() - ); - - mailService.sendAsync(subject, addTimestamp(bodyUserMail), eMailAddress); + + Integer transactionPk = transactionRepository.getActiveTransactionId(notification.getChargeBoxId(), + notification.getConnectorId()); + if (transactionPk != null) { + Transaction transaction = transactionRepository.getTransaction(transactionPk); + String ocppTag = transaction.getOcppIdTag(); + if (ocppTag != null) { + // No mail directly after the start of the transaction, + if (notification.getTimestamp().isAfter(transaction.getStartTimestamp().plusMinutes(1))) { + String eMailAddress = null; + UserRecord userRecord = new UserRecord(); + try { + userRecord = userRepository.getDetails(ocppTag).getUserRecord(); + eMailAddress = userRecord.getEMail(); + } catch (Exception e) { + log.error("Failed to send email (SuspendedEV). User not found! " + e.getMessage()); + } + // send email if user with eMail address found + if (!Strings.isNullOrEmpty(eMailAddress)) { + String bodyUserMail = + format("User: %s %s \n\n Connector %d of charging station %s notifies Suspended_EV", + userRecord.getFirstName(), + userRecord.getLastName(), + notification.getConnectorId(), + notification.getChargeBoxId() + ); + mailService.sendAsync(subject, addTimestamp(bodyUserMail), eMailAddress); + } + } } } From 230c1ee8816b08c2be01bd1498c0437075806c7a Mon Sep 17 00:00:00 2001 From: fnkbsi Date: Tue, 23 Jan 2024 13:21:34 +0100 Subject: [PATCH 29/39] getActiveTransactionId(String chargeBox, Integer connectorId) method, add .orderBy(TRANSACTION.TRANSACTION_PK.desc()) to avoid fetching ghost transactions, fetch the latest --- .../idsg/steve/repository/impl/TransactionRepositoryImpl.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/de/rwth/idsg/steve/repository/impl/TransactionRepositoryImpl.java b/src/main/java/de/rwth/idsg/steve/repository/impl/TransactionRepositoryImpl.java index 70ec7a739..339421071 100644 --- a/src/main/java/de/rwth/idsg/steve/repository/impl/TransactionRepositoryImpl.java +++ b/src/main/java/de/rwth/idsg/steve/repository/impl/TransactionRepositoryImpl.java @@ -107,6 +107,7 @@ public Integer getActiveTransactionId(String chargeBoxId, Integer connectorId) { .and(CONNECTOR.CHARGE_BOX_ID.equal(chargeBoxId)) .where(TRANSACTION.STOP_TIMESTAMP.isNull()) .and(CONNECTOR.CONNECTOR_ID.equal(connectorId)) + .orderBy(TRANSACTION.TRANSACTION_PK.desc()) // to avoid fetching ghost transactions, fetch the latest .fetchAny(TRANSACTION.TRANSACTION_PK); } From 9faa7b4d112ae27064c59cbd4b396b159c3f9e02 Mon Sep 17 00:00:00 2001 From: fnkbsi <135032168+fnkbsi@users.noreply.github.com> Date: Wed, 3 Apr 2024 14:46:40 +0200 Subject: [PATCH 30/39] Restore @override annotation after reslove conflict --- .../idsg/steve/repository/impl/TransactionRepositoryImpl.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/de/rwth/idsg/steve/repository/impl/TransactionRepositoryImpl.java b/src/main/java/de/rwth/idsg/steve/repository/impl/TransactionRepositoryImpl.java index 9417e7d7e..4f7697d36 100644 --- a/src/main/java/de/rwth/idsg/steve/repository/impl/TransactionRepositoryImpl.java +++ b/src/main/java/de/rwth/idsg/steve/repository/impl/TransactionRepositoryImpl.java @@ -110,6 +110,7 @@ public Integer getActiveTransactionId(String chargeBoxId, Integer connectorId) { .fetchAny(TRANSACTION.TRANSACTION_PK); } + @Override public TransactionDetails getDetails(int transactionPk) { // ------------------------------------------------------------------------- From b2c6908c978b2b65864242499fe7cd7a54f240a1 Mon Sep 17 00:00:00 2001 From: fnkbsi <135032168+fnkbsi@users.noreply.github.com> Date: Thu, 8 Aug 2024 15:00:50 +0200 Subject: [PATCH 31/39] Adding user individual notification selection. Selection is stored in a new row of the user table as comma separated list. --- .../rwth/idsg/steve/repository/dto/User.java | 5 +- .../dto/UserNotificationFeature.java | 72 +++++++ .../repository/impl/UserRepositoryImpl.java | 14 +- .../steve/service/NotificationService.java | 193 +++++++++++++----- .../steve/utils/mapper/UserFormMapper.java | 2 + .../steve/web/controller/UsersController.java | 4 + .../de/rwth/idsg/steve/web/dto/UserForm.java | 4 + .../resources/db/migration/V1_0_6__update.sql | 2 + .../views/data-man/00-user-profile.jsp | 6 + .../webapp/WEB-INF/views/data-man/users.jsp | 8 +- 10 files changed, 253 insertions(+), 57 deletions(-) create mode 100644 src/main/java/de/rwth/idsg/steve/repository/dto/UserNotificationFeature.java create mode 100644 src/main/resources/db/migration/V1_0_6__update.sql diff --git a/src/main/java/de/rwth/idsg/steve/repository/dto/User.java b/src/main/java/de/rwth/idsg/steve/repository/dto/User.java index b919a420e..05c6a5c71 100644 --- a/src/main/java/de/rwth/idsg/steve/repository/dto/User.java +++ b/src/main/java/de/rwth/idsg/steve/repository/dto/User.java @@ -17,7 +17,7 @@ * along with this program. If not, see . */ package de.rwth.idsg.steve.repository.dto; - +import java.util.List; import jooq.steve.db.tables.records.AddressRecord; import jooq.steve.db.tables.records.UserRecord; import lombok.Builder; @@ -36,6 +36,7 @@ public class User { public static final class Overview { private final Integer userPk, ocppTagPk; private final String ocppIdTag, name, phone, email; + private final List enabledFeatures; } @Getter @@ -43,6 +44,6 @@ public static final class Overview { public static final class Details { private final UserRecord userRecord; private final AddressRecord address; - private Optional ocppIdTag; + private final Optional ocppIdTag; } } diff --git a/src/main/java/de/rwth/idsg/steve/repository/dto/UserNotificationFeature.java b/src/main/java/de/rwth/idsg/steve/repository/dto/UserNotificationFeature.java new file mode 100644 index 000000000..247251485 --- /dev/null +++ b/src/main/java/de/rwth/idsg/steve/repository/dto/UserNotificationFeature.java @@ -0,0 +1,72 @@ +/* + * SteVe - SteckdosenVerwaltung - https://github.com/steve-community/steve + * Copyright (C) 2013-2024 SteVe Community Team + * All Rights Reserved. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package de.rwth.idsg.steve.repository.dto; + +//import static de.rwth.idsg.steve.utils.StringUtils.joinByComma; +//import static de.rwth.idsg.steve.utils.StringUtils.splitByComma; +//import java.util.List; +//import java.util.stream.Collectors; +import static de.rwth.idsg.steve.utils.StringUtils.joinByComma; +import static de.rwth.idsg.steve.utils.StringUtils.splitByComma; +import java.util.List; +import java.util.stream.Collectors; +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +/** + * + * @author fnkbsi + */ +@RequiredArgsConstructor +public enum UserNotificationFeature { + + // Ocpp related + // + //OcppStationBooted(" a charging station sends a boot notification (Note: This activates notifications about failed connection attempts for unregistered JSON stations, as well)"), + OcppStationStatusFailure(" a connector gets faulted"), + //OcppStationWebSocketConnected(" a JSON charging station connects"), + //OcppStationWebSocketDisconnected(" a JSON charging station disconnects"), + OcppTransactionStarted(" a charging station starts a transaction"), + OcppStationStatusSuspendedEV(" a EV suspended charging"), + OcppTransactionEnded(" a charging station ends a transaction"); + + + @Getter + private final String text; + + public static UserNotificationFeature fromName(String v) { + for (UserNotificationFeature c: UserNotificationFeature.values()) { + if (c.name().equalsIgnoreCase(v)) { + return c; + } + } + throw new IllegalArgumentException(v); + } + + public static List splitFeatures(String str) { + return splitByComma(str).stream() + .map(UserNotificationFeature::fromName) + .collect(Collectors.toList()); + } + + public static String joinFeatures(List enablesFeatures) { + return joinByComma(enablesFeatures); + } +} \ No newline at end of file diff --git a/src/main/java/de/rwth/idsg/steve/repository/impl/UserRepositoryImpl.java b/src/main/java/de/rwth/idsg/steve/repository/impl/UserRepositoryImpl.java index a4504cae2..ac013bb59 100644 --- a/src/main/java/de/rwth/idsg/steve/repository/impl/UserRepositoryImpl.java +++ b/src/main/java/de/rwth/idsg/steve/repository/impl/UserRepositoryImpl.java @@ -22,6 +22,7 @@ import de.rwth.idsg.steve.repository.AddressRepository; import de.rwth.idsg.steve.repository.UserRepository; import de.rwth.idsg.steve.repository.dto.User; +import de.rwth.idsg.steve.repository.dto.UserNotificationFeature; import de.rwth.idsg.steve.web.dto.UserForm; import de.rwth.idsg.steve.web.dto.UserQueryForm; import jooq.steve.db.tables.records.AddressRecord; @@ -31,7 +32,7 @@ import org.jooq.Field; import org.jooq.JoinType; import org.jooq.Record1; -import org.jooq.Record7; +import org.jooq.Record8; import org.jooq.Result; import org.jooq.SelectConditionStep; import org.jooq.SelectQuery; @@ -68,6 +69,7 @@ public List getOverview(UserQueryForm form) { .name(r.value4() + " " + r.value5()) .phone(r.value6()) .email(r.value7()) + .enabledFeatures(UserNotificationFeature.splitFeatures(r.value8())) .build() ); } @@ -199,7 +201,9 @@ public void delete(int userPk) { // ------------------------------------------------------------------------- @SuppressWarnings("unchecked") - private Result> getOverviewInternal(UserQueryForm form) { + private Result> + getOverviewInternal(UserQueryForm form) { + SelectQuery selectQuery = ctx.selectQuery(); selectQuery.addFrom(USER); selectQuery.addJoin(OCPP_TAG, JoinType.LEFT_OUTER_JOIN, USER.OCPP_TAG_PK.eq(OCPP_TAG.OCPP_TAG_PK)); @@ -210,7 +214,8 @@ private Result USER.FIRST_NAME, USER.LAST_NAME, USER.PHONE, - USER.E_MAIL + USER.E_MAIL, + USER.USER_NOTIFICATION_FEATURES ); if (form.isSetUserPk()) { @@ -261,6 +266,8 @@ private void addInternal(DSLContext ctx, UserForm form, Integer addressPk) { .set(USER.NOTE, form.getNote()) .set(USER.ADDRESS_PK, addressPk) .set(USER.OCPP_TAG_PK, selectOcppTagPk(form.getOcppIdTag())) + .set(USER.USER_NOTIFICATION_FEATURES, + UserNotificationFeature.joinFeatures(form.getEnabledFeatures())) .execute(); if (count != 1) { @@ -279,6 +286,7 @@ private void updateInternal(DSLContext ctx, UserForm form, Integer addressPk) { .set(USER.NOTE, form.getNote()) .set(USER.ADDRESS_PK, addressPk) .set(USER.OCPP_TAG_PK, selectOcppTagPk(form.getOcppIdTag())) + .set(USER.USER_NOTIFICATION_FEATURES, UserNotificationFeature.joinFeatures(form.getEnabledFeatures())) .where(USER.USER_PK.eq(form.getUserPk())) .execute(); } diff --git a/src/main/java/de/rwth/idsg/steve/service/NotificationService.java b/src/main/java/de/rwth/idsg/steve/service/NotificationService.java index f2a6d5560..e824a72fe 100644 --- a/src/main/java/de/rwth/idsg/steve/service/NotificationService.java +++ b/src/main/java/de/rwth/idsg/steve/service/NotificationService.java @@ -46,6 +46,7 @@ import de.rwth.idsg.steve.repository.TransactionRepository; import de.rwth.idsg.steve.repository.UserRepository; import de.rwth.idsg.steve.repository.dto.Transaction; +import de.rwth.idsg.steve.repository.dto.UserNotificationFeature; import static java.lang.String.format; import jooq.steve.db.tables.records.UserRecord; import java.util.concurrent.ScheduledExecutorService; @@ -72,7 +73,8 @@ public void ocppStationBooted(OccpStationBooted notification) { String subject = format("Received boot notification from '%s'", notification.getChargeBoxId()); String body; if (notification.getStatus().isPresent()) { - body = format("Charging station '%s' is in database and has registration status '%s'.", notification.getChargeBoxId(), notification.getStatus().get().value()); + body = format("Charging station '%s' is in database and has registration status '%s'.", + notification.getChargeBoxId(), notification.getStatus().get().value()); } else { body = format("Charging station '%s' is NOT in database", notification.getChargeBoxId()); } @@ -104,64 +106,166 @@ public void ocppStationWebSocketDisconnected(OcppStationWebSocketDisconnected no @EventListener public void ocppStationStatusFailure(OcppStationStatusFailure notification) { - if (isDisabled(OcppStationStatusFailure)) { - return; - } - String subject = format("Connector '%s' of charging station '%s' is FAULTED", notification.getConnectorId(), notification.getChargeBoxId() ); + + // user mail in separate task, so database queries don't block the execution + executorService.execute(() -> { + try { + userNotificationocppStationStatusFailure(notification, subject); + } catch (Exception e) { + log.error("Failed to execute the user notification of ocppStationStatusFailure.", e); + } + }); + + /* mail defined in settings */ + if (isDisabled(OcppStationStatusFailure)) { + return; + } + String body = format("Status Error Code: '%s'", notification.getErrorCode()); mailService.sendAsync(subject, addTimestamp(body)); } - @EventListener - public void ocppTransactionStarted(OcppTransactionStarted notification) { - if (isDisabled(OcppTransactionStarted)) { - return; + private void userNotificationocppStationStatusFailure(OcppStationStatusFailure notification, String subject) { + + Integer transactionPk = transactionRepository.getActiveTransactionId(notification.getChargeBoxId(), + notification.getConnectorId()); + if (transactionPk != null) { + Transaction transaction = transactionRepository.getTransaction(transactionPk); + String ocppTag = transaction.getOcppIdTag(); + if (ocppTag != null) { + String eMailAddress = null; + UserRecord userRecord = new UserRecord(); + try { + userRecord = userRepository.getDetails(ocppTag).getUserRecord(); + if (userRecord.getUserNotificationFeatures() + .contains(UserNotificationFeature.OcppStationStatusFailure.toString())) { + eMailAddress = userRecord.getEMail(); + } + } catch (Exception e) { + log.error("Failed to send email (StationStatusFailure). User not found! " + e.getMessage()); + } + // send email if user with eMail address found + if (!Strings.isNullOrEmpty(eMailAddress)) { + String bodyUserMail = + format("User: %s %s \n\n Connector %d of charging station %s notifies FAULTED! \n\n Error code: %s", + userRecord.getFirstName(), + userRecord.getLastName(), + notification.getConnectorId(), + notification.getChargeBoxId(), + notification.getErrorCode() + ); + mailService.sendAsync(subject, addTimestamp(bodyUserMail), eMailAddress); + } + } } + } + @EventListener + public void ocppTransactionStarted(OcppTransactionStarted notification) { String subject = format("Transaction '%s' has started on charging station '%s' on connector '%s'", notification.getTransactionId(), notification.getParams().getChargeBoxId(), notification.getParams().getConnectorId() ); + // user mail in separate task, so database queries don't block the execution + executorService.execute(() -> { + try { + userNotificationOcppTransactionStarted(notification, subject); + } catch (Exception e) { + log.error("Failed to execute the user notification of ocppStationStatusFailure.", e); + } + }); + + /* mail defined in settings */ + if (isDisabled(OcppTransactionStarted)) { + return; + } + mailService.sendAsync(subject, addTimestamp(createContent(notification.getParams()))); } - @EventListener - public void ocppStationStatusSuspendedEV(OcppStationStatusSuspendedEV notification) { - executorService.execute(() -> { + private void userNotificationOcppTransactionStarted(OcppTransactionStarted notification, String subject) { + + String ocppTag = notification.getParams().getIdTag(); + if (ocppTag != null) { + String eMailAddress = null; + UserRecord userRecord = new UserRecord(); try { - notificationActionSuspendedEV(notification); + userRecord = userRepository.getDetails(ocppTag).getUserRecord(); + if (userRecord.getUserNotificationFeatures() + .contains(UserNotificationFeature.OcppTransactionStarted.toString())) { + eMailAddress = userRecord.getEMail(); + } } catch (Exception e) { - log.error("Failed to execute the notification of SuspendedEV", e); + log.error("Failed to send email (StationStatusFailure). User not found! " + e.getMessage()); } - }); + // send email if user with eMail address found + if (!Strings.isNullOrEmpty(eMailAddress)) { + String bodyUserMail = + format("User: '%s' '%s' started transaction '%d' on connector '%s' of charging station '%s'", + userRecord.getFirstName(), + userRecord.getLastName(), + notification.getTransactionId(), + notification.getParams().getConnectorId(), + notification.getParams().getChargeBoxId() + ); + mailService.sendAsync(subject, addTimestamp(bodyUserMail), eMailAddress); + } + } } - private void notificationActionSuspendedEV(OcppStationStatusSuspendedEV notification) { + @EventListener + public void ocppStationStatusSuspendedEV(OcppStationStatusSuspendedEV notification) { String subject = format("EV stopped charging at charging station %s, Connector %d", notification.getChargeBoxId(), notification.getConnectorId() ); + // user mail in separate task, so database queries don't block the execution + executorService.execute(() -> { + try { + userNotificationActionSuspendedEV(notification, subject); + } catch (Exception e) { + log.error("Failed to execute the user notification of SuspendedEV", e); + } + }); + + /* mail defined in settings */ + if (isDisabled(OcppStationStatusSuspendedEV)) { + return; + } + + String body = format("Connector %d of charging station %s notifies Suspended_EV", + notification.getConnectorId(), + notification.getChargeBoxId() + ); + mailService.sendAsync(subject, addTimestamp(body)); + } + + private void userNotificationActionSuspendedEV(OcppStationStatusSuspendedEV notification, String subject) { + Integer transactionPk = transactionRepository.getActiveTransactionId(notification.getChargeBoxId(), notification.getConnectorId()); if (transactionPk != null) { Transaction transaction = transactionRepository.getTransaction(transactionPk); String ocppTag = transaction.getOcppIdTag(); if (ocppTag != null) { - // No mail directly after the start of the transaction, + // No mail directly after the start of the transaction, if (notification.getTimestamp().isAfter(transaction.getStartTimestamp().plusMinutes(1))) { String eMailAddress = null; UserRecord userRecord = new UserRecord(); try { userRecord = userRepository.getDetails(ocppTag).getUserRecord(); - eMailAddress = userRecord.getEMail(); + if (userRecord.getUserNotificationFeatures() + .contains(UserNotificationFeature.OcppStationStatusSuspendedEV.toString())) { + eMailAddress = userRecord.getEMail(); + } } catch (Exception e) { log.error("Failed to send email (SuspendedEV). User not found! " + e.getMessage()); } @@ -179,30 +283,33 @@ private void notificationActionSuspendedEV(OcppStationStatusSuspendedEV notifica } } } - - /* mail defined in settings */ - if (isDisabled(OcppStationStatusSuspendedEV)) { - return; - } - String body = format("Connector %d of charging station %s notifies Suspended_EV", - notification.getConnectorId(), - notification.getChargeBoxId() - ); - mailService.sendAsync(subject, addTimestamp(body)); } @EventListener public void ocppTransactionEnded(OcppTransactionEnded notification) { - executorService.execute(() -> { + String subject = format("Transaction '%s' has ended on charging station '%s'", + notification.getParams().getTransactionId(), + notification.getParams().getChargeBoxId() + ); + + // user mail in separate task, so database queries don't block the execution + executorService.execute(() -> { try { - notificationActionTransactionEnded(notification); + userNotificationActionTransactionEnded(notification, subject); } catch (Exception e) { log.error("Failed to execute the notification of SuspendedEV", e); } }); + + /* mail defined in settings */ + if (isDisabled(OcppTransactionEnded)) { + return; + } + + mailService.sendAsync(subject, addTimestamp(createContent(notification.getParams()))); } - private void notificationActionTransactionEnded(OcppTransactionEnded notification) { + private void userNotificationActionTransactionEnded(OcppTransactionEnded notification, String subject) { String eMailAddress = null; UserRecord userRecord = new UserRecord(); @@ -210,38 +317,24 @@ private void notificationActionTransactionEnded(OcppTransactionEnded notificatio try { userRecord = userRepository.getDetails(transActParams.getOcppIdTag()).getUserRecord(); - eMailAddress = userRecord.getEMail(); + if (userRecord.getUserNotificationFeatures() + .contains(UserNotificationFeature.OcppTransactionEnded.toString())) { + eMailAddress = userRecord.getEMail(); + } } catch (Exception e) { log.error("Failed to send email (TransactionStop). User not found! " + e.getMessage()); } // mail to user if (!Strings.isNullOrEmpty(eMailAddress)) { - String subjectUserMail = format("Transaction '%s' has ended on charging station '%s'", - transActParams.getId(), - transActParams.getChargeBoxId() - ); - // if the Transactionstop is received within the first Minute don't send an E-Mail if (transActParams.getStopTimestamp().isAfter(transActParams.getStartTimestamp().plusMinutes(1))) { - mailService.sendAsync(subjectUserMail, + mailService.sendAsync(subject, addTimestamp(createContent(transActParams, userRecord)), eMailAddress ); } } - - /* mail defined in settings */ - if (isDisabled(OcppTransactionEnded)) { - return; - } - - String subject = format("Transaction '%s' has ended on charging station '%s'", - notification.getParams().getTransactionId(), - notification.getParams().getChargeBoxId() - ); - - mailService.sendAsync(subject, addTimestamp(createContent(notification.getParams()))); } // ------------------------------------------------------------------------- diff --git a/src/main/java/de/rwth/idsg/steve/utils/mapper/UserFormMapper.java b/src/main/java/de/rwth/idsg/steve/utils/mapper/UserFormMapper.java index f43f4e888..72bb8639b 100644 --- a/src/main/java/de/rwth/idsg/steve/utils/mapper/UserFormMapper.java +++ b/src/main/java/de/rwth/idsg/steve/utils/mapper/UserFormMapper.java @@ -19,6 +19,7 @@ package de.rwth.idsg.steve.utils.mapper; import de.rwth.idsg.steve.repository.dto.User; +import de.rwth.idsg.steve.repository.dto.UserNotificationFeature; import de.rwth.idsg.steve.utils.ControllerHelper; import de.rwth.idsg.steve.web.dto.UserForm; import de.rwth.idsg.steve.web.dto.UserSex; @@ -44,6 +45,7 @@ public static UserForm toForm(User.Details details) { form.setPhone(userRecord.getPhone()); form.setSex(UserSex.fromDatabaseValue(userRecord.getSex())); form.setEMail(userRecord.getEMail()); + form.setEnabledFeatures(UserNotificationFeature.splitFeatures(userRecord.getUserNotificationFeatures())); form.setNote(userRecord.getNote()); form.setAddress(AddressMapper.recordToDto(details.getAddress())); form.setOcppIdTag(details.getOcppIdTag().orElse(ControllerHelper.EMPTY_OPTION)); diff --git a/src/main/java/de/rwth/idsg/steve/web/controller/UsersController.java b/src/main/java/de/rwth/idsg/steve/web/controller/UsersController.java index 542facbc4..fc31c6604 100644 --- a/src/main/java/de/rwth/idsg/steve/web/controller/UsersController.java +++ b/src/main/java/de/rwth/idsg/steve/web/controller/UsersController.java @@ -18,6 +18,7 @@ */ package de.rwth.idsg.steve.web.controller; +import de.rwth.idsg.steve.repository.dto.UserNotificationFeature; import de.rwth.idsg.steve.repository.UserRepository; import de.rwth.idsg.steve.repository.dto.User; import de.rwth.idsg.steve.service.OcppTagService; @@ -79,6 +80,7 @@ public String getQuery(@ModelAttribute(PARAMS) UserQueryForm params, Model model private void initList(Model model, UserQueryForm params) { model.addAttribute(PARAMS, params); model.addAttribute("userList", userRepository.getOverview(params)); + model.addAttribute("features", UserNotificationFeature.values()); } @RequestMapping(value = DETAILS_PATH, method = RequestMethod.GET) @@ -87,6 +89,7 @@ public String getDetails(@PathVariable("userPk") int userPk, Model model) { UserForm form = UserFormMapper.toForm(details); model.addAttribute("userForm", form); + model.addAttribute("features", UserNotificationFeature.values()); setTags(model); return "data-man/userDetails"; } @@ -95,6 +98,7 @@ public String getDetails(@PathVariable("userPk") int userPk, Model model) { public String addGet(Model model) { setTags(model); model.addAttribute("userForm", new UserForm()); + model.addAttribute("features", UserNotificationFeature.values()); return "data-man/userAdd"; } diff --git a/src/main/java/de/rwth/idsg/steve/web/dto/UserForm.java b/src/main/java/de/rwth/idsg/steve/web/dto/UserForm.java index 3d5eb8d49..c310c0bd3 100644 --- a/src/main/java/de/rwth/idsg/steve/web/dto/UserForm.java +++ b/src/main/java/de/rwth/idsg/steve/web/dto/UserForm.java @@ -18,6 +18,8 @@ */ package de.rwth.idsg.steve.web.dto; +import de.rwth.idsg.steve.repository.dto.UserNotificationFeature; +import java.util.List; import lombok.Getter; import lombok.Setter; import lombok.ToString; @@ -52,6 +54,8 @@ public class UserForm { @Email(message = "Not a valid e-mail address") private String eMail; + private List enabledFeatures; + private Address address; } diff --git a/src/main/resources/db/migration/V1_0_6__update.sql b/src/main/resources/db/migration/V1_0_6__update.sql new file mode 100644 index 000000000..2cfb2206b --- /dev/null +++ b/src/main/resources/db/migration/V1_0_6__update.sql @@ -0,0 +1,2 @@ +ALTER TABLE `user` + ADD COLUMN `user_notification_features` TEXT NULL DEFAULT NULL COMMENT 'comma separated list' COLLATE 'utf8mb3_unicode_ci' AFTER `e_mail`; \ No newline at end of file diff --git a/src/main/resources/webapp/WEB-INF/views/data-man/00-user-profile.jsp b/src/main/resources/webapp/WEB-INF/views/data-man/00-user-profile.jsp index c128b4af3..586fb0fce 100644 --- a/src/main/resources/webapp/WEB-INF/views/data-man/00-user-profile.jsp +++ b/src/main/resources/webapp/WEB-INF/views/data-man/00-user-profile.jsp @@ -33,5 +33,11 @@ Phone: E-mail: + + Notify when... + + + Additional Note: \ No newline at end of file diff --git a/src/main/resources/webapp/WEB-INF/views/data-man/users.jsp b/src/main/resources/webapp/WEB-INF/views/data-man/users.jsp index 90f2cd91d..f147fdbb8 100644 --- a/src/main/resources/webapp/WEB-INF/views/data-man/users.jsp +++ b/src/main/resources/webapp/WEB-INF/views/data-man/users.jsp @@ -61,6 +61,7 @@ Name Phone E-Mail + Notifications @@ -79,8 +80,11 @@ ${cr.name} ${cr.phone} ${cr.email} - - + + ${eF}
+
+ + From ac38d1e7fd97291c6c69150787671aa1b6619966 Mon Sep 17 00:00:00 2001 From: fnkbsi <135032168+fnkbsi@users.noreply.github.com> Date: Thu, 10 Oct 2024 11:19:07 +0200 Subject: [PATCH 32/39] Rename db migration script V1_06 to V1_08 --- src/main/resources/db/migration/V1_0_8__update.sql | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 src/main/resources/db/migration/V1_0_8__update.sql diff --git a/src/main/resources/db/migration/V1_0_8__update.sql b/src/main/resources/db/migration/V1_0_8__update.sql new file mode 100644 index 000000000..2cfb2206b --- /dev/null +++ b/src/main/resources/db/migration/V1_0_8__update.sql @@ -0,0 +1,2 @@ +ALTER TABLE `user` + ADD COLUMN `user_notification_features` TEXT NULL DEFAULT NULL COMMENT 'comma separated list' COLLATE 'utf8mb3_unicode_ci' AFTER `e_mail`; \ No newline at end of file From 239e62749d4e4655e798f56ffaf7289cba5a301f Mon Sep 17 00:00:00 2001 From: fnkbsi <135032168+fnkbsi@users.noreply.github.com> Date: Tue, 14 Jan 2025 17:03:58 +0100 Subject: [PATCH 33/39] manually merge origin/master into MailUserAtTransactionStop (previous merge was incomplete) --- README.md | 4 + pom.xml | 53 +-- .../java/de/rwth/idsg/steve/JettyServer.java | 8 - .../rwth/idsg/steve/SteveConfiguration.java | 2 + .../steve/config/ApiDocsConfiguration.java | 3 - .../rwth/idsg/steve/service/MailService.java | 34 +- .../steve/service/NotificationService.java | 1 + .../idsg/steve/service/WebUserService.java | 22 +- .../controller/AboutSettingsController.java | 2 - src/main/resources/checkstyle.xml | 336 +++++++++--------- 10 files changed, 226 insertions(+), 239 deletions(-) diff --git a/README.md b/README.md index e72367078..e6153a0c4 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,10 @@ SteVe is considered as an open platform to implement, test and evaluate novel id The project is distributed under [GPL](LICENSE.txt) and is free to use. If you are going to deploy it we are happy to see the [logo](website/logo/managed-by-steve.pdf) on a charge point. +## Relation to Powerfill + +[Powerfill](https://powerfill.co/) is a SaaS company to expand beyond the basics of SteVe: While SteVe covers the basics of OCPP functionality in a DIY sense, Powerfill offers more and enterprise features with ease of use. [See the announcement](https://github.com/steve-community/steve/issues/1643) and [sign up for early access](https://powerfill.co/early-access/). + ### Charge Point Support Electric charge points using the following OCPP versions are supported: diff --git a/pom.xml b/pom.xml index 728b49856..0ef520dc6 100644 --- a/pom.xml +++ b/pom.xml @@ -35,16 +35,16 @@ 17 UTF-8 - 3.19.11 - 10.18.2 - 4.0.5 - 6.1.11 - 6.3.3 - 9.0.0 - 12.0.13 - 1.18.34 - 2.18.0 - 4.5 + 3.19.16 + 11.1.0 + 4.1.0 + 6.2.0 + 6.4.2 + 9.1.0 + 12.0.16 + 1.18.36 + 2.18.2 + 4.6 jdbc:mysql://${db.ip}:${db.port}/${db.schema}?useSSL=true&serverTimezone=UTC @@ -181,6 +181,13 @@ ${java.version} ${java.version} + + + org.projectlombok + lombok + ${lombok.version} + + @@ -188,14 +195,14 @@ org.apache.maven.plugins maven-surefire-plugin - 3.5.0 + 3.5.2 org.apache.maven.plugins maven-pmd-plugin - 3.25.0 + 3.26.0 ${project.basedir}/src/main/resources/maven-pmd-plugin-default.xml @@ -205,7 +212,7 @@ com.github.spotbugs spotbugs-maven-plugin - 4.8.6.4 + 4.8.6.6 false de.rwth.idsg.steve.- @@ -221,7 +228,7 @@ org.apache.maven.plugins maven-checkstyle-plugin - 3.5.0 + 3.6.0 ${basedir}/src/main/resources/checkstyle.xml @@ -268,7 +275,7 @@ org.apache.maven.plugins maven-dependency-plugin - 3.8.0 + 3.8.1 copy-dependencies @@ -493,14 +500,14 @@ org.apache.logging.log4j log4j-bom - 2.24.0 + 2.24.3 import pom org.junit junit-bom - 5.11.2 + 5.11.4 pom import @@ -539,7 +546,7 @@ org.jetbrains annotations - 25.0.0 + 26.0.1 compile @@ -557,12 +564,12 @@ org.hibernate.validator hibernate-validator - 8.0.1.Final + 8.0.2.Final com.google.guava guava - 33.3.1-jre + 33.4.0-jre com.fasterxml.jackson.core @@ -588,7 +595,7 @@ org.apache.httpcomponents.client5 httpclient5 - 5.4 + 5.4.1 jakarta.websocket @@ -747,7 +754,7 @@ com.zaxxer HikariCP - 5.1.0 + 6.2.1 org.jooq @@ -786,7 +793,7 @@ net.bytebuddy byte-buddy - 1.15.1 + 1.15.11 test diff --git a/src/main/java/de/rwth/idsg/steve/JettyServer.java b/src/main/java/de/rwth/idsg/steve/JettyServer.java index 3a417c69a..3b4117666 100644 --- a/src/main/java/de/rwth/idsg/steve/JettyServer.java +++ b/src/main/java/de/rwth/idsg/steve/JettyServer.java @@ -135,14 +135,6 @@ private ServerConnector httpsConnector(HttpConfiguration httpConfig) { sslContextFactory.setKeyStorePath(CONFIG.getJetty().getKeyStorePath()); sslContextFactory.setKeyStorePassword(CONFIG.getJetty().getKeyStorePassword()); sslContextFactory.setKeyManagerPassword(CONFIG.getJetty().getKeyStorePassword()); - sslContextFactory.setExcludeCipherSuites( - "SSL_RSA_WITH_DES_CBC_SHA", - "SSL_DHE_RSA_WITH_DES_CBC_SHA", - "SSL_DHE_DSS_WITH_DES_CBC_SHA", - "SSL_RSA_EXPORT_WITH_RC4_40_MD5", - "SSL_RSA_EXPORT_WITH_DES40_CBC_SHA", - "SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA", - "SSL_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA"); // SSL HTTP Configuration HttpConfiguration httpsConfig = new HttpConfiguration(httpConfig); diff --git a/src/main/java/de/rwth/idsg/steve/SteveConfiguration.java b/src/main/java/de/rwth/idsg/steve/SteveConfiguration.java index 9df315f23..96f2341a9 100644 --- a/src/main/java/de/rwth/idsg/steve/SteveConfiguration.java +++ b/src/main/java/de/rwth/idsg/steve/SteveConfiguration.java @@ -67,7 +67,9 @@ public enum SteveConfiguration { contextPath = sanitizeContextPath(p.getOptionalString("context.path")); steveVersion = p.getString("steve.version"); gitDescribe = useFallbackIfNotSet(p.getOptionalString("git.describe"), null); + profile = ApplicationProfile.fromName(p.getString("profile")); + System.setProperty("spring.profiles.active", profile.name().toLowerCase()); jetty = Jetty.builder() .serverHost(p.getString("server.host")) diff --git a/src/main/java/de/rwth/idsg/steve/config/ApiDocsConfiguration.java b/src/main/java/de/rwth/idsg/steve/config/ApiDocsConfiguration.java index 81a77b24d..2b1d65076 100644 --- a/src/main/java/de/rwth/idsg/steve/config/ApiDocsConfiguration.java +++ b/src/main/java/de/rwth/idsg/steve/config/ApiDocsConfiguration.java @@ -19,7 +19,6 @@ package de.rwth.idsg.steve.config; import de.rwth.idsg.steve.SteveConfiguration; -import de.rwth.idsg.steve.SteveProdCondition; import io.swagger.v3.oas.models.Components; import io.swagger.v3.oas.models.OpenAPI; import io.swagger.v3.oas.models.info.Info; @@ -34,7 +33,6 @@ import org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; -import org.springframework.context.annotation.Conditional; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; @@ -52,7 +50,6 @@ SwaggerUiConfigProperties.class, SwaggerUiOAuthProperties.class, JacksonAutoConfiguration.class}) -@Conditional(SteveProdCondition.class) public class ApiDocsConfiguration { static { diff --git a/src/main/java/de/rwth/idsg/steve/service/MailService.java b/src/main/java/de/rwth/idsg/steve/service/MailService.java index 692910743..6375ddd61 100644 --- a/src/main/java/de/rwth/idsg/steve/service/MailService.java +++ b/src/main/java/de/rwth/idsg/steve/service/MailService.java @@ -26,7 +26,6 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; -import jakarta.annotation.PostConstruct; import jakarta.mail.Authenticator; import jakarta.mail.Message; import jakarta.mail.MessagingException; @@ -35,6 +34,7 @@ import jakarta.mail.Transport; import jakarta.mail.internet.InternetAddress; import jakarta.mail.internet.MimeMessage; + import java.util.Properties; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.locks.Lock; @@ -55,31 +55,8 @@ public class MailService { @Autowired private SettingsRepository settingsRepository; @Autowired private ScheduledExecutorService executorService; - private final ReadWriteLock readWriteLock = new ReentrantReadWriteLock(); - private final Lock readLock = readWriteLock.readLock(); - private final Lock writeLock = readWriteLock.writeLock(); - - private MailSettings settings; - private Session session; - - @PostConstruct - public void loadSettingsFromDB() { - writeLock.lock(); - try { - settings = settingsRepository.getMailSettings(); - } finally { - writeLock.unlock(); - } - session = createSession(getSettings()); - } - public MailSettings getSettings() { - readLock.lock(); - try { - return this.settings; - } finally { - readLock.unlock(); - } + return settingsRepository.getMailSettings(); } public void sendTestMail() { @@ -111,17 +88,18 @@ public void sendAsync(String subject, String body, String recipientAddresses) { } private void send(String subject, String body, String recipientAddresses) throws MessagingException { - MailSettings settingsLocal = getSettings(); + MailSettings settings = getSettings(); + Session session = createSession(settings); Message mail = new MimeMessage(session); mail.setSubject("[SteVe] " + subject); mail.setContent(body, "text/plain"); - mail.setFrom(new InternetAddress(settingsLocal.getFrom())); + mail.setFrom(new InternetAddress(settings.getFrom())); List eMailAddresses; if (recipientAddresses.isEmpty()) { - eMailAddresses = settingsLocal.getRecipients(); + eMailAddresses = settings.getRecipients(); } else { eMailAddresses = splitByComma(recipientAddresses); } diff --git a/src/main/java/de/rwth/idsg/steve/service/NotificationService.java b/src/main/java/de/rwth/idsg/steve/service/NotificationService.java index cfda57ee5..9a6d12b98 100644 --- a/src/main/java/de/rwth/idsg/steve/service/NotificationService.java +++ b/src/main/java/de/rwth/idsg/steve/service/NotificationService.java @@ -341,6 +341,7 @@ private void userNotificationActionTransactionEnded(OcppTransactionEnded notific // Private helpers // ------------------------------------------------------------------------- + private static String createContent(InsertTransactionParams params) { StringBuilder sb = new StringBuilder("Details:").append(System.lineSeparator()) .append("- chargeBoxId: ").append(params.getChargeBoxId()).append(System.lineSeparator()) diff --git a/src/main/java/de/rwth/idsg/steve/service/WebUserService.java b/src/main/java/de/rwth/idsg/steve/service/WebUserService.java index 9be5368eb..f2305e94e 100644 --- a/src/main/java/de/rwth/idsg/steve/service/WebUserService.java +++ b/src/main/java/de/rwth/idsg/steve/service/WebUserService.java @@ -26,12 +26,14 @@ import de.rwth.idsg.steve.repository.WebUserRepository; import jooq.steve.db.tables.records.WebUserRecord; import lombok.RequiredArgsConstructor; +import org.apache.commons.lang3.StringUtils; import org.jooq.JSON; import org.springframework.context.event.ContextRefreshedEvent; import org.springframework.context.event.EventListener; import org.springframework.security.access.AccessDeniedException; import org.springframework.security.core.Authentication; import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.authority.AuthorityUtils; import org.springframework.security.core.context.SecurityContext; import org.springframework.security.core.context.SecurityContextHolderStrategy; import org.springframework.security.core.userdetails.User; @@ -80,14 +82,20 @@ public void afterStart(ContextRefreshedEvent event) { return; } - var user = User - .withUsername(SteveConfiguration.CONFIG.getAuth().getUserName()) - .password(SteveConfiguration.CONFIG.getAuth().getEncodedPassword()) - .disabled(false) - .authorities("ADMIN") - .build(); + var headerVal = SteveConfiguration.CONFIG.getWebApi().getHeaderValue(); + + var encodedApiPassword = StringUtils.isEmpty(headerVal) + ? null + : SteveConfiguration.CONFIG.getAuth().getPasswordEncoder().encode(headerVal); + + var user = new WebUserRecord() + .setUsername(SteveConfiguration.CONFIG.getAuth().getUserName()) + .setPassword(SteveConfiguration.CONFIG.getAuth().getEncodedPassword()) + .setApiPassword(encodedApiPassword) + .setEnabled(true) + .setAuthorities(toJson(AuthorityUtils.createAuthorityList("ADMIN"))); - this.createUser(user); + webUserRepository.createUser(user); } @Override diff --git a/src/main/java/de/rwth/idsg/steve/web/controller/AboutSettingsController.java b/src/main/java/de/rwth/idsg/steve/web/controller/AboutSettingsController.java index 46c76f2a9..c56b10a43 100644 --- a/src/main/java/de/rwth/idsg/steve/web/controller/AboutSettingsController.java +++ b/src/main/java/de/rwth/idsg/steve/web/controller/AboutSettingsController.java @@ -94,7 +94,6 @@ public String postSettings(@Valid @ModelAttribute("settingsForm") SettingsForm s } settingsRepository.update(settingsForm); - mailService.loadSettingsFromDB(); return "redirect:/manager/settings"; } @@ -107,7 +106,6 @@ public String testMail(@Valid @ModelAttribute("settingsForm") SettingsForm setti } settingsRepository.update(settingsForm); - mailService.loadSettingsFromDB(); mailService.sendTestMail(); return "redirect:/manager/settings"; diff --git a/src/main/resources/checkstyle.xml b/src/main/resources/checkstyle.xml index cdc639ad6..3c8f5dd9e 100644 --- a/src/main/resources/checkstyle.xml +++ b/src/main/resources/checkstyle.xml @@ -1,168 +1,168 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 1fe84cebf3f6f48f3cd717d60ac2173745407c1a Mon Sep 17 00:00:00 2001 From: fnkbsi <135032168+fnkbsi@users.noreply.github.com> Date: Thu, 13 Mar 2025 14:29:43 +0100 Subject: [PATCH 34/39] MailService removed method send(String subject, String body, String recipientAddresses) from interface --- src/main/java/de/rwth/idsg/steve/service/MailService.java | 4 ---- .../java/de/rwth/idsg/steve/service/MailServiceDefault.java | 6 +++--- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/src/main/java/de/rwth/idsg/steve/service/MailService.java b/src/main/java/de/rwth/idsg/steve/service/MailService.java index c435aaa91..3e8944bfc 100644 --- a/src/main/java/de/rwth/idsg/steve/service/MailService.java +++ b/src/main/java/de/rwth/idsg/steve/service/MailService.java @@ -20,8 +20,6 @@ import de.rwth.idsg.steve.repository.dto.MailSettings; -import jakarta.mail.MessagingException; - /** * @author Sevket Goekay * @since 11.03.2025 @@ -35,6 +33,4 @@ public interface MailService { void sendAsync(String subject, String body); void sendAsync(String subject, String body, String recipientAddresses); - - void send(String subject, String body, String recipientAddresses) throws MessagingException; } diff --git a/src/main/java/de/rwth/idsg/steve/service/MailServiceDefault.java b/src/main/java/de/rwth/idsg/steve/service/MailServiceDefault.java index dd49ede4e..de7a0b2dd 100644 --- a/src/main/java/de/rwth/idsg/steve/service/MailServiceDefault.java +++ b/src/main/java/de/rwth/idsg/steve/service/MailServiceDefault.java @@ -16,6 +16,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ + package de.rwth.idsg.steve.service; import jakarta.mail.Authenticator; @@ -88,9 +89,8 @@ public void sendAsync(String subject, String body, String recipientAddresses) { } }); } - - @Override - public void send(String subject, String body, String recipientAddresses) throws MessagingException { + + private void send(String subject, String body, String recipientAddresses) throws MessagingException { MailSettings settings = getSettings(); Session session = createSession(settings); From 3ecbd70efc5e64bc5d9ca9a1b923b40ba51cc04a Mon Sep 17 00:00:00 2001 From: fnkbsi <135032168+fnkbsi@users.noreply.github.com> Date: Sat, 26 Apr 2025 18:33:36 +0200 Subject: [PATCH 35/39] NotificationService reduce DB calls on user notification of OcppStationStatusFailure and SuspendedEV event. adapt QueryForm and TransactionRepository --- .../repository/TransactionRepository.java | 3 +- .../impl/TransactionRepositoryImpl.java | 35 ++++++++++++------- .../steve/service/NotificationService.java | 18 +++++----- .../steve/web/dto/TransactionQueryForm.java | 9 +++++ 4 files changed, 42 insertions(+), 23 deletions(-) diff --git a/src/main/java/de/rwth/idsg/steve/repository/TransactionRepository.java b/src/main/java/de/rwth/idsg/steve/repository/TransactionRepository.java index ae786abe5..68a49abf5 100644 --- a/src/main/java/de/rwth/idsg/steve/repository/TransactionRepository.java +++ b/src/main/java/de/rwth/idsg/steve/repository/TransactionRepository.java @@ -31,13 +31,14 @@ */ public interface TransactionRepository { Transaction getTransaction(int transactionPk); + + Transaction getActiveTransaction(String chargeBoxId, Integer connectorId); List getTransactions(TransactionQueryForm form); void writeTransactionsCSV(TransactionQueryForm form, Writer writer); List getActiveTransactionIds(String chargeBoxId); - Integer getActiveTransactionId(String chargeBoxId, Integer connectorId); TransactionDetails getDetails(int transactionPk); } diff --git a/src/main/java/de/rwth/idsg/steve/repository/impl/TransactionRepositoryImpl.java b/src/main/java/de/rwth/idsg/steve/repository/impl/TransactionRepositoryImpl.java index 54d1afe21..81b9e155f 100644 --- a/src/main/java/de/rwth/idsg/steve/repository/impl/TransactionRepositoryImpl.java +++ b/src/main/java/de/rwth/idsg/steve/repository/impl/TransactionRepositoryImpl.java @@ -75,6 +75,24 @@ public Transaction getTransaction(int transactionPk) { return getInternal(form).fetch() .map(new TransactionMapper()).get(0); } + + @Override + public Transaction getActiveTransaction(String chargeBoxId, Integer connectorId) { + Transaction retVal = null; + TransactionQueryForm form = new TransactionQueryForm(); + form.setChargeBoxId(chargeBoxId); + form.setConnectorId(connectorId); + form.setReturnCSV(false); + form.setType(TransactionQueryForm.QueryType.ACTIVE); + Record12 + transactionRecord = getInternal(form).fetchAny(); + if (transactionRecord != null) { + TransactionMapper mapper = new TransactionMapper(); + retVal = mapper.map(transactionRecord); + } + return retVal; + } @Override public List getTransactions(TransactionQueryForm form) { @@ -99,19 +117,6 @@ public List getActiveTransactionIds(String chargeBoxId) { .fetch(TRANSACTION.TRANSACTION_PK); } - @Override - public Integer getActiveTransactionId(String chargeBoxId, Integer connectorId) { - return ctx.select(TRANSACTION.TRANSACTION_PK) - .from(TRANSACTION) - .join(CONNECTOR) - .on(TRANSACTION.CONNECTOR_PK.equal(CONNECTOR.CONNECTOR_PK)) - .and(CONNECTOR.CHARGE_BOX_ID.equal(chargeBoxId)) - .where(TRANSACTION.STOP_TIMESTAMP.isNull()) - .and(CONNECTOR.CONNECTOR_ID.equal(connectorId)) - .orderBy(TRANSACTION.TRANSACTION_PK.desc()) // to avoid fetching ghost transactions, fetch the latest - .fetchAny(TRANSACTION.TRANSACTION_PK); - } - @Override public TransactionDetails getDetails(int transactionPk) { @@ -307,6 +312,10 @@ private SelectQuery addConditions(SelectQuery selectQuery, TransactionQueryForm selectQuery.addConditions(CONNECTOR.CHARGE_BOX_ID.eq(form.getChargeBoxId())); } + if (form.isConnectorId()) { + selectQuery.addConditions(CONNECTOR.CONNECTOR_ID.eq(form.getConnectorId())); + } + if (form.isOcppIdTagSet()) { selectQuery.addConditions(TRANSACTION.ID_TAG.eq(form.getOcppIdTag())); } diff --git a/src/main/java/de/rwth/idsg/steve/service/NotificationService.java b/src/main/java/de/rwth/idsg/steve/service/NotificationService.java index 03333226c..dc307d191 100644 --- a/src/main/java/de/rwth/idsg/steve/service/NotificationService.java +++ b/src/main/java/de/rwth/idsg/steve/service/NotificationService.java @@ -48,7 +48,9 @@ import de.rwth.idsg.steve.repository.UserRepository; import de.rwth.idsg.steve.repository.dto.Transaction; import de.rwth.idsg.steve.repository.dto.UserNotificationFeature; +import de.rwth.idsg.steve.web.dto.TransactionQueryForm; import static java.lang.String.format; +import java.util.List; import jooq.steve.db.tables.records.UserRecord; /** @@ -114,7 +116,7 @@ public void ocppStationStatusFailure(OcppStationStatusFailure notification) { // user mail in separate task, so database queries don't block the execution asyncTaskExecutor.execute(() -> { try { - userNotificationocppStationStatusFailure(notification, subject); + userNotificationOcppStationStatusFailure(notification, subject); } catch (Exception e) { log.error("Failed to execute the user notification of ocppStationStatusFailure.", e); } @@ -130,12 +132,11 @@ public void ocppStationStatusFailure(OcppStationStatusFailure notification) { mailService.sendAsync(subject, addTimestamp(body)); } - private void userNotificationocppStationStatusFailure(OcppStationStatusFailure notification, String subject) { + private void userNotificationOcppStationStatusFailure(OcppStationStatusFailure notification, String subject) { - Integer transactionPk = transactionRepository.getActiveTransactionId(notification.getChargeBoxId(), + Transaction transaction = transactionRepository.getActiveTransaction(notification.getChargeBoxId(), notification.getConnectorId()); - if (transactionPk != null) { - Transaction transaction = transactionRepository.getTransaction(transactionPk); + if (transaction != null) { String ocppTag = transaction.getOcppIdTag(); if (ocppTag != null) { String eMailAddress = null; @@ -249,11 +250,10 @@ public void ocppStationStatusSuspendedEV(OcppStationStatusSuspendedEV notificati } private void userNotificationActionSuspendedEV(OcppStationStatusSuspendedEV notification, String subject) { - - Integer transactionPk = transactionRepository.getActiveTransactionId(notification.getChargeBoxId(), + + Transaction transaction= transactionRepository.getActiveTransaction(notification.getChargeBoxId(), notification.getConnectorId()); - if (transactionPk != null) { - Transaction transaction = transactionRepository.getTransaction(transactionPk); + if (transaction != null) { String ocppTag = transaction.getOcppIdTag(); if (ocppTag != null) { // No mail directly after the start of the transaction, diff --git a/src/main/java/de/rwth/idsg/steve/web/dto/TransactionQueryForm.java b/src/main/java/de/rwth/idsg/steve/web/dto/TransactionQueryForm.java index ef16d8d7c..6f5ddf970 100644 --- a/src/main/java/de/rwth/idsg/steve/web/dto/TransactionQueryForm.java +++ b/src/main/java/de/rwth/idsg/steve/web/dto/TransactionQueryForm.java @@ -39,6 +39,9 @@ public class TransactionQueryForm extends QueryForm { // Internal database Id @Schema(description = "Database primary key of the transaction") private Integer transactionPk; + + @Schema(description = "ID of the connector") + Integer connectorId; @Schema(description = "Disabled for the Web APIs. Do not use and set", hidden = true) private boolean returnCSV = false; @@ -59,6 +62,12 @@ public boolean isPeriodFromToCorrect() { public boolean isTransactionPkSet() { return transactionPk != null; } + + @Schema(hidden = true) + public boolean isConnectorId() { + return connectorId != null; + } + public QueryType getType() { return Objects.requireNonNullElse(type, QueryType.ALL); From 8d1ede80771a383694aa2307d75691e1df370292 Mon Sep 17 00:00:00 2001 From: fnkbsi <135032168+fnkbsi@users.noreply.github.com> Date: Sat, 26 Apr 2025 19:19:38 +0200 Subject: [PATCH 36/39] NotificationService reduce nested if-blocks --- .../steve/service/NotificationService.java | 197 ++++++++++-------- 1 file changed, 108 insertions(+), 89 deletions(-) diff --git a/src/main/java/de/rwth/idsg/steve/service/NotificationService.java b/src/main/java/de/rwth/idsg/steve/service/NotificationService.java index dc307d191..c768d28de 100644 --- a/src/main/java/de/rwth/idsg/steve/service/NotificationService.java +++ b/src/main/java/de/rwth/idsg/steve/service/NotificationService.java @@ -48,9 +48,7 @@ import de.rwth.idsg.steve.repository.UserRepository; import de.rwth.idsg.steve.repository.dto.Transaction; import de.rwth.idsg.steve.repository.dto.UserNotificationFeature; -import de.rwth.idsg.steve.web.dto.TransactionQueryForm; import static java.lang.String.format; -import java.util.List; import jooq.steve.db.tables.records.UserRecord; /** @@ -136,34 +134,41 @@ private void userNotificationOcppStationStatusFailure(OcppStationStatusFailure n Transaction transaction = transactionRepository.getActiveTransaction(notification.getChargeBoxId(), notification.getConnectorId()); - if (transaction != null) { - String ocppTag = transaction.getOcppIdTag(); - if (ocppTag != null) { - String eMailAddress = null; - UserRecord userRecord = new UserRecord(); - try { - userRecord = userRepository.getDetails(ocppTag).getUserRecord(); - if (userRecord.getUserNotificationFeatures() - .contains(UserNotificationFeature.OcppStationStatusFailure.toString())) { - eMailAddress = userRecord.getEMail(); - } - } catch (Exception e) { - log.error("Failed to send email (StationStatusFailure). User not found! " + e.getMessage()); - } - // send email if user with eMail address found - if (!Strings.isNullOrEmpty(eMailAddress)) { - String bodyUserMail = - format("User: %s %s \n\n Connector %d of charging station %s notifies FAULTED! \n\n Error code: %s", - userRecord.getFirstName(), - userRecord.getLastName(), - notification.getConnectorId(), - notification.getChargeBoxId(), - notification.getErrorCode() - ); - mailService.sendAsync(subject, addTimestamp(bodyUserMail), eMailAddress); - } + if (transaction == null) { + return; + } + + String ocppTag = transaction.getOcppIdTag(); + if (ocppTag == null) { + return; + } + + String eMailAddress = null; + UserRecord userRecord = new UserRecord(); + try { + userRecord = userRepository.getDetails(ocppTag).getUserRecord(); + if (userRecord.getUserNotificationFeatures() + .contains(UserNotificationFeature.OcppStationStatusFailure.toString())) { + eMailAddress = userRecord.getEMail(); } + } catch (Exception e) { + log.error("Failed to send email (StationStatusFailure). User not found! " + e.getMessage()); + } + + if (Strings.isNullOrEmpty(eMailAddress)) { + return; } + + // send email if user with eMail address found + String bodyUserMail = + format("User: %s %s \n\n Connector %d of charging station %s notifies FAULTED! \n\n Error code: %s", + userRecord.getFirstName(), + userRecord.getLastName(), + notification.getConnectorId(), + notification.getChargeBoxId(), + notification.getErrorCode() + ); + mailService.sendAsync(subject, addTimestamp(bodyUserMail), eMailAddress); } @EventListener @@ -194,31 +199,36 @@ public void ocppTransactionStarted(OcppTransactionStarted notification) { private void userNotificationOcppTransactionStarted(OcppTransactionStarted notification, String subject) { String ocppTag = notification.getParams().getIdTag(); - if (ocppTag != null) { - String eMailAddress = null; - UserRecord userRecord = new UserRecord(); - try { - userRecord = userRepository.getDetails(ocppTag).getUserRecord(); - if (userRecord.getUserNotificationFeatures() - .contains(UserNotificationFeature.OcppTransactionStarted.toString())) { - eMailAddress = userRecord.getEMail(); - } - } catch (Exception e) { - log.error("Failed to send email (StationStatusFailure). User not found! " + e.getMessage()); - } - // send email if user with eMail address found - if (!Strings.isNullOrEmpty(eMailAddress)) { - String bodyUserMail = - format("User: '%s' '%s' started transaction '%d' on connector '%s' of charging station '%s'", - userRecord.getFirstName(), - userRecord.getLastName(), - notification.getTransactionId(), - notification.getParams().getConnectorId(), - notification.getParams().getChargeBoxId() - ); - mailService.sendAsync(subject, addTimestamp(bodyUserMail), eMailAddress); + if (ocppTag == null) { + return; + } + + String eMailAddress = null; + UserRecord userRecord = new UserRecord(); + try { + userRecord = userRepository.getDetails(ocppTag).getUserRecord(); + if (userRecord.getUserNotificationFeatures() + .contains(UserNotificationFeature.OcppTransactionStarted.toString())) { + eMailAddress = userRecord.getEMail(); } + } catch (Exception e) { + log.error("Failed to send email (StationStatusFailure). User not found! " + e.getMessage()); + } + + if (Strings.isNullOrEmpty(eMailAddress)) { + return; } + + // send email if user with eMail address found + String bodyUserMail = + format("User: '%s' '%s' started transaction '%d' on connector '%s' of charging station '%s'", + userRecord.getFirstName(), + userRecord.getLastName(), + notification.getTransactionId(), + notification.getParams().getConnectorId(), + notification.getParams().getChargeBoxId() + ); + mailService.sendAsync(subject, addTimestamp(bodyUserMail), eMailAddress); } @EventListener @@ -250,44 +260,51 @@ public void ocppStationStatusSuspendedEV(OcppStationStatusSuspendedEV notificati } private void userNotificationActionSuspendedEV(OcppStationStatusSuspendedEV notification, String subject) { - + Transaction transaction= transactionRepository.getActiveTransaction(notification.getChargeBoxId(), notification.getConnectorId()); - if (transaction != null) { - String ocppTag = transaction.getOcppIdTag(); - if (ocppTag != null) { - // No mail directly after the start of the transaction, - if (notification.getTimestamp().isAfter(transaction.getStartTimestamp().plusMinutes(1))) { - String eMailAddress = null; - UserRecord userRecord = new UserRecord(); - try { - userRecord = userRepository.getDetails(ocppTag).getUserRecord(); - if (userRecord.getUserNotificationFeatures() - .contains(UserNotificationFeature.OcppStationStatusSuspendedEV.toString())) { - eMailAddress = userRecord.getEMail(); - } - } catch (Exception e) { - log.error("Failed to send email (SuspendedEV). User not found! " + e.getMessage()); - } - // send email if user with eMail address found - if (!Strings.isNullOrEmpty(eMailAddress)) { - String bodyUserMail = - format("User: %s %s \n\n Connector %d of charging station %s notifies Suspended_EV", - userRecord.getFirstName(), - userRecord.getLastName(), - notification.getConnectorId(), - notification.getChargeBoxId() - ); - mailService.sendAsync(subject, addTimestamp(bodyUserMail), eMailAddress); - } - } + if (transaction == null) { + return;} + + String ocppTag = transaction.getOcppIdTag(); + if (ocppTag == null) { + return;} + + // No mail directly after the start of the transaction, + if (!notification.getTimestamp().isAfter(transaction.getStartTimestamp().plusMinutes(1))) { + return; + } + + String eMailAddress = null; + UserRecord userRecord = new UserRecord(); + try { + userRecord = userRepository.getDetails(ocppTag).getUserRecord(); + if (userRecord.getUserNotificationFeatures() + .contains(UserNotificationFeature.OcppStationStatusSuspendedEV.toString())) { + eMailAddress = userRecord.getEMail(); } + } catch (Exception e) { + log.error("Failed to send email (SuspendedEV). User not found! " + e.getMessage()); + } + + if (Strings.isNullOrEmpty(eMailAddress)) { + return; } + + // send email if user with eMail address found + String bodyUserMail = + format("User: %s %s \n\n Connector %d of charging station %s notifies Suspended_EV", + userRecord.getFirstName(), + userRecord.getLastName(), + notification.getConnectorId(), + notification.getChargeBoxId() + ); + mailService.sendAsync(subject, addTimestamp(bodyUserMail), eMailAddress); } @EventListener public void ocppTransactionEnded(OcppTransactionEnded notification) { - String subject = format("Transaction '%s' has ended on charging station '%s'", + String subject = format("Transaction '%s' has ended on charging station '%s'", notification.getParams().getTransactionId(), notification.getParams().getChargeBoxId() ); @@ -326,14 +343,16 @@ private void userNotificationActionTransactionEnded(OcppTransactionEnded notific } // mail to user - if (!Strings.isNullOrEmpty(eMailAddress)) { - // if the Transactionstop is received within the first Minute don't send an E-Mail - if (transActParams.getStopTimestamp().isAfter(transActParams.getStartTimestamp().plusMinutes(1))) { - mailService.sendAsync(subject, - addTimestamp(createContent(transActParams, userRecord)), - eMailAddress - ); - } + if (Strings.isNullOrEmpty(eMailAddress)) { + return; + } + + // if the Transactionstop is received within the first Minute don't send an E-Mail + if (transActParams.getStopTimestamp().isAfter(transActParams.getStartTimestamp().plusMinutes(1))) { + mailService.sendAsync(subject, + addTimestamp(createContent(transActParams, userRecord)), + eMailAddress + ); } } From 7a5dba7d3b0635945ef9879668a96e78dbe60b20 Mon Sep 17 00:00:00 2001 From: fnkbsi <135032168+fnkbsi@users.noreply.github.com> Date: Sat, 26 Apr 2025 19:53:03 +0200 Subject: [PATCH 37/39] MailService: changed eMailAdresses form string to List --- .../rwth/idsg/steve/service/MailService.java | 5 ++-- .../steve/service/MailServiceDefault.java | 20 ++++++------- .../steve/service/NotificationService.java | 28 ++++++++++++------- 3 files changed, 30 insertions(+), 23 deletions(-) diff --git a/src/main/java/de/rwth/idsg/steve/service/MailService.java b/src/main/java/de/rwth/idsg/steve/service/MailService.java index 3e8944bfc..d08ac89bf 100644 --- a/src/main/java/de/rwth/idsg/steve/service/MailService.java +++ b/src/main/java/de/rwth/idsg/steve/service/MailService.java @@ -19,6 +19,7 @@ package de.rwth.idsg.steve.service; import de.rwth.idsg.steve.repository.dto.MailSettings; +import java.util.List; /** * @author Sevket Goekay @@ -31,6 +32,6 @@ public interface MailService { void sendTestMail(); void sendAsync(String subject, String body); - - void sendAsync(String subject, String body, String recipientAddresses); + + void sendAsync(String subject, String body, List eMailAddresses); } diff --git a/src/main/java/de/rwth/idsg/steve/service/MailServiceDefault.java b/src/main/java/de/rwth/idsg/steve/service/MailServiceDefault.java index de7a0b2dd..10a50747f 100644 --- a/src/main/java/de/rwth/idsg/steve/service/MailServiceDefault.java +++ b/src/main/java/de/rwth/idsg/steve/service/MailServiceDefault.java @@ -39,8 +39,8 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; -import static de.rwth.idsg.steve.utils.StringUtils.splitByComma; import static de.rwth.idsg.steve.utils.StringUtils.isValidAddress; +import java.util.ArrayList; import java.util.List; /** @@ -62,7 +62,8 @@ public MailSettings getSettings() { @Override public void sendTestMail() { try { - send("Test", "Test", ""); + List noAddress = new ArrayList(); + send("Test", "Test", noAddress); } catch (MessagingException e) { throw new SteveException("Failed to send mail", e); } @@ -72,7 +73,8 @@ public void sendTestMail() { public void sendAsync(String subject, String body) { asyncTaskExecutor.execute(() -> { try { - send(subject, body, ""); + List noAddress = new ArrayList(); + send(subject, body, noAddress); } catch (MessagingException e) { log.error("Failed to send mail", e); } @@ -80,17 +82,17 @@ public void sendAsync(String subject, String body) { } @Override - public void sendAsync(String subject, String body, String recipientAddresses) { + public void sendAsync(String subject, String body, List eMailAddresses) { asyncTaskExecutor.execute(() -> { try { - send(subject, body, recipientAddresses); + send(subject, body, eMailAddresses); } catch (MessagingException e) { log.error("Failed to send mail", e); } }); } - private void send(String subject, String body, String recipientAddresses) throws MessagingException { + private void send(String subject, String body, List eMailAddresses) throws MessagingException { MailSettings settings = getSettings(); Session session = createSession(settings); @@ -99,12 +101,8 @@ private void send(String subject, String body, String recipientAddresses) throws mail.setContent(body, "text/plain"); mail.setFrom(new InternetAddress(settings.getFrom())); - List eMailAddresses; - - if (recipientAddresses.isEmpty()) { + if (eMailAddresses.isEmpty()) { eMailAddresses = settings.getRecipients(); - } else { - eMailAddresses = splitByComma(recipientAddresses); } for (String rep : eMailAddresses) { diff --git a/src/main/java/de/rwth/idsg/steve/service/NotificationService.java b/src/main/java/de/rwth/idsg/steve/service/NotificationService.java index c768d28de..56214f877 100644 --- a/src/main/java/de/rwth/idsg/steve/service/NotificationService.java +++ b/src/main/java/de/rwth/idsg/steve/service/NotificationService.java @@ -49,6 +49,8 @@ import de.rwth.idsg.steve.repository.dto.Transaction; import de.rwth.idsg.steve.repository.dto.UserNotificationFeature; import static java.lang.String.format; +import static de.rwth.idsg.steve.utils.StringUtils.splitByComma; +import java.util.List; import jooq.steve.db.tables.records.UserRecord; /** @@ -159,6 +161,7 @@ private void userNotificationOcppStationStatusFailure(OcppStationStatusFailure n return; } + List eMailAddressList = splitByComma(eMailAddress); // send email if user with eMail address found String bodyUserMail = format("User: %s %s \n\n Connector %d of charging station %s notifies FAULTED! \n\n Error code: %s", @@ -168,7 +171,7 @@ private void userNotificationOcppStationStatusFailure(OcppStationStatusFailure n notification.getChargeBoxId(), notification.getErrorCode() ); - mailService.sendAsync(subject, addTimestamp(bodyUserMail), eMailAddress); + mailService.sendAsync(subject, addTimestamp(bodyUserMail), eMailAddressList); } @EventListener @@ -219,6 +222,7 @@ private void userNotificationOcppTransactionStarted(OcppTransactionStarted notif return; } + List eMailAddressList = splitByComma(eMailAddress); // send email if user with eMail address found String bodyUserMail = format("User: '%s' '%s' started transaction '%d' on connector '%s' of charging station '%s'", @@ -228,7 +232,7 @@ private void userNotificationOcppTransactionStarted(OcppTransactionStarted notif notification.getParams().getConnectorId(), notification.getParams().getChargeBoxId() ); - mailService.sendAsync(subject, addTimestamp(bodyUserMail), eMailAddress); + mailService.sendAsync(subject, addTimestamp(bodyUserMail), eMailAddressList); } @EventListener @@ -291,6 +295,7 @@ private void userNotificationActionSuspendedEV(OcppStationStatusSuspendedEV noti return; } + List eMailAddressList = splitByComma(eMailAddress); // send email if user with eMail address found String bodyUserMail = format("User: %s %s \n\n Connector %d of charging station %s notifies Suspended_EV", @@ -299,7 +304,7 @@ private void userNotificationActionSuspendedEV(OcppStationStatusSuspendedEV noti notification.getConnectorId(), notification.getChargeBoxId() ); - mailService.sendAsync(subject, addTimestamp(bodyUserMail), eMailAddress); + mailService.sendAsync(subject, addTimestamp(bodyUserMail), eMailAddressList); } @EventListener @@ -332,6 +337,10 @@ private void userNotificationActionTransactionEnded(OcppTransactionEnded notific Transaction transActParams = transactionRepository.getTransaction(notification.getParams().getTransactionId()); + // if the Transactionstop is received within the first Minute don't send an E-Mail + if (!transActParams.getStopTimestamp().isAfter(transActParams.getStartTimestamp().plusMinutes(1))) { + return; + } try { userRecord = userRepository.getDetails(transActParams.getOcppIdTag()).getUserRecord(); if (userRecord.getUserNotificationFeatures() @@ -347,13 +356,12 @@ private void userNotificationActionTransactionEnded(OcppTransactionEnded notific return; } - // if the Transactionstop is received within the first Minute don't send an E-Mail - if (transActParams.getStopTimestamp().isAfter(transActParams.getStartTimestamp().plusMinutes(1))) { - mailService.sendAsync(subject, - addTimestamp(createContent(transActParams, userRecord)), - eMailAddress - ); - } + List eMailAddressList = splitByComma(eMailAddress); + + mailService.sendAsync(subject, + addTimestamp(createContent(transActParams, userRecord)), + eMailAddressList + ); } // ------------------------------------------------------------------------- From d28937327c10728a731ecaf604db0a83aeece123 Mon Sep 17 00:00:00 2001 From: fnkbsi <135032168+fnkbsi@users.noreply.github.com> Date: Sun, 27 Apr 2025 11:08:25 +0200 Subject: [PATCH 38/39] style check improvements --- .../rwth/idsg/steve/repository/TransactionRepository.java | 2 +- .../idsg/steve/repository/dto/UserNotificationFeature.java | 5 +++-- .../steve/repository/impl/TransactionRepositoryImpl.java | 6 +++--- .../de/rwth/idsg/steve/service/NotificationService.java | 5 +++-- 4 files changed, 10 insertions(+), 8 deletions(-) diff --git a/src/main/java/de/rwth/idsg/steve/repository/TransactionRepository.java b/src/main/java/de/rwth/idsg/steve/repository/TransactionRepository.java index 68a49abf5..840c2cfff 100644 --- a/src/main/java/de/rwth/idsg/steve/repository/TransactionRepository.java +++ b/src/main/java/de/rwth/idsg/steve/repository/TransactionRepository.java @@ -31,7 +31,7 @@ */ public interface TransactionRepository { Transaction getTransaction(int transactionPk); - + Transaction getActiveTransaction(String chargeBoxId, Integer connectorId); List getTransactions(TransactionQueryForm form); diff --git a/src/main/java/de/rwth/idsg/steve/repository/dto/UserNotificationFeature.java b/src/main/java/de/rwth/idsg/steve/repository/dto/UserNotificationFeature.java index 247251485..9616d0070 100644 --- a/src/main/java/de/rwth/idsg/steve/repository/dto/UserNotificationFeature.java +++ b/src/main/java/de/rwth/idsg/steve/repository/dto/UserNotificationFeature.java @@ -39,7 +39,8 @@ public enum UserNotificationFeature { // Ocpp related // - //OcppStationBooted(" a charging station sends a boot notification (Note: This activates notifications about failed connection attempts for unregistered JSON stations, as well)"), + //OcppStationBooted(" a charging station sends a boot notification (Note: This activates notifications " + // + "about failed connection attempts for unregistered JSON stations, as well)"), OcppStationStatusFailure(" a connector gets faulted"), //OcppStationWebSocketConnected(" a JSON charging station connects"), //OcppStationWebSocketDisconnected(" a JSON charging station disconnects"), @@ -69,4 +70,4 @@ public static List splitFeatures(String str) { public static String joinFeatures(List enablesFeatures) { return joinByComma(enablesFeatures); } -} \ No newline at end of file +} diff --git a/src/main/java/de/rwth/idsg/steve/repository/impl/TransactionRepositoryImpl.java b/src/main/java/de/rwth/idsg/steve/repository/impl/TransactionRepositoryImpl.java index 81b9e155f..9fd57b143 100644 --- a/src/main/java/de/rwth/idsg/steve/repository/impl/TransactionRepositoryImpl.java +++ b/src/main/java/de/rwth/idsg/steve/repository/impl/TransactionRepositoryImpl.java @@ -75,7 +75,7 @@ public Transaction getTransaction(int transactionPk) { return getInternal(form).fetch() .map(new TransactionMapper()).get(0); } - + @Override public Transaction getActiveTransaction(String chargeBoxId, Integer connectorId) { Transaction retVal = null; @@ -91,7 +91,7 @@ public Transaction getActiveTransaction(String chargeBoxId, Integer connectorId) TransactionMapper mapper = new TransactionMapper(); retVal = mapper.map(transactionRecord); } - return retVal; + return retVal; } @Override @@ -315,7 +315,7 @@ private SelectQuery addConditions(SelectQuery selectQuery, TransactionQueryForm if (form.isConnectorId()) { selectQuery.addConditions(CONNECTOR.CONNECTOR_ID.eq(form.getConnectorId())); } - + if (form.isOcppIdTagSet()) { selectQuery.addConditions(TRANSACTION.ID_TAG.eq(form.getOcppIdTag())); } diff --git a/src/main/java/de/rwth/idsg/steve/service/NotificationService.java b/src/main/java/de/rwth/idsg/steve/service/NotificationService.java index 56214f877..69d21db17 100644 --- a/src/main/java/de/rwth/idsg/steve/service/NotificationService.java +++ b/src/main/java/de/rwth/idsg/steve/service/NotificationService.java @@ -265,10 +265,11 @@ public void ocppStationStatusSuspendedEV(OcppStationStatusSuspendedEV notificati private void userNotificationActionSuspendedEV(OcppStationStatusSuspendedEV notification, String subject) { - Transaction transaction= transactionRepository.getActiveTransaction(notification.getChargeBoxId(), + Transaction transaction = transactionRepository.getActiveTransaction(notification.getChargeBoxId(), notification.getConnectorId()); if (transaction == null) { - return;} + return; + } String ocppTag = transaction.getOcppIdTag(); if (ocppTag == null) { From a29b5947f001ae4500a4f9232dc4f104efbb7049 Mon Sep 17 00:00:00 2001 From: fnkbsi <135032168+fnkbsi@users.noreply.github.com> Date: Sun, 27 Apr 2025 11:18:25 +0200 Subject: [PATCH 39/39] Update Licence --- .../idsg/steve/repository/dto/UserNotificationFeature.java | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/main/java/de/rwth/idsg/steve/repository/dto/UserNotificationFeature.java b/src/main/java/de/rwth/idsg/steve/repository/dto/UserNotificationFeature.java index 9616d0070..63f5d9904 100644 --- a/src/main/java/de/rwth/idsg/steve/repository/dto/UserNotificationFeature.java +++ b/src/main/java/de/rwth/idsg/steve/repository/dto/UserNotificationFeature.java @@ -1,6 +1,6 @@ /* * SteVe - SteckdosenVerwaltung - https://github.com/steve-community/steve - * Copyright (C) 2013-2024 SteVe Community Team + * Copyright (C) 2013-2025 SteVe Community Team * All Rights Reserved. * * This program is free software: you can redistribute it and/or modify @@ -19,10 +19,6 @@ package de.rwth.idsg.steve.repository.dto; -//import static de.rwth.idsg.steve.utils.StringUtils.joinByComma; -//import static de.rwth.idsg.steve.utils.StringUtils.splitByComma; -//import java.util.List; -//import java.util.stream.Collectors; import static de.rwth.idsg.steve.utils.StringUtils.joinByComma; import static de.rwth.idsg.steve.utils.StringUtils.splitByComma; import java.util.List;