Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WebApi of remote start, remote stop and connector status #1291

Open
wants to merge 53 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
53 commits
Select commit Hold shift + click to select a range
be40edd
set webapi.value in dev
Oct 20, 2023
9d79bdd
add constructor with "caller" value at tasks RemoteStart, RemoteStop …
Oct 20, 2023
d2f54b5
ChargePointRepository: add method getChargePointSelect(String ChageBo…
Oct 20, 2023
cdcb3ee
TransactionRepository: add methods getActiveTransactionId(String char…
Oct 20, 2023
3a3e575
TaskOverview: added swagger.annotations
Oct 20, 2023
08d7fa6
ChargePointService12_Client: add methods which uses new task construc…
Oct 20, 2023
9b580be
Added a first draft of RemoteStartStopREstController, also added data…
Oct 20, 2023
1771e16
Removed tailing spaces
Oct 20, 2023
7417a9e
ConnectorStatusForm: added ApiModelProperty annotation;
Oct 20, 2023
59b31cc
OcppJsonStatus: added ApiModelProperty annotation and JasonIgnor anno…
Oct 20, 2023
bf14cb1
ConnectorStatus: added ApiModelProperty annotation to all fields and …
Oct 20, 2023
55ac2ec
no functional changes
Oct 20, 2023
7fc9c5b
added dto list of connector status for Api
Oct 20, 2023
c39fafa
added connector REST controller
Oct 20, 2023
5a04018
RemoteStartStopController: methode postRemoteStartTx now checks if th…
Oct 25, 2023
ed42990
RemoteStartStopController: moved the task related methods to the new …
Oct 25, 2023
0e724a4
TaskReskController: removed unused import BadRequest...
Oct 25, 2023
9a58541
style
Oct 25, 2023
41017aa
removed unused dto ApiConnectorList
Oct 25, 2023
9b708c8
ApiTaskInfo: removed unused/out commented imports
Oct 25, 2023
e09d14c
REmoteStartStopREstController: added check for active tasks on charge…
Oct 25, 2023
5517cd1
add dto ApiConnectorList
Oct 25, 2023
bc02600
ConnectorRestController: rename method
Oct 25, 2023
770c594
ApiConnectorList: removed unnessesary semicolon
Oct 25, 2023
b5211f7
ApiChargePointStart: correct ApiModelProperty value
Nov 2, 2023
8a56922
Merge branch 'steve-community:master' into ApiRemoteStartStop
fnkbsi Nov 2, 2023
6b77e92
ConnectorStatusForm add menber for Strategy of listing the connector
Nov 5, 2023
4c01d02
HomeController & ConnectorRestController: option to choose between Pr…
Nov 5, 2023
703867b
connectorStatus.jsp: add query field for strategy the connector listing
Nov 5, 2023
d41e059
style improvement
Nov 6, 2023
5de4edf
Merge branch 'steve-community:master' into ApiRemoteStartStop
fnkbsi Nov 28, 2023
86cef45
typo and trailing spaces
Dec 6, 2023
875e4fa
Merge branch 'steve-community:master' into ApiRemoteStartStop
fnkbsi Dec 13, 2023
9908064
Merge branch 'steve-community:master' into ApiRemoteStartStop
fnkbsi Dec 24, 2023
de45bb3
Merge branch 'steve-community:master' into ApiRemoteStartStop
fnkbsi Jan 15, 2024
b358166
TaskRestController: changed address to /api/v1/tasks;
Jan 16, 2024
cb7576d
Update license headers
Jan 16, 2024
f087287
getActiveTransactionId(String chargeBox, Integer connectorId) method,…
Jan 23, 2024
85ccc66
TransactionRepository: add method getTransaction(int transactionPk); …
Jan 23, 2024
b46859a
RemoteStartStopRestController adapt to methode changes at Transaction…
Jan 23, 2024
a790ef6
RemoteStartStopRestController: changed style of switch case
Jan 23, 2024
b3da4c3
TransactionRepository: methode getTransaction(int transactionPk) usin…
Jan 23, 2024
52c07da
Merge branch 'steve-community:master' into ApiRemoteStartStop
fnkbsi Feb 1, 2024
c30d163
Merge branch 'steve-community:master' into ApiRemoteStartStop
fnkbsi Feb 6, 2024
83efc74
smaller style improvements
Feb 6, 2024
a2dbd95
RemoteStartStopController: removed trailing spaces
Feb 6, 2024
7a34ce7
Merge branch 'steve-community:master' into ApiRemoteStartStop
fnkbsi Feb 19, 2024
7aaa690
Merge branch 'steve-community:master' into ApiRemoteStartStop
fnkbsi Mar 28, 2024
c7e795d
Resolve Conflicts: Caused by removing the flag firstArrivingMeterValu…
fnkbsi Apr 3, 2024
e13a6aa
Merge branch 'master' into ApiRemoteStartStop
fnkbsi Apr 3, 2024
aec19e3
Merge branch 'steve-community:master' into ApiRemoteStartStop
fnkbsi Apr 19, 2024
7c0c773
Merge branch 'steve-community:master' into ApiRemoteStartStop
fnkbsi May 13, 2024
fd4f789
Merge branch 'steve-community:master' into ApiRemoteStartStop
fnkbsi Jun 19, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions src/main/java/de/rwth/idsg/steve/ocpp/CommunicationTask.java
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,10 @@ public CommunicationTask(OcppVersion ocppVersion, S params) {
this(ocppVersion, params, TaskOrigin.INTERNAL, "SteVe");
}

public CommunicationTask(OcppVersion ocppVersion, S params, String caller) {
this(ocppVersion, params, TaskOrigin.EXTERNAL, caller);
}

/**
* Do not expose the constructor, make it package-private
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@ public RemoteStartTransactionTask(OcppVersion ocppVersion, RemoteStartTransactio
super(ocppVersion, params);
}

public RemoteStartTransactionTask(OcppVersion ocppVersion, RemoteStartTransactionParams params, String caller) {
super(ocppVersion, params, caller);
}

@Override
public OcppCallback<String> defaultCallback() {
return new StringOcppCallback();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@ public RemoteStopTransactionTask(OcppVersion ocppVersion, RemoteStopTransactionP
super(ocppVersion, params);
}

public RemoteStopTransactionTask(OcppVersion ocppVersion, RemoteStopTransactionParams params, String caller) {
super(ocppVersion, params, caller);
}

@Override
public OcppCallback<String> defaultCallback() {
return new StringOcppCallback();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@ public UnlockConnectorTask(OcppVersion ocppVersion, UnlockConnectorParams params
super(ocppVersion, params);
}

public UnlockConnectorTask(OcppVersion ocppVersion, UnlockConnectorParams params, String caller) {
super(ocppVersion, params, caller);
}

@Override
public OcppCallback<String> defaultCallback() {
return new StringOcppCallback();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ default List<ChargePointSelect> getChargePointSelect(OcppProtocol protocol, List
return getChargePointSelect(protocol, inStatusFilter, Collections.emptyList());
}

List<ChargePointSelect> getChargePointSelect(String chageBoxID);

List<String> getChargeBoxIds();
Map<String, Integer> getChargeBoxIdPkPair(List<String> chargeBoxIdList);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,14 @@
* @since 19.08.2014
*/
public interface TransactionRepository {
Transaction getTransaction(int transactionPk);

List<Transaction> getTransactions(TransactionQueryForm form);

void writeTransactionsCSV(TransactionQueryForm form, Writer writer);

List<Integer> getActiveTransactionIds(String chargeBoxId);
Integer getActiveTransactionId(String chargeBoxId, Integer connectorId);

TransactionDetails getDetails(int transactionPk);
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@
*/
package de.rwth.idsg.steve.repository.dto;

import com.fasterxml.jackson.annotation.JsonIgnore;
import de.rwth.idsg.steve.ocpp.OcppProtocol;
import io.swagger.annotations.ApiModelProperty;
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
Expand All @@ -32,17 +34,32 @@
@Getter
@Builder
public final class ConnectorStatus {
private final String chargeBoxId, timeStamp, status, errorCode;
private final int chargeBoxPk, connectorId;
@JsonIgnore
@ApiModelProperty(value = "Charge Box DB key", hidden = true)
private final int chargeBoxPk;
@ApiModelProperty(value = "Charge Box ID")
private final String chargeBoxId;
@ApiModelProperty(value = "Connector ID")
private final int connectorId;

@ApiModelProperty(value = "Status")
private final String status;
@ApiModelProperty(value = "Error code")
private final String errorCode;

@ApiModelProperty(value = "Timestamp")
private final String timeStamp;
// For additional internal processing. Not related to the humanized
// String version above, which is for representation on frontend
@ApiModelProperty(value = "Timestamp of the status")
private final DateTime statusTimestamp;

@ApiModelProperty(value = "OCPP version")
private final OcppProtocol ocppProtocol;

// This is true, if the chargeBox this connector belongs to is a WS/JSON station
// and it is disconnected at the moment of building this DTO.
@ApiModelProperty(value = "Json and Disconnected")
@Setter
@Builder.Default
private boolean jsonAndDisconnected = false;
Expand Down
14 changes: 12 additions & 2 deletions src/main/java/de/rwth/idsg/steve/repository/dto/TaskOverview.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
package de.rwth.idsg.steve.repository.dto;

import de.rwth.idsg.steve.ocpp.TaskOrigin;
import io.swagger.annotations.ApiModelProperty;
import lombok.Builder;
import lombok.EqualsAndHashCode;
import lombok.Getter;
Expand All @@ -32,8 +33,17 @@
@EqualsAndHashCode
@Builder
public final class TaskOverview implements Comparable<TaskOverview> {
private final int taskId, responseCount, requestCount;
private final DateTime start, end;
@ApiModelProperty(value = "Task ID")
private final int taskId;
@ApiModelProperty(value = "Response count")
private final int responseCount;
@ApiModelProperty(value = "Request count")
private final int requestCount;
@ApiModelProperty(value = "Starttime")
private final DateTime start;
@ApiModelProperty(value = "Endtime")
private final DateTime end;
@ApiModelProperty(value = "Task triggered internal or external")
private final TaskOrigin origin;

/**
Expand All @@ -41,6 +51,6 @@
*/
@Override
public int compareTo(TaskOverview o) {
return (o.taskId - this.taskId);

Check warning on line 54 in src/main/java/de/rwth/idsg/steve/repository/dto/TaskOverview.java

View workflow job for this annotation

GitHub Actions / pmd

Useless parentheses.

Parenthesized expressions are used to override the default operator precedence rules. Parentheses whose removal would not change the relative nesting of operators are unnecessary, because they don't change the semantics of the enclosing expression. Some parentheses that strictly speaking are unnecessary, may still be considered useful for readability. This rule allows to ignore violations on two kinds of unnecessary parentheses: - "Clarifying" parentheses, which separate operators of difference precedence. While unnecessary, they make precedence rules explicit, which may be useful for rarely used operators. For example: ```java (a + b) & c // is equivalent to `a + b & c`, but probably clearer ``` Unset the property `ignoreClarifying` to report them. - "Balancing" parentheses, which are unnecessary but visually balance out another pair of parentheses around an equality operator. For example, those two expressions are equivalent: ```java (a == null) != (b == null) a == null != (b == null) ``` The parentheses on the right are required, and the parentheses on the left are just more visually pleasing. Unset the property `ignoreBalancing` to report them. UselessParentheses (Priority: 4, Ruleset: Code Style) https://docs.pmd-code.org/pmd-doc-7.2.0/pmd_rules_java_codestyle.html#uselessparentheses
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,367 +16,377 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.rwth.idsg.steve.repository.impl;

import de.rwth.idsg.steve.SteveException;
import de.rwth.idsg.steve.ocpp.OcppProtocol;
import de.rwth.idsg.steve.repository.AddressRepository;
import de.rwth.idsg.steve.repository.ChargePointRepository;
import de.rwth.idsg.steve.repository.dto.ChargePoint;
import de.rwth.idsg.steve.repository.dto.ChargePointSelect;
import de.rwth.idsg.steve.repository.dto.ConnectorStatus;
import de.rwth.idsg.steve.utils.DateTimeUtils;
import de.rwth.idsg.steve.web.dto.ChargePointForm;
import de.rwth.idsg.steve.web.dto.ChargePointQueryForm;
import de.rwth.idsg.steve.web.dto.ConnectorStatusForm;
import jooq.steve.db.tables.records.AddressRecord;
import jooq.steve.db.tables.records.ChargeBoxRecord;
import lombok.extern.slf4j.Slf4j;
import ocpp.cs._2015._10.RegistrationStatus;
import org.joda.time.DateTime;
import org.jooq.Condition;
import org.jooq.DSLContext;
import org.jooq.Field;
import org.jooq.Record1;
import org.jooq.Record5;
import org.jooq.Result;
import org.jooq.SelectConditionStep;
import org.jooq.SelectQuery;
import org.jooq.Table;
import org.jooq.exception.DataAccessException;
import org.jooq.impl.DSL;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import org.springframework.util.CollectionUtils;

import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;

import static de.rwth.idsg.steve.utils.CustomDSL.date;
import static de.rwth.idsg.steve.utils.CustomDSL.includes;
import static jooq.steve.db.tables.ChargeBox.CHARGE_BOX;
import static jooq.steve.db.tables.Connector.CONNECTOR;
import static jooq.steve.db.tables.ConnectorStatus.CONNECTOR_STATUS;

/**
* @author Sevket Goekay <[email protected]>
* @since 14.08.2014
*/
@Slf4j
@Repository
public class ChargePointRepositoryImpl implements ChargePointRepository {

private final DSLContext ctx;
private final AddressRepository addressRepository;

@Autowired
public ChargePointRepositoryImpl(DSLContext ctx, AddressRepository addressRepository) {
this.ctx = ctx;
this.addressRepository = addressRepository;
}

@Override
public Optional<String> getRegistrationStatus(String chargeBoxId) {
String status = ctx.select(CHARGE_BOX.REGISTRATION_STATUS)
.from(CHARGE_BOX)
.where(CHARGE_BOX.CHARGE_BOX_ID.eq(chargeBoxId))
.fetchOne(CHARGE_BOX.REGISTRATION_STATUS);

return Optional.ofNullable(status);
}

@Override
public List<ChargePointSelect> getChargePointSelect(OcppProtocol protocol, List<String> inStatusFilter, List<String> chargeBoxIdFilter) {
Condition chargeBoxIdCondition = CollectionUtils.isEmpty(chargeBoxIdFilter)
? DSL.trueCondition()
: CHARGE_BOX.CHARGE_BOX_ID.in(chargeBoxIdFilter);

return ctx.select(CHARGE_BOX.CHARGE_BOX_ID, CHARGE_BOX.ENDPOINT_ADDRESS)
.from(CHARGE_BOX)
.where(CHARGE_BOX.OCPP_PROTOCOL.equal(protocol.getCompositeValue()))
.and(CHARGE_BOX.ENDPOINT_ADDRESS.isNotNull())
.and(CHARGE_BOX.REGISTRATION_STATUS.in(inStatusFilter))
.and(chargeBoxIdCondition)
.fetch()
.map(r -> new ChargePointSelect(protocol.getTransport(), r.value1(), r.value2()));
}

@Override // returns List of zero or one ChargeBox
public List<ChargePointSelect> getChargePointSelect(String chageBoxID) {
return ctx.select(CHARGE_BOX.CHARGE_BOX_ID, CHARGE_BOX.ENDPOINT_ADDRESS, CHARGE_BOX.OCPP_PROTOCOL)
.from(CHARGE_BOX)
.where(CHARGE_BOX.CHARGE_BOX_ID.eq(chageBoxID))
.fetch()
.map(r -> new ChargePointSelect(OcppProtocol.fromCompositeValue(r.value3()).getTransport(),
r.value1(), r.value2()));
}

@Override
public List<String> getChargeBoxIds() {
return ctx.select(CHARGE_BOX.CHARGE_BOX_ID)
.from(CHARGE_BOX)
.fetch(CHARGE_BOX.CHARGE_BOX_ID);
}

@Override
public Map<String, Integer> getChargeBoxIdPkPair(List<String> chargeBoxIdList) {
return ctx.select(CHARGE_BOX.CHARGE_BOX_ID, CHARGE_BOX.CHARGE_BOX_PK)
.from(CHARGE_BOX)
.where(CHARGE_BOX.CHARGE_BOX_ID.in(chargeBoxIdList))
.fetchMap(CHARGE_BOX.CHARGE_BOX_ID, CHARGE_BOX.CHARGE_BOX_PK);
}

@Override
public List<ChargePoint.Overview> getOverview(ChargePointQueryForm form) {
return getOverviewInternal(form)
.map(r -> ChargePoint.Overview.builder()
.chargeBoxPk(r.value1())
.chargeBoxId(r.value2())
.description(r.value3())
.ocppProtocol(r.value4())
.lastHeartbeatTimestampDT(r.value5())
.lastHeartbeatTimestamp(DateTimeUtils.humanize(r.value5()))
.build()
);
}

@SuppressWarnings("unchecked")
private Result<Record5<Integer, String, String, String, DateTime>> getOverviewInternal(ChargePointQueryForm form) {
SelectQuery selectQuery = ctx.selectQuery();
selectQuery.addFrom(CHARGE_BOX);
selectQuery.addSelect(
CHARGE_BOX.CHARGE_BOX_PK,
CHARGE_BOX.CHARGE_BOX_ID,
CHARGE_BOX.DESCRIPTION,
CHARGE_BOX.OCPP_PROTOCOL,
CHARGE_BOX.LAST_HEARTBEAT_TIMESTAMP
);

if (form.isSetOcppVersion()) {

// http:https://dev.mysql.com/doc/refman/5.7/en/pattern-matching.html
selectQuery.addConditions(CHARGE_BOX.OCPP_PROTOCOL.like(form.getOcppVersion().getValue() + "_"));
}

if (form.isSetDescription()) {
selectQuery.addConditions(includes(CHARGE_BOX.DESCRIPTION, form.getDescription()));
}

if (form.isSetChargeBoxId()) {
selectQuery.addConditions(includes(CHARGE_BOX.CHARGE_BOX_ID, form.getChargeBoxId()));
}

switch (form.getHeartbeatPeriod()) {
case ALL:
break;

case TODAY:
selectQuery.addConditions(
date(CHARGE_BOX.LAST_HEARTBEAT_TIMESTAMP).eq(date(DateTime.now()))
);
break;

case YESTERDAY:
selectQuery.addConditions(
date(CHARGE_BOX.LAST_HEARTBEAT_TIMESTAMP).eq(date(DateTime.now().minusDays(1)))
);
break;

case EARLIER:
selectQuery.addConditions(
date(CHARGE_BOX.LAST_HEARTBEAT_TIMESTAMP).lessThan(date(DateTime.now().minusDays(1)))
);
break;

default:
throw new SteveException("Unknown enum type");
}

// Default order
selectQuery.addOrderBy(CHARGE_BOX.CHARGE_BOX_PK.asc());

return selectQuery.fetch();
}

@Override
public ChargePoint.Details getDetails(int chargeBoxPk) {
ChargeBoxRecord cbr = ctx.selectFrom(CHARGE_BOX)
.where(CHARGE_BOX.CHARGE_BOX_PK.equal(chargeBoxPk))
.fetchOne();

if (cbr == null) {
throw new SteveException("Charge point not found");
}

AddressRecord ar = addressRepository.get(ctx, cbr.getAddressPk());

return new ChargePoint.Details(cbr, ar);
}

@Override
public List<ConnectorStatus> getChargePointConnectorStatus(ConnectorStatusForm form) {
// find out the latest timestamp for each connector
Field<Integer> t1Pk = CONNECTOR_STATUS.CONNECTOR_PK.as("t1_pk");
Field<DateTime> t1TsMax = DSL.max(CONNECTOR_STATUS.STATUS_TIMESTAMP).as("t1_ts_max");
Table<?> t1 = ctx.select(t1Pk, t1TsMax)
.from(CONNECTOR_STATUS)
.groupBy(CONNECTOR_STATUS.CONNECTOR_PK)
.asTable("t1");

// get the status table with latest timestamps only
Field<Integer> t2Pk = CONNECTOR_STATUS.CONNECTOR_PK.as("t2_pk");
Field<DateTime> t2Ts = CONNECTOR_STATUS.STATUS_TIMESTAMP.as("t2_ts");
Field<String> t2Status = CONNECTOR_STATUS.STATUS.as("t2_status");
Field<String> t2Error = CONNECTOR_STATUS.ERROR_CODE.as("t2_error");
Table<?> t2 = ctx.selectDistinct(t2Pk, t2Ts, t2Status, t2Error)
.from(CONNECTOR_STATUS)
.join(t1)
.on(CONNECTOR_STATUS.CONNECTOR_PK.equal(t1.field(t1Pk)))
.and(CONNECTOR_STATUS.STATUS_TIMESTAMP.equal(t1.field(t1TsMax)))
.asTable("t2");

// https://github.com/steve-community/steve/issues/691
Condition chargeBoxCondition = CHARGE_BOX.REGISTRATION_STATUS.eq(RegistrationStatus.ACCEPTED.value());

if (form != null && form.getChargeBoxId() != null) {
chargeBoxCondition = chargeBoxCondition.and(CHARGE_BOX.CHARGE_BOX_ID.eq(form.getChargeBoxId()));
}

final Condition statusCondition;
if (form == null || form.getStatus() == null) {
statusCondition = DSL.noCondition();
} else {
statusCondition = t2.field(t2Status).eq(form.getStatus());
}

return ctx.select(
CHARGE_BOX.CHARGE_BOX_PK,
CONNECTOR.CHARGE_BOX_ID,
CONNECTOR.CONNECTOR_ID,
t2.field(t2Ts),
t2.field(t2Status),
t2.field(t2Error),
CHARGE_BOX.OCPP_PROTOCOL)
.from(t2)
.join(CONNECTOR)
.on(CONNECTOR.CONNECTOR_PK.eq(t2.field(t2Pk)))
.join(CHARGE_BOX)
.on(CHARGE_BOX.CHARGE_BOX_ID.eq(CONNECTOR.CHARGE_BOX_ID))
.where(chargeBoxCondition, statusCondition)
.orderBy(t2.field(t2Ts).desc())
.fetch()
.map(r -> ConnectorStatus.builder()
.chargeBoxPk(r.value1())
.chargeBoxId(r.value2())
.connectorId(r.value3())
.timeStamp(DateTimeUtils.humanize(r.value4()))
.statusTimestamp(r.value4())
.status(r.value5())
.errorCode(r.value6())
.ocppProtocol(r.value7() == null ? null : OcppProtocol.fromCompositeValue(r.value7()))
.build()
);
}

@Override
public List<Integer> getNonZeroConnectorIds(String chargeBoxId) {
return ctx.select(CONNECTOR.CONNECTOR_ID)
.from(CONNECTOR)
.where(CONNECTOR.CHARGE_BOX_ID.equal(chargeBoxId))
.and(CONNECTOR.CONNECTOR_ID.notEqual(0))
.fetch(CONNECTOR.CONNECTOR_ID);
}

@Override
public void addChargePointList(List<String> chargeBoxIdList) {
List<ChargeBoxRecord> batch = chargeBoxIdList.stream()
.map(s -> ctx.newRecord(CHARGE_BOX)
.setChargeBoxId(s)
.setInsertConnectorStatusAfterTransactionMsg(false))
.collect(Collectors.toList());

ctx.batchInsert(batch).execute();
}

@Override
public int addChargePoint(ChargePointForm form) {
return ctx.transactionResult(configuration -> {
DSLContext ctx = DSL.using(configuration);
try {
Integer addressId = addressRepository.updateOrInsert(ctx, form.getAddress());
return addChargePointInternal(ctx, form, addressId);

} catch (DataAccessException e) {
throw new SteveException("Failed to add the charge point with chargeBoxId '%s'",
form.getChargeBoxId(), e);
}
});
}

@Override
public void updateChargePoint(ChargePointForm form) {
ctx.transaction(configuration -> {
DSLContext ctx = DSL.using(configuration);
try {
Integer addressId = addressRepository.updateOrInsert(ctx, form.getAddress());
updateChargePointInternal(ctx, form, addressId);

} catch (DataAccessException e) {
throw new SteveException("Failed to update the charge point with chargeBoxId '%s'",
form.getChargeBoxId(), e);
}
});
}

@Override
public void deleteChargePoint(int chargeBoxPk) {
ctx.transaction(configuration -> {
DSLContext ctx = DSL.using(configuration);
try {
addressRepository.delete(ctx, selectAddressId(chargeBoxPk));
deleteChargePointInternal(ctx, chargeBoxPk);

} catch (DataAccessException e) {
throw new SteveException("Failed to delete the charge point", e);
}
});
}

// -------------------------------------------------------------------------
// Helpers
// -------------------------------------------------------------------------

private SelectConditionStep<Record1<Integer>> selectAddressId(int chargeBoxPk) {
return ctx.select(CHARGE_BOX.ADDRESS_PK)
.from(CHARGE_BOX)
.where(CHARGE_BOX.CHARGE_BOX_PK.eq(chargeBoxPk));
}

private int addChargePointInternal(DSLContext ctx, ChargePointForm form, Integer addressPk) {
return ctx.insertInto(CHARGE_BOX)
.set(CHARGE_BOX.CHARGE_BOX_ID, form.getChargeBoxId())
.set(CHARGE_BOX.DESCRIPTION, form.getDescription())
.set(CHARGE_BOX.LOCATION_LATITUDE, form.getLocationLatitude())
.set(CHARGE_BOX.LOCATION_LONGITUDE, form.getLocationLongitude())
.set(CHARGE_BOX.INSERT_CONNECTOR_STATUS_AFTER_TRANSACTION_MSG, form.getInsertConnectorStatusAfterTransactionMsg())
.set(CHARGE_BOX.REGISTRATION_STATUS, form.getRegistrationStatus())
.set(CHARGE_BOX.NOTE, form.getNote())
.set(CHARGE_BOX.ADMIN_ADDRESS, form.getAdminAddress())
.set(CHARGE_BOX.ADDRESS_PK, addressPk)
.returning(CHARGE_BOX.CHARGE_BOX_PK)
.fetchOne()
.getChargeBoxPk();
}

private void updateChargePointInternal(DSLContext ctx, ChargePointForm form, Integer addressPk) {
ctx.update(CHARGE_BOX)
.set(CHARGE_BOX.DESCRIPTION, form.getDescription())
.set(CHARGE_BOX.LOCATION_LATITUDE, form.getLocationLatitude())
.set(CHARGE_BOX.LOCATION_LONGITUDE, form.getLocationLongitude())
.set(CHARGE_BOX.INSERT_CONNECTOR_STATUS_AFTER_TRANSACTION_MSG, form.getInsertConnectorStatusAfterTransactionMsg())
.set(CHARGE_BOX.REGISTRATION_STATUS, form.getRegistrationStatus())
.set(CHARGE_BOX.NOTE, form.getNote())
.set(CHARGE_BOX.ADMIN_ADDRESS, form.getAdminAddress())
.set(CHARGE_BOX.ADDRESS_PK, addressPk)
.where(CHARGE_BOX.CHARGE_BOX_PK.equal(form.getChargeBoxPk()))
.execute();
}

private void deleteChargePointInternal(DSLContext ctx, int chargeBoxPk) {
ctx.delete(CHARGE_BOX)
.where(CHARGE_BOX.CHARGE_BOX_PK.equal(chargeBoxPk))
.execute();
}
}

Check warning on line 392 in src/main/java/de/rwth/idsg/steve/repository/impl/ChargePointRepositoryImpl.java

View workflow job for this annotation

GitHub Actions / pmd

Too many static imports may lead to messy code

If you overuse the static import feature, it can make your program unreadable and unmaintainable, polluting its namespace with all the static members you import. Readers of your code (including you, a few months after you wrote it) will not know which class a static member comes from (Sun 1.5 Language Guide). TooManyStaticImports (Priority: 3, Ruleset: Code Style) https://docs.pmd-code.org/pmd-doc-7.2.0/pmd_rules_java_codestyle.html#toomanystaticimports
Original file line number Diff line number Diff line change
Expand Up @@ -16,332 +16,354 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.rwth.idsg.steve.repository.impl;

import de.rwth.idsg.steve.SteveException;
import de.rwth.idsg.steve.repository.TransactionRepository;
import de.rwth.idsg.steve.repository.dto.Transaction;
import de.rwth.idsg.steve.repository.dto.TransactionDetails;
import de.rwth.idsg.steve.utils.DateTimeUtils;
import de.rwth.idsg.steve.web.dto.TransactionQueryForm;
import jooq.steve.db.enums.TransactionStopEventActor;
import jooq.steve.db.tables.records.ConnectorMeterValueRecord;
import jooq.steve.db.tables.records.TransactionStartRecord;
import org.joda.time.DateTime;
import org.jooq.Condition;
import org.jooq.DSLContext;
import org.jooq.Field;
import org.jooq.Record12;
import org.jooq.Record9;
import org.jooq.RecordMapper;
import org.jooq.SelectQuery;
import org.jooq.Table;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;

import java.io.Writer;
import java.util.List;

import static de.rwth.idsg.steve.utils.CustomDSL.date;
import static jooq.steve.db.tables.ChargeBox.CHARGE_BOX;
import static jooq.steve.db.tables.Connector.CONNECTOR;
import static jooq.steve.db.tables.ConnectorMeterValue.CONNECTOR_METER_VALUE;
import static jooq.steve.db.tables.OcppTag.OCPP_TAG;
import static jooq.steve.db.tables.Transaction.TRANSACTION;
import static jooq.steve.db.tables.TransactionStart.TRANSACTION_START;

/**
* @author Sevket Goekay <[email protected]>
* @since 14.08.2014
*/
@Repository
public class TransactionRepositoryImpl implements TransactionRepository {

private final DSLContext ctx;

@Autowired
public TransactionRepositoryImpl(DSLContext ctx) {
this.ctx = ctx;
}

@Override
public Transaction getTransaction(int transactionPk) {
TransactionQueryForm form = new TransactionQueryForm();
form.setTransactionPk(transactionPk);
form.setReturnCSV(false);
form.setType(TransactionQueryForm.QueryType.ALL);
return getInternal(form).fetchAny(new TransactionMapper());
}

@Override
public List<Transaction> getTransactions(TransactionQueryForm form) {
return getInternal(form).fetch()
.map(new TransactionMapper());
}

@Override
public void writeTransactionsCSV(TransactionQueryForm form, Writer writer) {
getInternalCSV(form).fetch()
.formatCSV(writer);
}

@Override
public List<Integer> getActiveTransactionIds(String chargeBoxId) {
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())
.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) {

// -------------------------------------------------------------------------
// Step 1: Collect general data about transaction
// -------------------------------------------------------------------------

TransactionQueryForm form = new TransactionQueryForm();
form.setTransactionPk(transactionPk);
form.setType(TransactionQueryForm.QueryType.ALL);
form.setPeriodType(TransactionQueryForm.QueryPeriodType.ALL);

Record12<Integer, String, Integer, String, DateTime, String, DateTime, String, String, Integer, Integer, TransactionStopEventActor>
transaction = getInternal(form).fetchOne();

if (transaction == null) {
throw new SteveException("There is no transaction with id '%s'", transactionPk);
}

DateTime startTimestamp = transaction.value5();
DateTime stopTimestamp = transaction.value7();
String stopValue = transaction.value8();
String chargeBoxId = transaction.value2();
int connectorId = transaction.value3();

// -------------------------------------------------------------------------
// Step 2: Collect intermediate meter values
// -------------------------------------------------------------------------

Condition timestampCondition;
TransactionStartRecord nextTx = null;

if (stopTimestamp == null && stopValue == null) {

// https://github.com/steve-community/steve/issues/97
//
// handle "zombie" transaction, for which we did not receive any StopTransaction. if we do not handle it,
// meter values for all subsequent transactions at this chargebox and connector will be falsely attributed
// to this zombie transaction.
//
// "what is the subsequent transaction at the same chargebox and connector?"
nextTx = ctx.selectFrom(TRANSACTION_START)
.where(TRANSACTION_START.CONNECTOR_PK.eq(ctx.select(CONNECTOR.CONNECTOR_PK)
.from(CONNECTOR)
.where(CONNECTOR.CHARGE_BOX_ID.equal(chargeBoxId))
.and(CONNECTOR.CONNECTOR_ID.equal(connectorId))))
.and(TRANSACTION_START.START_TIMESTAMP.greaterThan(startTimestamp))
.orderBy(TRANSACTION_START.START_TIMESTAMP)
.limit(1)
.fetchOne();

if (nextTx == null) {
// the last active transaction
timestampCondition = CONNECTOR_METER_VALUE.VALUE_TIMESTAMP.greaterOrEqual(startTimestamp);
} else {
timestampCondition = CONNECTOR_METER_VALUE.VALUE_TIMESTAMP.between(startTimestamp, nextTx.getStartTimestamp());
}
} else {
// finished transaction
timestampCondition = CONNECTOR_METER_VALUE.VALUE_TIMESTAMP.between(startTimestamp, stopTimestamp);
}

// Case 1: Ideal and most accurate case. Station sends meter values with transaction id set.
//
SelectQuery<ConnectorMeterValueRecord> transactionQuery =
ctx.selectFrom(CONNECTOR_METER_VALUE)
.where(CONNECTOR_METER_VALUE.TRANSACTION_PK.eq(transactionPk))
.getQuery();

// Case 2: Fall back to filtering according to time windows
//
SelectQuery<ConnectorMeterValueRecord> timestampQuery =
ctx.selectFrom(CONNECTOR_METER_VALUE)
.where(CONNECTOR_METER_VALUE.CONNECTOR_PK.eq(ctx.select(CONNECTOR.CONNECTOR_PK)
.from(CONNECTOR)
.where(CONNECTOR.CHARGE_BOX_ID.eq(chargeBoxId))
.and(CONNECTOR.CONNECTOR_ID.eq(connectorId))))
.and(timestampCondition)
.getQuery();

// Actually, either case 1 applies or 2. If we retrieved values using 1, case 2 is should not be
// executed (best case). In worst case (1 returns empty list and we fall back to case 2) though,
// we make two db calls. Alternatively, we can pass both queries in one go, and make the db work.
//
// UNION removes all duplicate records
//
Table<ConnectorMeterValueRecord> t1 = transactionQuery.union(timestampQuery).asTable("t1");

Field<DateTime> dateTimeField = t1.field(2, DateTime.class);

List<TransactionDetails.MeterValues> values =
ctx.select(
dateTimeField,
t1.field(3, String.class),
t1.field(4, String.class),
t1.field(5, String.class),
t1.field(6, String.class),
t1.field(7, String.class),
t1.field(8, String.class),
t1.field(9, String.class))
.from(t1)
.orderBy(dateTimeField)
.fetch()
.map(r -> TransactionDetails.MeterValues.builder()
.valueTimestamp(r.value1())
.value(r.value2())
.readingContext(r.value3())
.format(r.value4())
.measurand(r.value5())
.location(r.value6())
.unit(r.value7())
.phase(r.value8())
.build());

return new TransactionDetails(new TransactionMapper().map(transaction), values, nextTx);
}

// -------------------------------------------------------------------------
// Private helpers
// -------------------------------------------------------------------------

@SuppressWarnings("unchecked")
private
SelectQuery<Record9<Integer, String, Integer, String, DateTime, String, DateTime, String, String>>
getInternalCSV(TransactionQueryForm form) {

SelectQuery selectQuery = ctx.selectQuery();
selectQuery.addFrom(TRANSACTION);
selectQuery.addJoin(CONNECTOR, TRANSACTION.CONNECTOR_PK.eq(CONNECTOR.CONNECTOR_PK));
selectQuery.addSelect(
TRANSACTION.TRANSACTION_PK,
CONNECTOR.CHARGE_BOX_ID,
CONNECTOR.CONNECTOR_ID,
TRANSACTION.ID_TAG,
TRANSACTION.START_TIMESTAMP,
TRANSACTION.START_VALUE,
TRANSACTION.STOP_TIMESTAMP,
TRANSACTION.STOP_VALUE,
TRANSACTION.STOP_REASON
);

return addConditions(selectQuery, form);
}

/**
* Difference from getInternalCSV:
* Joins with CHARGE_BOX and OCPP_TAG tables, selects CHARGE_BOX_PK and OCPP_TAG_PK additionally
*/
@SuppressWarnings("unchecked")
private
SelectQuery<Record12<Integer, String, Integer, String, DateTime, String, DateTime, String, String, Integer, Integer, TransactionStopEventActor>>
getInternal(TransactionQueryForm form) {

SelectQuery selectQuery = ctx.selectQuery();
selectQuery.addFrom(TRANSACTION);
selectQuery.addJoin(CONNECTOR, TRANSACTION.CONNECTOR_PK.eq(CONNECTOR.CONNECTOR_PK));
selectQuery.addJoin(CHARGE_BOX, CHARGE_BOX.CHARGE_BOX_ID.eq(CONNECTOR.CHARGE_BOX_ID));
selectQuery.addJoin(OCPP_TAG, OCPP_TAG.ID_TAG.eq(TRANSACTION.ID_TAG));
selectQuery.addSelect(
TRANSACTION.TRANSACTION_PK,
CONNECTOR.CHARGE_BOX_ID,
CONNECTOR.CONNECTOR_ID,
TRANSACTION.ID_TAG,
TRANSACTION.START_TIMESTAMP,
TRANSACTION.START_VALUE,
TRANSACTION.STOP_TIMESTAMP,
TRANSACTION.STOP_VALUE,
TRANSACTION.STOP_REASON,
CHARGE_BOX.CHARGE_BOX_PK,
OCPP_TAG.OCPP_TAG_PK,
TRANSACTION.STOP_EVENT_ACTOR
);

return addConditions(selectQuery, form);
}

@SuppressWarnings("unchecked")
private SelectQuery addConditions(SelectQuery selectQuery, TransactionQueryForm form) {
if (form.isTransactionPkSet()) {
selectQuery.addConditions(TRANSACTION.TRANSACTION_PK.eq(form.getTransactionPk()));
}

if (form.isChargeBoxIdSet()) {
selectQuery.addConditions(CONNECTOR.CHARGE_BOX_ID.eq(form.getChargeBoxId()));
}

if (form.isOcppIdTagSet()) {
selectQuery.addConditions(TRANSACTION.ID_TAG.eq(form.getOcppIdTag()));
}

if (form.getType() == TransactionQueryForm.QueryType.ACTIVE) {
selectQuery.addConditions(TRANSACTION.STOP_TIMESTAMP.isNull());
}

processType(selectQuery, form);

// Default order
selectQuery.addOrderBy(TRANSACTION.TRANSACTION_PK.desc());

return selectQuery;
}

private void processType(SelectQuery selectQuery, TransactionQueryForm form) {
switch (form.getPeriodType()) {
case TODAY:
selectQuery.addConditions(
date(TRANSACTION.START_TIMESTAMP).eq(date(DateTime.now()))
);
break;

case LAST_10:
case LAST_30:
case LAST_90:
DateTime now = DateTime.now();
selectQuery.addConditions(
date(TRANSACTION.START_TIMESTAMP).between(
date(now.minusDays(form.getPeriodType().getInterval())),
date(now)
)
);
break;

case ALL:
break;

case FROM_TO:
selectQuery.addConditions(
TRANSACTION.START_TIMESTAMP.between(form.getFrom().toDateTime(), form.getTo().toDateTime())
);
break;

default:
throw new SteveException("Unknown enum type");
}
}

private static class TransactionMapper implements RecordMapper<Record12<Integer, String, Integer, String, DateTime, String, DateTime, String, String, Integer, Integer, TransactionStopEventActor>, Transaction> {
@Override
public Transaction map(Record12<Integer, String, Integer, String, DateTime, String, DateTime, String, String, Integer, Integer, TransactionStopEventActor> r) {
return Transaction.builder()
.id(r.value1())
.chargeBoxId(r.value2())
.connectorId(r.value3())
.ocppIdTag(r.value4())
.startTimestamp(r.value5())
.startTimestampFormatted(DateTimeUtils.humanize(r.value5()))
.startValue(r.value6())
.stopTimestamp(r.value7())
.stopTimestampFormatted(DateTimeUtils.humanize(r.value7()))
.stopValue(r.value8())
.stopReason(r.value9())
.chargeBoxPk(r.value10())
.ocppTagPk(r.value11())
.stopEventActor(r.value12())
.build();
}
}
}

Check warning on line 369 in src/main/java/de/rwth/idsg/steve/repository/impl/TransactionRepositoryImpl.java

View workflow job for this annotation

GitHub Actions / pmd

Too many static imports may lead to messy code

If you overuse the static import feature, it can make your program unreadable and unmaintainable, polluting its namespace with all the static members you import. Readers of your code (including you, a few months after you wrote it) will not know which class a static member comes from (Sun 1.5 Language Guide). TooManyStaticImports (Priority: 3, Ruleset: Code Style) https://docs.pmd-code.org/pmd-doc-7.2.0/pmd_rules_java_codestyle.html#toomanystaticimports
Original file line number Diff line number Diff line change
Expand Up @@ -138,17 +138,33 @@ public int updateFirmware(UpdateFirmwareParams params) {

public int remoteStartTransaction(RemoteStartTransactionParams params) {
RemoteStartTransactionTask task = new RemoteStartTransactionTask(getVersion(), params);
return addRemoteStartTask(task);
}

public int remoteStartTransaction(RemoteStartTransactionParams params, String caller) {
RemoteStartTransactionTask task = new RemoteStartTransactionTask(getVersion(), params, caller);
return addRemoteStartTask(task);
}

private int addRemoteStartTask(RemoteStartTransactionTask task) {
BackgroundService.with(executorService)
.forFirst(task.getParams().getChargePointSelectList())
.execute(c -> getOcpp12Invoker().remoteStartTransaction(c, task));

return taskStore.add(task);
}

public int remoteStopTransaction(RemoteStopTransactionParams params, String caller) {
RemoteStopTransactionTask task = new RemoteStopTransactionTask(getVersion(), params, caller);
return addRemoteStopTask(task);
}

public int remoteStopTransaction(RemoteStopTransactionParams params) {
RemoteStopTransactionTask task = new RemoteStopTransactionTask(getVersion(), params);
return addRemoteStopTask(task);
}

private int addRemoteStopTask(RemoteStopTransactionTask task) {
BackgroundService.with(executorService)
.forFirst(task.getParams().getChargePointSelectList())
.execute(c -> getOcpp12Invoker().remoteStopTransaction(c, task));
Expand All @@ -158,6 +174,15 @@ public int remoteStopTransaction(RemoteStopTransactionParams params) {

public int unlockConnector(UnlockConnectorParams params) {
UnlockConnectorTask task = new UnlockConnectorTask(getVersion(), params);
return addRemoteUnlockTask(task);
}

public int unlockConnector(UnlockConnectorParams params, String caller) {
UnlockConnectorTask task = new UnlockConnectorTask(getVersion(), params, caller);
return addRemoteUnlockTask(task);
}

private int addRemoteUnlockTask(UnlockConnectorTask task) {

BackgroundService.with(executorService)
.forFirst(task.getParams().getChargePointSelectList())
Expand Down
120 changes: 120 additions & 0 deletions src/main/java/de/rwth/idsg/steve/web/api/ConnectorRestController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
/*
* 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 <https://www.gnu.org/licenses/>.
*/
package de.rwth.idsg.steve.web.api;

import de.rwth.idsg.steve.repository.ChargePointRepository;
import de.rwth.idsg.steve.repository.dto.ConnectorStatus;
import de.rwth.idsg.steve.service.ChargePointHelperService;
import de.rwth.idsg.steve.utils.ConnectorStatusFilter;
import de.rwth.idsg.steve.web.api.dto.ApiConnectorList;
import de.rwth.idsg.steve.web.dto.ConnectorStatusForm;
import de.rwth.idsg.steve.web.dto.OcppJsonStatus;
import io.swagger.annotations.ApiResponse;
import io.swagger.annotations.ApiResponses;
import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.web.bind.annotation.RequestMapping;

import java.util.List;
import static java.util.Objects.isNull;
import javax.validation.Valid;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;

/**
*
* @author fnkbsi
* since 20.10.2023
*
*/
@Slf4j
@RestController
@RequestMapping(value = "/api/v1/connectors", produces = MediaType.APPLICATION_JSON_VALUE)
@RequiredArgsConstructor
public class ConnectorRestController {

@Autowired private ChargePointRepository chargePointRepository;
@Autowired private ChargePointHelperService chargePointHelperService;

// -------------------------------------------------------------------------
// HTTP methods
// -------------------------------------------------------------------------

@ApiResponses(value = {
@ApiResponse(code = 200, message = "OK"),
@ApiResponse(code = 400, message = "Bad Request", response = ApiControllerAdvice.ApiErrorResponse.class),
@ApiResponse(code = 401, message = "Unauthorized", response = ApiControllerAdvice.ApiErrorResponse.class),
@ApiResponse(code = 500, message = "Internal Server Error",
response = ApiControllerAdvice.ApiErrorResponse.class)}
)
@GetMapping(value = "")
@ResponseBody
public ApiConnectorList getConnectors(@Valid ConnectorStatusForm queryParams) {
ApiConnectorList conList = new ApiConnectorList();
conList.setChargeBoxList(chargePointRepository.getChargeBoxIds());

conList.setIsFiltered(isFilterd(queryParams));
List<ConnectorStatus> latestList = chargePointHelperService.getChargePointConnectorStatus(queryParams);
List<ConnectorStatus> sortedList;
if (queryParams.getStrategy() == ConnectorStatusForm.Strategy.PreferZero) {
sortedList = ConnectorStatusFilter.filterAndPreferZero(latestList);
} else {
sortedList = ConnectorStatusFilter.filterAndPreferOthersWithStatusOfZero(latestList);
}
conList.setConnectors(sortedList);
return conList;
}

@ApiResponses(value = {
@ApiResponse(code = 200, message = "OK"),
@ApiResponse(code = 400, message = "Bad Request", response = ApiControllerAdvice.ApiErrorResponse.class),
@ApiResponse(code = 401, message = "Unauthorized", response = ApiControllerAdvice.ApiErrorResponse.class),
@ApiResponse(code = 500, message = "Internal Server Error",
response = ApiControllerAdvice.ApiErrorResponse.class)}
)
@GetMapping(value = "OCPP_JSON_STATUS")
@ResponseBody
public List<OcppJsonStatus> getOcppJsonStatus() {
return chargePointHelperService.getOcppJsonStatus();
}

// -------------------------------------------------------------------------
// Helpers
// -------------------------------------------------------------------------

private Boolean isFilterd(ConnectorStatusForm queryParams) {
if (!isNull(queryParams)) {
if (!isNull(queryParams.getChargeBoxId())) {
if (!queryParams.getChargeBoxId().isBlank()) {
return true;
}

Check warning on line 110 in src/main/java/de/rwth/idsg/steve/web/api/ConnectorRestController.java

View workflow job for this annotation

GitHub Actions / pmd

This if statement could be combined with its parent

Reports nested 'if' statements that can be merged together by joining their conditions with a boolean `&&` operator in between. CollapsibleIfStatements (Priority: 3, Ruleset: Design) https://docs.pmd-code.org/pmd-doc-7.2.0/pmd_rules_java_design.html#collapsibleifstatements
}
if (!isNull(queryParams.getStatus())) {
if (!queryParams.getStatus().isBlank()) {
return true;
}

Check warning on line 115 in src/main/java/de/rwth/idsg/steve/web/api/ConnectorRestController.java

View workflow job for this annotation

GitHub Actions / pmd

This if statement could be combined with its parent

Reports nested 'if' statements that can be merged together by joining their conditions with a boolean `&&` operator in between. CollapsibleIfStatements (Priority: 3, Ruleset: Design) https://docs.pmd-code.org/pmd-doc-7.2.0/pmd_rules_java_design.html#collapsibleifstatements
}
}
return false;
}
}
Loading