11package de .rwth .idsg .steve .repository .impl ;
22
3+ import de .rwth .idsg .steve .SteveException ;
34import de .rwth .idsg .steve .repository .OcppServerRepository ;
45import de .rwth .idsg .steve .repository .ReservationRepository ;
56import de .rwth .idsg .steve .repository .dto .InsertConnectorStatusParams ;
@@ -44,12 +45,6 @@ public class OcppServerRepositoryImpl implements OcppServerRepository {
4445 @ Autowired private DSLContext ctx ;
4546 @ Autowired private ReservationRepository reservationRepository ;
4647
47- // true story: while testing with abusive-charge-point, from time to time, we get MySql exceptions with
48- // "Deadlock found when trying to get lock; try restarting transaction" when processing startTransaction and/or
49- // stopTransaction messages.
50- // TODO: this is an initial dirty solution/workaround. look into the cause of the problem.
51- private final Object TRANSACTION_LOCK = new Object ();
52-
5348 @ Override
5449 public void updateChargebox (UpdateChargeboxParams p ) {
5550 ctx .update (CHARGE_BOX )
@@ -161,10 +156,6 @@ public void insertMeterValues(String chargeBoxIdentity, List<MeterValue> list, i
161156
162157 @ Override
163158 public Integer insertTransaction (InsertTransactionParams p ) {
164- insertIgnoreConnector (ctx , p .getChargeBoxId (), p .getConnectorId ());
165-
166- // it is important to insert idTag before transaction, since the transaction table references it
167- boolean unknownTagInserted = insertIgnoreIdTag (ctx , p );
168159
169160 SelectConditionStep <Record1 <Integer >> connectorPkQuery =
170161 DSL .select (CONNECTOR .CONNECTOR_PK )
@@ -173,20 +164,30 @@ public Integer insertTransaction(InsertTransactionParams p) {
173164 .and (CONNECTOR .CONNECTOR_ID .equal (p .getConnectorId ()));
174165
175166 // -------------------------------------------------------------------------
176- // Step 1: Insert transaction
167+ // Step 1: Insert connector and idTag, if they are new to us
168+ // -------------------------------------------------------------------------
169+
170+ insertIgnoreConnector (ctx , p .getChargeBoxId (), p .getConnectorId ());
171+
172+ // it is important to insert idTag before transaction, since the transaction table references it
173+ boolean unknownTagInserted = insertIgnoreIdTag (ctx , p );
174+
175+ // -------------------------------------------------------------------------
176+ // Step 2: Insert transaction
177177 // -------------------------------------------------------------------------
178178
179- int transactionId ;
180-
181- synchronized (TRANSACTION_LOCK ) {
182- transactionId = ctx .insertInto (TRANSACTION )
183- .set (TRANSACTION .CONNECTOR_PK , connectorPkQuery )
184- .set (TRANSACTION .ID_TAG , p .getIdTag ())
185- .set (TRANSACTION .START_TIMESTAMP , p .getStartTimestamp ())
186- .set (TRANSACTION .START_VALUE , p .getStartMeterValue ())
187- .returning (TRANSACTION .TRANSACTION_PK )
188- .fetchOne ()
189- .getTransactionPk ();
179+ Integer transactionId = ctx .insertInto (TRANSACTION )
180+ .set (TRANSACTION .CONNECTOR_PK , connectorPkQuery )
181+ .set (TRANSACTION .ID_TAG , p .getIdTag ())
182+ .set (TRANSACTION .START_TIMESTAMP , p .getStartTimestamp ())
183+ .set (TRANSACTION .START_VALUE , p .getStartMeterValue ())
184+ .returning (TRANSACTION .TRANSACTION_PK )
185+ .fetchOne ()
186+ .getTransactionPk ();
187+
188+ // Actually unnecessary, because JOOQ will throw an exception, if something goes wrong
189+ if (transactionId == null ) {
190+ throw new SteveException ("Failed to INSERT transaction into database" );
190191 }
191192
192193 if (unknownTagInserted ) {
@@ -195,15 +196,28 @@ public Integer insertTransaction(InsertTransactionParams p) {
195196 }
196197
197198 // -------------------------------------------------------------------------
198- // Step 2 for OCPP 1.5: A startTransaction may be related to a reservation
199+ // Step 3: Update OCPP tag (in_transaction=true)
200+ // -------------------------------------------------------------------------
201+
202+ int count = ctx .update (OCPP_TAG )
203+ .set (OCPP_TAG .IN_TRANSACTION , true )
204+ .where (OCPP_TAG .ID_TAG .eq (p .getIdTag ()))
205+ .execute ();
206+
207+ if (count == 0 ) {
208+ log .warn ("Failed to set in_transaction=true of OCPP tag of STARTED transaction {}" , transactionId );
209+ }
210+
211+ // -------------------------------------------------------------------------
212+ // Step 4 for OCPP >= 1.5: A startTransaction may be related to a reservation
199213 // -------------------------------------------------------------------------
200214
201215 if (p .isSetReservationId ()) {
202216 reservationRepository .used (connectorPkQuery , p .getIdTag (), p .getReservationId (), transactionId );
203217 }
204218
205219 // -------------------------------------------------------------------------
206- // Step 3 : Set connector status to "Occupied"
220+ // Step 5 : Set connector status to "Occupied"
207221 // -------------------------------------------------------------------------
208222
209223 insertConnectorStatus (ctx , connectorPkQuery , p .getStartTimestamp (), p .getStatusUpdate ());
@@ -216,23 +230,40 @@ public void updateTransaction(UpdateTransactionParams p) {
216230
217231 // -------------------------------------------------------------------------
218232 // Step 1: Update transaction table
219- //
220- // After update, a DB trigger sets the user.inTransaction field to 0
221233 // -------------------------------------------------------------------------
222234
223- synchronized (TRANSACTION_LOCK ) {
224- ctx .update (TRANSACTION )
225- .set (TRANSACTION .STOP_TIMESTAMP , p .getStopTimestamp ())
226- .set (TRANSACTION .STOP_VALUE , p .getStopMeterValue ())
227- .set (TRANSACTION .STOP_REASON , p .getStopReason ())
228- .where (TRANSACTION .TRANSACTION_PK .equal (p .getTransactionId ()))
229- .and (TRANSACTION .STOP_TIMESTAMP .isNull ())
230- .and (TRANSACTION .STOP_VALUE .isNull ())
231- .execute ();
235+ int transactionUpdateCount = ctx .update (TRANSACTION )
236+ .set (TRANSACTION .STOP_TIMESTAMP , p .getStopTimestamp ())
237+ .set (TRANSACTION .STOP_VALUE , p .getStopMeterValue ())
238+ .set (TRANSACTION .STOP_REASON , p .getStopReason ())
239+ .where (TRANSACTION .TRANSACTION_PK .equal (p .getTransactionId ()))
240+ .execute ();
241+
242+ // Actually unnecessary, because JOOQ will throw an exception, if something goes wrong
243+ if (transactionUpdateCount == 0 ) {
244+ throw new SteveException ("Failed to UPDATE transaction in database" );
245+ }
246+
247+ // -------------------------------------------------------------------------
248+ // Step 2: Update OCPP tag (in_transaction=false)
249+ // -------------------------------------------------------------------------
250+
251+ SelectConditionStep <Record1 <String >> idTagSelect =
252+ DSL .select (TRANSACTION .ID_TAG )
253+ .from (TRANSACTION )
254+ .where (TRANSACTION .TRANSACTION_PK .equal (p .getTransactionId ()));
255+
256+ int ocppTagUpdateCount = ctx .update (OCPP_TAG )
257+ .set (OCPP_TAG .IN_TRANSACTION , false )
258+ .where (OCPP_TAG .ID_TAG .eq (idTagSelect ))
259+ .execute ();
260+
261+ if (ocppTagUpdateCount == 0 ) {
262+ log .warn ("Failed to set in_transaction=false of OCPP tag of STOPPED transaction {}" , p .getTransactionId ());
232263 }
233264
234265 // -------------------------------------------------------------------------
235- // Step 2 : Set connector status back to "Available" again
266+ // Step 3 : Set connector status back to "Available" again
236267 // -------------------------------------------------------------------------
237268
238269 SelectConditionStep <Record1 <Integer >> connectorPkQuery =
0 commit comments