Skip to content

Commit 1322c23

Browse files
committed
add "transaction details" page which lists intermediate meter values
1 parent 99a75c8 commit 1322c23

7 files changed

Lines changed: 212 additions & 13 deletions

File tree

src/main/java/de/rwth/idsg/steve/repository/TransactionRepository.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package de.rwth.idsg.steve.repository;
22

33
import de.rwth.idsg.steve.repository.dto.Transaction;
4+
import de.rwth.idsg.steve.repository.dto.TransactionDetails;
45
import de.rwth.idsg.steve.web.dto.TransactionQueryForm;
56

67
import java.io.Writer;
@@ -14,4 +15,5 @@ public interface TransactionRepository {
1415
List<Transaction> getTransactions(TransactionQueryForm form);
1516
void writeTransactionsCSV(TransactionQueryForm form, Writer writer);
1617
List<Integer> getActiveTransactionIds(String chargeBoxId);
18+
TransactionDetails getDetails(int transactionPk);
1719
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package de.rwth.idsg.steve.repository.dto;
2+
3+
import lombok.Builder;
4+
import lombok.Getter;
5+
import lombok.RequiredArgsConstructor;
6+
import org.joda.time.DateTime;
7+
8+
import java.util.List;
9+
10+
/**
11+
* @author Sevket Goekay <goekay@dbis.rwth-aachen.de>
12+
* @since 27.04.2016
13+
*/
14+
@Getter
15+
@RequiredArgsConstructor
16+
public class TransactionDetails {
17+
private final Transaction transaction;
18+
private final List<MeterValues> values;
19+
20+
@Getter
21+
@Builder
22+
public static class MeterValues {
23+
private final DateTime valueTimestamp;
24+
private final String value, readingContext, format, measurand, location, unit;
25+
}
26+
}

src/main/java/de/rwth/idsg/steve/repository/impl/TransactionRepositoryImpl.java

Lines changed: 128 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,20 @@
33
import de.rwth.idsg.steve.SteveException;
44
import de.rwth.idsg.steve.repository.TransactionRepository;
55
import de.rwth.idsg.steve.repository.dto.Transaction;
6+
import de.rwth.idsg.steve.repository.dto.TransactionDetails;
67
import de.rwth.idsg.steve.utils.CustomDSL;
78
import de.rwth.idsg.steve.utils.DateTimeUtils;
89
import de.rwth.idsg.steve.web.dto.TransactionQueryForm;
10+
import jooq.steve.db.tables.records.ConnectorMeterValueRecord;
911
import org.joda.time.DateTime;
12+
import org.jooq.Condition;
1013
import org.jooq.DSLContext;
1114
import org.jooq.Record10;
1215
import org.jooq.Record8;
16+
import org.jooq.RecordMapper;
1317
import org.jooq.SelectQuery;
18+
import org.jooq.Table;
19+
import org.jooq.impl.DSL;
1420
import org.springframework.beans.factory.annotation.Autowired;
1521
import org.springframework.stereotype.Repository;
1622

@@ -20,6 +26,7 @@
2026
import static de.rwth.idsg.steve.utils.CustomDSL.date;
2127
import static jooq.steve.db.tables.ChargeBox.CHARGE_BOX;
2228
import static jooq.steve.db.tables.Connector.CONNECTOR;
29+
import static jooq.steve.db.tables.ConnectorMeterValue.CONNECTOR_METER_VALUE;
2330
import static jooq.steve.db.tables.OcppTag.OCPP_TAG;
2431
import static jooq.steve.db.tables.Transaction.TRANSACTION;
2532

@@ -36,18 +43,7 @@ public class TransactionRepositoryImpl implements TransactionRepository {
3643
@SuppressWarnings("unchecked")
3744
public List<Transaction> getTransactions(TransactionQueryForm form) {
3845
return getInternal(form).fetch()
39-
.map(r -> Transaction.builder()
40-
.id(r.value1())
41-
.chargeBoxId(r.value2())
42-
.connectorId(r.value3())
43-
.ocppIdTag(r.value4())
44-
.startTimestamp(DateTimeUtils.humanize(r.value5()))
45-
.startValue(r.value6())
46-
.stopTimestamp(DateTimeUtils.humanize(r.value7()))
47-
.stopValue(r.value8())
48-
.chargeBoxPk(r.value9())
49-
.ocppTagPk(r.value10())
50-
.build());
46+
.map(new TransactionMapper());
5147
}
5248

5349
@Override
@@ -68,6 +64,105 @@ public List<Integer> getActiveTransactionIds(String chargeBoxId) {
6864
.fetch(TRANSACTION.TRANSACTION_PK);
6965
}
7066

67+
@Override
68+
public TransactionDetails getDetails(int transactionPk) {
69+
70+
// -------------------------------------------------------------------------
71+
// Step 1: Collect general data about transaction
72+
// -------------------------------------------------------------------------
73+
74+
TransactionQueryForm form = new TransactionQueryForm();
75+
form.setTransactionPk(transactionPk);
76+
form.setType(TransactionQueryForm.QueryType.ALL);
77+
form.setPeriodType(TransactionQueryForm.QueryPeriodType.ALL);
78+
79+
Record10<Integer, String, Integer, String, DateTime, String, DateTime, String, Integer, Integer>
80+
transaction = getInternal(form).fetchOne();
81+
82+
DateTime startTimestamp = transaction.value5();
83+
DateTime stopTimestamp = transaction.value7();
84+
String stopValue = transaction.value8();
85+
String chargeBoxId = transaction.value2();
86+
int connectorId = transaction.value3();
87+
88+
// -------------------------------------------------------------------------
89+
// Step 2: Collect intermediate meter values
90+
// -------------------------------------------------------------------------
91+
92+
Condition timestampCondition;
93+
if (stopTimestamp == null && stopValue == null) {
94+
// active transaction
95+
timestampCondition = CONNECTOR_METER_VALUE.VALUE_TIMESTAMP.greaterOrEqual(startTimestamp);
96+
} else {
97+
// finished transaction
98+
timestampCondition = CONNECTOR_METER_VALUE.VALUE_TIMESTAMP.between(startTimestamp, stopTimestamp);
99+
}
100+
101+
// Case 1: Ideal and most accurate case. Station sends meter values with transaction id set.
102+
//
103+
SelectQuery<ConnectorMeterValueRecord> transactionQuery =
104+
ctx.selectFrom(CONNECTOR_METER_VALUE)
105+
.where(CONNECTOR_METER_VALUE.TRANSACTION_PK.eq(transactionPk))
106+
.getQuery();
107+
108+
// Case 2: Fall back to filtering according to time windows
109+
//
110+
SelectQuery<ConnectorMeterValueRecord> timestampQuery =
111+
ctx.selectFrom(CONNECTOR_METER_VALUE)
112+
.where(CONNECTOR_METER_VALUE.CONNECTOR_PK.eq(ctx.select(CONNECTOR.CONNECTOR_PK)
113+
.from(CONNECTOR)
114+
.where(CONNECTOR.CHARGE_BOX_ID.eq(chargeBoxId))
115+
.and(CONNECTOR.CONNECTOR_ID.eq(connectorId))))
116+
.and(timestampCondition)
117+
.getQuery();
118+
119+
// Actually, either case 1 applies or 2. If we retrieved values using 1, case 2 is should not be
120+
// executed (best case). In worst case (1 returns empty list and we fall back to case 2) though,
121+
// we make two db calls. Alternatively, we can pass both queries in one go, and make the db work.
122+
//
123+
// UNION removes all duplicate records
124+
//
125+
Table<ConnectorMeterValueRecord> t1 = transactionQuery.union(timestampQuery).asTable("t1");
126+
127+
// -------------------------------------------------------------------------
128+
// Step 3: Charging station might send meter vales at fixed intervals (e.g.
129+
// every 15 min) regardless of the fact that connector's meter value did not
130+
// change (e.g. vehicle is fully charged, but cable is still connected). This
131+
// yields multiple entries in db with the same value but different timestamp.
132+
// We are only interested in the first arriving entry.
133+
// -------------------------------------------------------------------------
134+
135+
List<TransactionDetails.MeterValues> values =
136+
ctx.select(
137+
DSL.min(t1.field(2, DateTime.class)),
138+
t1.field(3, String.class),
139+
t1.field(4, String.class),
140+
t1.field(5, String.class),
141+
t1.field(6, String.class),
142+
t1.field(7, String.class),
143+
t1.field(8, String.class))
144+
.from(t1)
145+
.groupBy(
146+
t1.field(3),
147+
t1.field(4),
148+
t1.field(5),
149+
t1.field(6),
150+
t1.field(7),
151+
t1.field(8))
152+
.fetch()
153+
.map(r -> TransactionDetails.MeterValues.builder()
154+
.valueTimestamp(r.value1())
155+
.value(r.value2())
156+
.readingContext(r.value3())
157+
.format(r.value4())
158+
.measurand(r.value5())
159+
.location(r.value6())
160+
.unit(r.value7())
161+
.build());
162+
163+
return new TransactionDetails(new TransactionMapper().map(transaction), values);
164+
}
165+
71166
// -------------------------------------------------------------------------
72167
// Private helpers
73168
// -------------------------------------------------------------------------
@@ -183,4 +278,25 @@ private void processType(SelectQuery selectQuery, TransactionQueryForm form) {
183278
throw new SteveException("Unknown enum type");
184279
}
185280
}
281+
282+
private static class TransactionMapper
283+
implements RecordMapper<Record10<Integer, String, Integer, String, DateTime, String, DateTime,
284+
String, Integer, Integer>, Transaction> {
285+
@Override
286+
public Transaction map(Record10<Integer, String, Integer, String, DateTime, String, DateTime,
287+
String, Integer, Integer> r) {
288+
return Transaction.builder()
289+
.id(r.value1())
290+
.chargeBoxId(r.value2())
291+
.connectorId(r.value3())
292+
.ocppIdTag(r.value4())
293+
.startTimestamp(DateTimeUtils.humanize(r.value5()))
294+
.startValue(r.value6())
295+
.stopTimestamp(DateTimeUtils.humanize(r.value7()))
296+
.stopValue(r.value8())
297+
.chargeBoxPk(r.value9())
298+
.ocppTagPk(r.value10())
299+
.build();
300+
}
301+
}
186302
}

src/main/java/de/rwth/idsg/steve/web/controller/TransactionsReservationsController.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
import org.springframework.ui.Model;
1313
import org.springframework.validation.BindingResult;
1414
import org.springframework.web.bind.annotation.ModelAttribute;
15+
import org.springframework.web.bind.annotation.PathVariable;
1516
import org.springframework.web.bind.annotation.RequestMapping;
1617
import org.springframework.web.bind.annotation.RequestMethod;
1718

@@ -41,6 +42,7 @@ public class TransactionsReservationsController {
4142
// -------------------------------------------------------------------------
4243

4344
private static final String TRANSACTIONS_PATH = "/transactions";
45+
private static final String TRANSACTIONS_DETAILS_PATH = "/transactions/details/{transactionPk}";
4446
private static final String TRANSACTIONS_QUERY_PATH = "/transactions/query";
4547
private static final String RESERVATIONS_PATH = "/reservations";
4648
private static final String RESERVATIONS_QUERY_PATH = "/reservations/query";
@@ -59,6 +61,12 @@ public String getTransactions(Model model) {
5961
return "data-man/transactions";
6062
}
6163

64+
@RequestMapping(value = TRANSACTIONS_DETAILS_PATH)
65+
public String getTransactionDetails(@PathVariable("transactionPk") int transactionPk, Model model) {
66+
model.addAttribute("details", transactionRepository.getDetails(transactionPk));
67+
return "data-man/transactionDetails";
68+
}
69+
6270
@RequestMapping(value = TRANSACTIONS_QUERY_PATH)
6371
public String getTransactionsQuery(@Valid @ModelAttribute(PARAMS) TransactionQueryForm params,
6472
BindingResult result, Model model,
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
ALTER TABLE `connector_meter_value`
2+
ADD INDEX `cmv_value_timestamp_idx` (`value_timestamp` ASC) COMMENT '';
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
<%@ include file="../00-header.jsp" %>
2+
<div class="content"><div>
3+
<center>
4+
<table id="details" class="cpd">
5+
<thead><tr><th>Transaction Overview</th><th></th></tr></thead>
6+
<tr><td>Transaction ID</td><td>${details.transaction.id}</td></tr>
7+
<tr><td>ChargeBox ID</td><td><a href="${ctxPath}/manager/chargepoints/details/${details.transaction.chargeBoxPk}">${details.transaction.chargeBoxId}</a></td></tr>
8+
<tr><td>Connector ID</td><td>${details.transaction.connectorId}</td></tr>
9+
<tr><td>OCPP ID Tag</td><td><a href="${ctxPath}/manager/ocppTags/details/${details.transaction.ocppTagPk}">${details.transaction.ocppIdTag}</a></td></tr>
10+
<tr><td>Start Date/Time</td><td>${details.transaction.startTimestamp}</td></tr>
11+
<tr><td>Start Value</td><td>${details.transaction.startValue}</td></tr>
12+
<tr><td>Stop Date/Time</td><td>${details.transaction.stopTimestamp}</td></tr>
13+
<tr><td>Stop Value</td><td>${details.transaction.stopValue}</td></tr>
14+
</table>
15+
</center>
16+
<br>
17+
<section><span>Intermediate Meter Values</span></section>
18+
<table class="res">
19+
<thead>
20+
<tr>
21+
<th>Value Timestamp</th>
22+
<th>Value</th>
23+
<th>Reading Context</th>
24+
<th>Format</th>
25+
<th>Measurand</th>
26+
<th>Location</th>
27+
<th>Unit</th>
28+
</tr>
29+
</thead>
30+
<tbody>
31+
<c:forEach items="${details.values}" var="v">
32+
<tr>
33+
<td>${v.valueTimestamp}</td>
34+
<td>${v.value}</td>
35+
<td>${v.readingContext}</td>
36+
<td>${v.format}</td>
37+
<td>${v.measurand}</td>
38+
<td>${v.location}</td>
39+
<td>${v.unit}</td>
40+
</tr>
41+
</c:forEach>
42+
</tbody>
43+
</table>
44+
</div></div>
45+
<%@ include file="../00-footer.jsp" %>

src/main/resources/webapp/WEB-INF/views/data-man/transactions.jsp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ Transactions
8888
<tbody>
8989
<c:forEach items="${transList}" var="ta">
9090
<tr>
91-
<td>${ta.id}</td>
91+
<td><a href="${ctxPath}/manager/transactions/details/${ta.id}">${ta.id}</a></td>
9292
<td><a href="${ctxPath}/manager/chargepoints/details/${ta.chargeBoxPk}">${ta.chargeBoxId}</a></td>
9393
<td>${ta.connectorId}</td>
9494
<td><a href="${ctxPath}/manager/ocppTags/details/${ta.ocppTagPk}">${ta.ocppIdTag}</a></td>

0 commit comments

Comments
 (0)