public async Task Invoke(HttpContext context) { _logger.LogTrace("OCPPMiddleware => Websocket request: Path='{0}'", context.Request.Path); ChargePointStatus chargePointStatus = null; if (context.Request.Path.StartsWithSegments("/OCPP")) { string chargepointIdentifier; string[] parts = context.Request.Path.Value.Split('/'); if (string.IsNullOrWhiteSpace(parts[parts.Length - 1])) { // (Last part - 1) is chargepoint identifier chargepointIdentifier = parts[parts.Length - 2]; } else { // Last part is chargepoint identifier chargepointIdentifier = parts[parts.Length - 1]; } _logger.LogInformation("OCPPMiddleware => Connection request with chargepoint identifier = '{0}'", chargepointIdentifier); // Known chargepoint? if (!string.IsNullOrWhiteSpace(chargepointIdentifier)) { using (OCPPCoreContext dbContext = new OCPPCoreContext(_configuration)) { ChargePoint chargePoint = dbContext.Find <ChargePoint>(chargepointIdentifier); if (chargePoint != null) { _logger.LogInformation("OCPPMiddleware => SUCCESS: Found chargepoint with identifier={0}", chargePoint.ChargePointId); // Check optional chargepoint authentication if (!string.IsNullOrWhiteSpace(chargePoint.Username)) { // Chargepoint MUST send basic authentication header bool basicAuthSuccess = false; string authHeader = context.Request.Headers["Authorization"]; if (!string.IsNullOrEmpty(authHeader)) { string[] cred = System.Text.ASCIIEncoding.ASCII.GetString(Convert.FromBase64String(authHeader.Substring(6))).Split(':'); if (cred.Length == 2 && chargePoint.Username == cred[0] && chargePoint.Password == cred[1]) { // Authentication match => OK _logger.LogInformation("OCPPMiddleware => SUCCESS: Basic authentication for chargepoint '{0}' match", chargePoint.ChargePointId); basicAuthSuccess = true; } else { // Authentication does NOT match => Failure _logger.LogWarning("OCPPMiddleware => FAILURE: Basic authentication for chargepoint '{0}' does NOT match", chargePoint.ChargePointId); } } if (basicAuthSuccess == false) { context.Response.Headers.Add("WWW-Authenticate", "Basic realm=\"OCPP.Core\""); context.Response.StatusCode = (int)HttpStatusCode.Unauthorized; return; } } else if (!string.IsNullOrWhiteSpace(chargePoint.ClientCertThumb)) { // Chargepoint MUST send basic authentication header bool certAuthSuccess = false; X509Certificate2 clientCert = context.Connection.ClientCertificate; if (clientCert != null) { if (clientCert.Thumbprint.Equals(chargePoint.ClientCertThumb, StringComparison.InvariantCultureIgnoreCase)) { // Authentication match => OK _logger.LogInformation("OCPPMiddleware => SUCCESS: Certificate authentication for chargepoint '{0}' match", chargePoint.ChargePointId); certAuthSuccess = true; } else { // Authentication does NOT match => Failure _logger.LogWarning("OCPPMiddleware => FAILURE: Certificate authentication for chargepoint '{0}' does NOT match", chargePoint.ChargePointId); } } if (certAuthSuccess == false) { context.Response.StatusCode = (int)HttpStatusCode.Unauthorized; return; } } else { _logger.LogInformation("OCPPMiddleware => No authentication for chargepoint '{0}' configured", chargePoint.ChargePointId); } // Store chargepoint data chargePointStatus = new ChargePointStatus(chargePoint); } else { _logger.LogWarning("OCPPMiddleware => FAILURE: Found no chargepoint with identifier={0}", chargepointIdentifier); } } } if (chargePointStatus != null) { if (context.WebSockets.IsWebSocketRequest) { // Match supported sub protocols string subProtocol = null; foreach (string supportedProtocol in SupportedProtocols) { if (context.WebSockets.WebSocketRequestedProtocols.Contains(supportedProtocol)) { subProtocol = supportedProtocol; break; } } if (string.IsNullOrEmpty(subProtocol)) { // Not matching protocol! => failure string protocols = string.Empty; foreach (string p in context.WebSockets.WebSocketRequestedProtocols) { if (string.IsNullOrEmpty(protocols)) { protocols += ","; } protocols += p; } _logger.LogWarning("OCPPMiddleware => No supported sub-protocol in '{0}' from charge station '{1}'", protocols, chargepointIdentifier); context.Response.StatusCode = (int)HttpStatusCode.BadRequest; } else { chargePointStatus.Protocol = subProtocol; bool statusSuccess = false; try { _logger.LogTrace("OCPPMiddleware => Store/Update status object"); lock (_chargePointStatusDict) { // Check if this chargepoint already/still hat a status object if (_chargePointStatusDict.ContainsKey(chargepointIdentifier)) { // exists => check status if (_chargePointStatusDict[chargepointIdentifier].WebSocket.State != WebSocketState.Open) { // Closed or aborted => remove _chargePointStatusDict.Remove(chargepointIdentifier); } } _chargePointStatusDict.Add(chargepointIdentifier, chargePointStatus); statusSuccess = true; } } catch (Exception exp) { _logger.LogError(exp, "OCPPMiddleware => Error storing status object in dictionary => refuse connection"); context.Response.StatusCode = (int)HttpStatusCode.InternalServerError; } if (statusSuccess) { // Handle socket communication _logger.LogTrace("OCPPMiddleware => Waiting for message..."); using (WebSocket webSocket = await context.WebSockets.AcceptWebSocketAsync(subProtocol)) { _logger.LogTrace("OCPPMiddleware => WebSocket connection with charge point '{0}'", chargepointIdentifier); chargePointStatus.WebSocket = webSocket; if (subProtocol == Protocol_OCPP20) { // OCPP V2.0 await Receive20(chargePointStatus, context); } else { // OCPP V1.6 await Receive16(chargePointStatus, context); } } } } } else { // no websocket request => failure _logger.LogWarning("OCPPMiddleware => Non-Websocket request"); context.Response.StatusCode = (int)HttpStatusCode.BadRequest; } } else { // unknown chargepoint _logger.LogTrace("OCPPMiddleware => no chargepoint: http 412"); context.Response.StatusCode = (int)HttpStatusCode.PreconditionFailed; } } else if (context.Request.Path.StartsWithSegments("/API")) { // Check authentication (X-API-Key) string apiKeyConfig = _configuration.GetValue <string>("ApiKey"); if (!string.IsNullOrWhiteSpace(apiKeyConfig)) { // ApiKey specified => check request string apiKeyCaller = context.Request.Headers["X-API-Key"].FirstOrDefault(); if (apiKeyConfig == apiKeyCaller) { // API-Key matches _logger.LogInformation("OCPPMiddleware => Success: X-API-Key matches"); } else { // API-Key does NOT matches => authentication failure!!! _logger.LogWarning("OCPPMiddleware => Failure: Wrong X-API-Key! Caller='{0}'", apiKeyCaller); context.Response.StatusCode = (int)HttpStatusCode.Unauthorized; return; } } else { // No API-Key configured => no authenticatiuon _logger.LogWarning("OCPPMiddleware => No X-API-Key configured!"); } // format: /API/<command>[/chargepointId] string[] urlParts = context.Request.Path.Value.Split('/'); if (urlParts.Length >= 3) { string cmd = urlParts[2]; string urlChargePointId = (urlParts.Length >= 4) ? urlParts[3] : null; _logger.LogTrace("OCPPMiddleware => cmd='{0}' / id='{1}' / FullPath='{2}')", cmd, urlChargePointId, context.Request.Path.Value); if (cmd == "Status") { try { List <ChargePointStatus> statusList = new List <ChargePointStatus>(); foreach (ChargePointStatus status in _chargePointStatusDict.Values) { statusList.Add(status); } string jsonStatus = JsonConvert.SerializeObject(statusList); context.Response.ContentType = "application/json"; await context.Response.WriteAsync(jsonStatus); } catch (Exception exp) { _logger.LogError(exp, "OCPPMiddleware => Error: {0}", exp.Message); context.Response.StatusCode = (int)HttpStatusCode.InternalServerError; } } else if (cmd == "Reset") { if (!string.IsNullOrEmpty(urlChargePointId)) { try { ChargePointStatus status = null; if (_chargePointStatusDict.TryGetValue(urlChargePointId, out status)) { // Send message to chargepoint if (status.Protocol == Protocol_OCPP20) { // OCPP V2.0 await Reset20(status, context); } else { // OCPP V1.6 await Reset16(status, context); } } else { // Chargepoint offline _logger.LogError("OCPPMiddleware SoftReset => Chargepoint offline: {0}", urlChargePointId); context.Response.StatusCode = (int)HttpStatusCode.NotFound; } } catch (Exception exp) { _logger.LogError(exp, "OCPPMiddleware SoftReset => Error: {0}", exp.Message); context.Response.StatusCode = (int)HttpStatusCode.InternalServerError; } } else { _logger.LogError("OCPPMiddleware SoftReset => Missing chargepoint ID"); context.Response.StatusCode = (int)HttpStatusCode.BadRequest; } } else if (cmd == "UnlockConnector") { if (!string.IsNullOrEmpty(urlChargePointId)) { try { ChargePointStatus status = null; if (_chargePointStatusDict.TryGetValue(urlChargePointId, out status)) { // Send message to chargepoint if (status.Protocol == Protocol_OCPP20) { // OCPP V2.0 await UnlockConnector20(status, context); } else { // OCPP V1.6 await UnlockConnector16(status, context); } } else { // Chargepoint offline _logger.LogError("OCPPMiddleware UnlockConnector => Chargepoint offline: {0}", urlChargePointId); context.Response.StatusCode = (int)HttpStatusCode.NotFound; } } catch (Exception exp) { _logger.LogError(exp, "OCPPMiddleware UnlockConnector => Error: {0}", exp.Message); context.Response.StatusCode = (int)HttpStatusCode.InternalServerError; } } else { _logger.LogError("OCPPMiddleware UnlockConnector => Missing chargepoint ID"); context.Response.StatusCode = (int)HttpStatusCode.BadRequest; } } else { // Unknown action/function _logger.LogWarning("OCPPMiddleware => action/function: {0}", cmd); context.Response.StatusCode = (int)HttpStatusCode.NotFound; } } } else if (context.Request.Path.StartsWithSegments("/")) { try { bool showIndexInfo = _configuration.GetValue <bool>("ShowIndexInfo"); if (showIndexInfo) { _logger.LogTrace("OCPPMiddleware => Index status page"); context.Response.ContentType = "text/plain"; await context.Response.WriteAsync(string.Format("Running...\r\n\r\n{0} chargepoints connected", _chargePointStatusDict.Values.Count)); } else { _logger.LogInformation("OCPPMiddleware => Root path with deactivated index page"); context.Response.StatusCode = (int)HttpStatusCode.BadRequest; } } catch (Exception exp) { _logger.LogError(exp, "OCPPMiddleware => Error: {0}", exp.Message); context.Response.StatusCode = (int)HttpStatusCode.InternalServerError; } } else { _logger.LogWarning("OCPPMiddleware => Bad path request"); context.Response.StatusCode = (int)HttpStatusCode.BadRequest; } }
public string HandleStopTransaction(OCPPMessage msgIn, OCPPMessage msgOut) { string errorCode = null; StopTransactionResponse stopTransactionResponse = new StopTransactionResponse(); try { Logger.LogTrace("Processing stopTransaction request..."); StopTransactionRequest stopTransactionRequest = JsonConvert.DeserializeObject <StopTransactionRequest>(msgIn.JsonPayload); Logger.LogTrace("StopTransaction => Message deserialized"); string idTag = Utils.CleanChargeTagId(stopTransactionRequest.IdTag, Logger); if (string.IsNullOrWhiteSpace(idTag)) { // no RFID-Tag => accept request stopTransactionResponse.IdTagInfo.Status = IdTagInfoStatus.Accepted; Logger.LogInformation("StopTransaction => no charge tag => Status: {0}", stopTransactionResponse.IdTagInfo.Status); } else { stopTransactionResponse.IdTagInfo = new IdTagInfo(); stopTransactionResponse.IdTagInfo.ExpiryDate = Utils.MaxExpiryDate; try { using (OCPPCoreContext dbContext = new OCPPCoreContext(Configuration)) { ChargeTag ct = dbContext.Find <ChargeTag>(idTag); if (ct != null) { if (ct.ExpiryDate.HasValue) { stopTransactionResponse.IdTagInfo.ExpiryDate = ct.ExpiryDate.Value; } stopTransactionResponse.IdTagInfo.ParentIdTag = ct.ParentTagId; if (ct.Blocked.HasValue && ct.Blocked.Value) { stopTransactionResponse.IdTagInfo.Status = IdTagInfoStatus.Blocked; } else if (ct.ExpiryDate.HasValue && ct.ExpiryDate.Value < DateTime.Now) { stopTransactionResponse.IdTagInfo.Status = IdTagInfoStatus.Expired; } else { stopTransactionResponse.IdTagInfo.Status = IdTagInfoStatus.Accepted; } } else { stopTransactionResponse.IdTagInfo.Status = IdTagInfoStatus.Invalid; } Logger.LogInformation("StopTransaction => RFID-tag='{0}' => Status: {1}", idTag, stopTransactionResponse.IdTagInfo.Status); } } catch (Exception exp) { Logger.LogError(exp, "StopTransaction => Exception reading charge tag ({0}): {1}", idTag, exp.Message); stopTransactionResponse.IdTagInfo.Status = IdTagInfoStatus.Invalid; } } if (stopTransactionResponse.IdTagInfo.Status == IdTagInfoStatus.Accepted) { try { using (OCPPCoreContext dbContext = new OCPPCoreContext(Configuration)) { Transaction transaction = dbContext.Find <Transaction>(stopTransactionRequest.TransactionId); if (transaction == null || transaction.ChargePointId != ChargePointStatus.Id || transaction.StopTime.HasValue) { // unknown transaction id or already stopped transaction // => find latest transaction for the charge point and check if its open Logger.LogWarning("StopTransaction => Unknown or closed transaction id={0}", transaction?.TransactionId); transaction = dbContext.Transactions .Where(t => t.ChargePointId == ChargePointStatus.Id) .OrderByDescending(t => t.TransactionId) .FirstOrDefault(); if (transaction != null) { Logger.LogTrace("StopTransaction => Last transaction id={0} / Start='{1}' / Stop='{2}'", transaction.TransactionId, transaction.StartTime.ToString("O"), transaction?.StopTime?.ToString("o")); if (transaction.StopTime.HasValue) { Logger.LogTrace("StopTransaction => Last transaction (id={0}) is already closed ", transaction.TransactionId); transaction = null; } } else { Logger.LogTrace("StopTransaction => Found no transaction for charge point '{0}'", ChargePointStatus.Id); } } if (transaction != null) { // check current tag against start tag bool valid = true; if (!string.Equals(transaction.StartTagId, idTag, StringComparison.InvariantCultureIgnoreCase)) { // tags are different => same group? ChargeTag startTag = dbContext.Find <ChargeTag>(transaction.StartTagId); if (startTag != null) { if (!string.Equals(startTag.ParentTagId, stopTransactionResponse.IdTagInfo.ParentIdTag, StringComparison.InvariantCultureIgnoreCase)) { Logger.LogInformation("StopTransaction => Start-Tag ('{0}') and End-Tag ('{1}') do not match: Invalid!", transaction.StartTagId, idTag); stopTransactionResponse.IdTagInfo.Status = IdTagInfoStatus.Invalid; valid = false; } else { Logger.LogInformation("StopTransaction => Different RFID-Tags but matching group ('{0}')", stopTransactionResponse.IdTagInfo.ParentIdTag); } } else { Logger.LogError("StopTransaction => Start-Tag not found: '{0}'", transaction.StartTagId); // assume "valid" and allow to end the transaction } } if (valid) { transaction.StopTagId = idTag; transaction.MeterStop = (double)stopTransactionRequest.MeterStop / 1000; // Meter value here is always Wh transaction.StopReason = stopTransactionRequest.Reason.ToString(); transaction.StopTime = stopTransactionRequest.Timestamp.UtcDateTime; dbContext.SaveChanges(); } } else { Logger.LogError("StopTransaction => Unknown transaction: id={0} / chargepoint={1} / tag={2}", stopTransactionRequest.TransactionId, ChargePointStatus?.Id, idTag); WriteMessageLog(ChargePointStatus?.Id, transaction?.ConnectorId, msgIn.Action, string.Format("UnknownTransaction:ID={0}/Meter={1}", stopTransactionRequest.TransactionId, stopTransactionRequest.MeterStop), errorCode); errorCode = ErrorCodes.PropertyConstraintViolation; } } } catch (Exception exp) { Logger.LogError(exp, "StopTransaction => Exception writing transaction: chargepoint={0} / tag={1}", ChargePointStatus?.Id, idTag); errorCode = ErrorCodes.InternalError; } } msgOut.JsonPayload = JsonConvert.SerializeObject(stopTransactionResponse); Logger.LogTrace("StopTransaction => Response serialized"); } catch (Exception exp) { Logger.LogError(exp, "StopTransaction => Exception: {0}", exp.Message); errorCode = ErrorCodes.FormationViolation; } WriteMessageLog(ChargePointStatus?.Id, null, msgIn.Action, stopTransactionResponse.IdTagInfo?.Status.ToString(), errorCode); return(errorCode); }
public string HandleStartTransaction(OCPPMessage msgIn, OCPPMessage msgOut) { string errorCode = null; StartTransactionResponse startTransactionResponse = new StartTransactionResponse(); int?connectorId = null; try { Logger.LogTrace("Processing startTransaction request..."); StartTransactionRequest startTransactionRequest = JsonConvert.DeserializeObject <StartTransactionRequest>(msgIn.JsonPayload); Logger.LogTrace("StartTransaction => Message deserialized"); string idTag = Utils.CleanChargeTagId(startTransactionRequest.IdTag, Logger); connectorId = startTransactionRequest.ConnectorId; startTransactionResponse.IdTagInfo.ParentIdTag = string.Empty; startTransactionResponse.IdTagInfo.ExpiryDate = Utils.MaxExpiryDate; if (string.IsNullOrWhiteSpace(idTag)) { // no RFID-Tag => accept request startTransactionResponse.IdTagInfo.Status = IdTagInfoStatus.Accepted; Logger.LogInformation("StartTransaction => no charge tag => Status: {0}", startTransactionResponse.IdTagInfo.Status); } else { try { using (OCPPCoreContext dbContext = new OCPPCoreContext(Configuration)) { ChargeTag ct = dbContext.Find <ChargeTag>(idTag); if (ct != null) { if (ct.ExpiryDate.HasValue) { startTransactionResponse.IdTagInfo.ExpiryDate = ct.ExpiryDate.Value; } startTransactionResponse.IdTagInfo.ParentIdTag = ct.ParentTagId; if (ct.Blocked.HasValue && ct.Blocked.Value) { startTransactionResponse.IdTagInfo.Status = IdTagInfoStatus.Blocked; } else if (ct.ExpiryDate.HasValue && ct.ExpiryDate.Value < DateTime.Now) { startTransactionResponse.IdTagInfo.Status = IdTagInfoStatus.Expired; } else { startTransactionResponse.IdTagInfo.Status = IdTagInfoStatus.Accepted; } } else { startTransactionResponse.IdTagInfo.Status = IdTagInfoStatus.Invalid; } Logger.LogInformation("StartTransaction => Charge tag='{0}' => Status: {1}", idTag, startTransactionResponse.IdTagInfo.Status); } } catch (Exception exp) { Logger.LogError(exp, "StartTransaction => Exception reading charge tag ({0}): {1}", idTag, exp.Message); startTransactionResponse.IdTagInfo.Status = IdTagInfoStatus.Invalid; } } if (startTransactionResponse.IdTagInfo.Status == IdTagInfoStatus.Accepted) { try { using (OCPPCoreContext dbContext = new OCPPCoreContext(Configuration)) { Transaction transaction = new Transaction(); transaction.ChargePointId = ChargePointStatus?.Id; transaction.ConnectorId = startTransactionRequest.ConnectorId; transaction.StartTagId = idTag; transaction.StartTime = startTransactionRequest.Timestamp.UtcDateTime; transaction.MeterStart = (double)startTransactionRequest.MeterStart / 1000; // Meter value here is always Wh transaction.StartResult = startTransactionResponse.IdTagInfo.Status.ToString(); dbContext.Add <Transaction>(transaction); dbContext.SaveChanges(); // Return DB-ID as transaction ID startTransactionResponse.TransactionId = transaction.TransactionId; } } catch (Exception exp) { Logger.LogError(exp, "StartTransaction => Exception writing transaction: chargepoint={0} / tag={1}", ChargePointStatus?.Id, idTag); errorCode = ErrorCodes.InternalError; } } msgOut.JsonPayload = JsonConvert.SerializeObject(startTransactionResponse); Logger.LogTrace("StartTransaction => Response serialized"); } catch (Exception exp) { Logger.LogError(exp, "StartTransaction => Exception: {0}", exp.Message); errorCode = ErrorCodes.FormationViolation; } WriteMessageLog(ChargePointStatus?.Id, connectorId, msgIn.Action, startTransactionResponse.IdTagInfo?.Status.ToString(), errorCode); return(errorCode); }
public string HandleAuthorize(OCPPMessage msgIn, OCPPMessage msgOut) { string errorCode = null; AuthorizeResponse authorizeResponse = new AuthorizeResponse(); string idTag = null; try { Logger.LogTrace("Processing authorize request..."); AuthorizeRequest authorizeRequest = JsonConvert.DeserializeObject <AuthorizeRequest>(msgIn.JsonPayload); Logger.LogTrace("Authorize => Message deserialized"); idTag = Utils.CleanChargeTagId(authorizeRequest.IdToken?.IdToken, Logger); authorizeResponse.CustomData = new CustomDataType(); authorizeResponse.CustomData.VendorId = VendorId; authorizeResponse.IdTokenInfo = new IdTokenInfoType(); authorizeResponse.IdTokenInfo.CustomData = new CustomDataType(); authorizeResponse.IdTokenInfo.CustomData.VendorId = VendorId; authorizeResponse.IdTokenInfo.GroupIdToken = new IdTokenType(); authorizeResponse.IdTokenInfo.GroupIdToken.CustomData = new CustomDataType(); authorizeResponse.IdTokenInfo.GroupIdToken.CustomData.VendorId = VendorId; authorizeResponse.IdTokenInfo.GroupIdToken.IdToken = string.Empty; try { using (OCPPCoreContext dbContext = new OCPPCoreContext(Configuration)) { ChargeTag ct = dbContext.Find <ChargeTag>(idTag); if (ct != null) { if (!string.IsNullOrEmpty(ct.ParentTagId)) { authorizeResponse.IdTokenInfo.GroupIdToken.IdToken = ct.ParentTagId; } if (ct.Blocked.HasValue && ct.Blocked.Value) { authorizeResponse.IdTokenInfo.Status = AuthorizationStatusEnumType.Blocked; } else if (ct.ExpiryDate.HasValue && ct.ExpiryDate.Value < DateTime.Now) { authorizeResponse.IdTokenInfo.Status = AuthorizationStatusEnumType.Expired; } else { authorizeResponse.IdTokenInfo.Status = AuthorizationStatusEnumType.Accepted; } } else { authorizeResponse.IdTokenInfo.Status = AuthorizationStatusEnumType.Invalid; } Logger.LogInformation("Authorize => Status: {0}", authorizeResponse.IdTokenInfo.Status); } } catch (Exception exp) { Logger.LogError(exp, "Authorize => Exception reading charge tag ({0}): {1}", idTag, exp.Message); authorizeResponse.IdTokenInfo.Status = AuthorizationStatusEnumType.Invalid; } msgOut.JsonPayload = JsonConvert.SerializeObject(authorizeResponse); Logger.LogTrace("Authorize => Response serialized"); } catch (Exception exp) { Logger.LogError(exp, "Authorize => Exception: {0}", exp.Message); errorCode = ErrorCodes.FormationViolation; } WriteMessageLog(ChargePointStatus?.Id, null, msgIn.Action, $"'{idTag}'=>{authorizeResponse.IdTokenInfo?.Status}", errorCode); return(errorCode); }
public string HandleTransactionEvent(OCPPMessage msgIn, OCPPMessage msgOut) { string errorCode = null; TransactionEventResponse transactionEventResponse = new TransactionEventResponse(); transactionEventResponse.CustomData = new CustomDataType(); transactionEventResponse.CustomData.VendorId = VendorId; transactionEventResponse.IdTokenInfo = new IdTokenInfoType(); int connectorId = 0; try { Logger.LogTrace("TransactionEvent => Processing transactionEvent request..."); TransactionEventRequest transactionEventRequest = JsonConvert.DeserializeObject <TransactionEventRequest>(msgIn.JsonPayload); Logger.LogTrace("TransactionEvent => Message deserialized"); string idTag = Utils.CleanChargeTagId(transactionEventRequest.IdToken?.IdToken, Logger); connectorId = (transactionEventRequest.Evse != null) ? transactionEventRequest.Evse.ConnectorId : 0; if (transactionEventRequest.EventType == TransactionEventEnumType.Started) { try { #region Start Transaction using (OCPPCoreContext dbContext = new OCPPCoreContext(Configuration)) { if (string.IsNullOrWhiteSpace(idTag)) { // no RFID-Tag => accept request transactionEventResponse.IdTokenInfo.Status = AuthorizationStatusEnumType.Accepted; Logger.LogInformation("StartTransaction => no charge tag => accepted"); } else { ChargeTag ct = dbContext.Find <ChargeTag>(idTag); if (ct != null) { if (ct.Blocked.HasValue && ct.Blocked.Value) { Logger.LogInformation("StartTransaction => Tag '{1}' blocked)", idTag); transactionEventResponse.IdTokenInfo.Status = AuthorizationStatusEnumType.Blocked; } else if (ct.ExpiryDate.HasValue && ct.ExpiryDate.Value < DateTime.Now) { Logger.LogInformation("StartTransaction => Tag '{1}' expired)", idTag); transactionEventResponse.IdTokenInfo.Status = AuthorizationStatusEnumType.Expired; } else { Logger.LogInformation("StartTransaction => Tag '{1}' accepted)", idTag); transactionEventResponse.IdTokenInfo.Status = AuthorizationStatusEnumType.Accepted; } } else { Logger.LogInformation("StartTransaction => Tag '{1}' unknown)", idTag); transactionEventResponse.IdTokenInfo.Status = AuthorizationStatusEnumType.Unknown; } } if (transactionEventResponse.IdTokenInfo.Status == AuthorizationStatusEnumType.Accepted) { try { double meterKWH = GetMeterValue(transactionEventRequest.MeterValue); Logger.LogInformation("StartTransaction => Meter='{0}' (kWh)", meterKWH); Transaction transaction = new Transaction(); transaction.Uid = transactionEventRequest.TransactionInfo.TransactionId; transaction.ChargePointId = ChargePointStatus?.Id; transaction.ConnectorId = connectorId; transaction.StartTagId = idTag; transaction.StartTime = transactionEventRequest.Timestamp.UtcDateTime; transaction.MeterStart = meterKWH; transaction.StartResult = transactionEventRequest.TriggerReason.ToString(); dbContext.Add <Transaction>(transaction); dbContext.SaveChanges(); } catch (Exception exp) { Logger.LogError(exp, "StartTransaction => Exception writing transaction: chargepoint={0} / tag={1}", ChargePointStatus?.Id, idTag); errorCode = ErrorCodes.InternalError; } } } #endregion } catch (Exception exp) { Logger.LogError(exp, "StartTransaction => Exception: {0}", exp.Message); transactionEventResponse.IdTokenInfo.Status = AuthorizationStatusEnumType.Invalid; } } else if (transactionEventRequest.EventType == TransactionEventEnumType.Updated) { try { #region Update Transaction using (OCPPCoreContext dbContext = new OCPPCoreContext(Configuration)) { Transaction transaction = dbContext.Transactions .Where(t => t.Uid == transactionEventRequest.TransactionInfo.TransactionId) .OrderByDescending(t => t.TransactionId) .FirstOrDefault(); if (transaction == null || transaction.ChargePointId != ChargePointStatus.Id || transaction.StopTime.HasValue) { // unknown transaction id or already stopped transaction // => find latest transaction for the charge point and check if its open Logger.LogWarning("UpdateTransaction => Unknown or closed transaction uid={0}", transactionEventRequest.TransactionInfo?.TransactionId); // find latest transaction for this charge point transaction = dbContext.Transactions .Where(t => t.ChargePointId == ChargePointStatus.Id && t.ConnectorId == connectorId) .OrderByDescending(t => t.TransactionId) .FirstOrDefault(); if (transaction != null) { Logger.LogTrace("UpdateTransaction => Last transaction id={0} / Start='{1}' / Stop='{2}'", transaction.TransactionId, transaction.StartTime.ToString("O"), transaction?.StopTime?.ToString("O")); if (transaction.StopTime.HasValue) { Logger.LogTrace("UpdateTransaction => Last transaction (id={0}) is already closed ", transaction.TransactionId); transaction = null; } } else { Logger.LogTrace("UpdateTransaction => Found no transaction for charge point '{0}' and connectorId '{1}'", ChargePointStatus.Id, connectorId); } } if (transaction != null) { // write current meter value in "stop" value double meterKWH = GetMeterValue(transactionEventRequest.MeterValue); Logger.LogInformation("UpdateTransaction => Meter='{0}' (kWh)", meterKWH); if (meterKWH >= 0) { transaction.MeterStop = meterKWH; dbContext.SaveChanges(); } } else { Logger.LogError("UpdateTransaction => Unknown transaction: uid='{0}' / chargepoint='{1}' / tag={2}", transactionEventRequest.TransactionInfo?.TransactionId, ChargePointStatus?.Id, idTag); WriteMessageLog(ChargePointStatus?.Id, null, msgIn.Action, string.Format("UnknownTransaction:UID={0}/Meter={1}", transactionEventRequest.TransactionInfo?.TransactionId, GetMeterValue(transactionEventRequest.MeterValue)), errorCode); errorCode = ErrorCodes.PropertyConstraintViolation; } } #endregion } catch (Exception exp) { Logger.LogError(exp, "UpdateTransaction => Exception: {0}", exp.Message); transactionEventResponse.IdTokenInfo.Status = AuthorizationStatusEnumType.Invalid; } } else if (transactionEventRequest.EventType == TransactionEventEnumType.Ended) { try { #region End Transaction using (OCPPCoreContext dbContext = new OCPPCoreContext(Configuration)) { ChargeTag ct = null; if (string.IsNullOrWhiteSpace(idTag)) { // no RFID-Tag => accept request transactionEventResponse.IdTokenInfo.Status = AuthorizationStatusEnumType.Accepted; Logger.LogInformation("EndTransaction => no charge tag => accepted"); } else { ct = dbContext.Find <ChargeTag>(idTag); if (ct != null) { if (ct.Blocked.HasValue && ct.Blocked.Value) { Logger.LogInformation("EndTransaction => Tag '{1}' blocked)", idTag); transactionEventResponse.IdTokenInfo.Status = AuthorizationStatusEnumType.Blocked; } else if (ct.ExpiryDate.HasValue && ct.ExpiryDate.Value < DateTime.Now) { Logger.LogInformation("EndTransaction => Tag '{1}' expired)", idTag); transactionEventResponse.IdTokenInfo.Status = AuthorizationStatusEnumType.Expired; } else { Logger.LogInformation("EndTransaction => Tag '{1}' accepted)", idTag); transactionEventResponse.IdTokenInfo.Status = AuthorizationStatusEnumType.Accepted; } } else { Logger.LogInformation("EndTransaction => Tag '{1}' unknown)", idTag); transactionEventResponse.IdTokenInfo.Status = AuthorizationStatusEnumType.Unknown; } } Transaction transaction = dbContext.Transactions .Where(t => t.Uid == transactionEventRequest.TransactionInfo.TransactionId) .OrderByDescending(t => t.TransactionId) .FirstOrDefault(); if (transaction == null || transaction.ChargePointId != ChargePointStatus.Id || transaction.StopTime.HasValue) { // unknown transaction id or already stopped transaction // => find latest transaction for the charge point and check if its open Logger.LogWarning("EndTransaction => Unknown or closed transaction uid={0}", transactionEventRequest.TransactionInfo?.TransactionId); // find latest transaction for this charge point transaction = dbContext.Transactions .Where(t => t.ChargePointId == ChargePointStatus.Id && t.ConnectorId == connectorId) .OrderByDescending(t => t.TransactionId) .FirstOrDefault(); if (transaction != null) { Logger.LogTrace("EndTransaction => Last transaction id={0} / Start='{1}' / Stop='{2}'", transaction.TransactionId, transaction.StartTime.ToString("O"), transaction?.StopTime?.ToString("O")); if (transaction.StopTime.HasValue) { Logger.LogTrace("EndTransaction => Last transaction (id={0}) is already closed ", transaction.TransactionId); transaction = null; } } else { Logger.LogTrace("EndTransaction => Found no transaction for charge point '{0}' and connectorId '{1}'", ChargePointStatus.Id, connectorId); } } if (transaction != null) { // check current tag against start tag bool valid = true; if (!string.Equals(transaction.StartTagId, idTag, StringComparison.InvariantCultureIgnoreCase)) { // tags are different => same group? ChargeTag startTag = dbContext.Find <ChargeTag>(transaction.StartTagId); if (startTag != null) { if (!string.Equals(startTag.ParentTagId, ct?.ParentTagId, StringComparison.InvariantCultureIgnoreCase)) { Logger.LogInformation("EndTransaction => Start-Tag ('{0}') and End-Tag ('{1}') do not match: Invalid!", transaction.StartTagId, ct?.TagId); transactionEventResponse.IdTokenInfo.Status = AuthorizationStatusEnumType.Invalid; valid = false; } else { Logger.LogInformation("EndTransaction => Different charge tags but matching group ('{0}')", ct?.ParentTagId); } } else { Logger.LogError("EndTransaction => Start-Tag not found: '{0}'", transaction.StartTagId); // assume "valid" and allow to end the transaction } } if (valid) { // write current meter value in "stop" value double meterKWH = GetMeterValue(transactionEventRequest.MeterValue); Logger.LogInformation("EndTransaction => Meter='{0}' (kWh)", meterKWH); transaction.StopTime = transactionEventRequest.Timestamp.UtcDateTime; transaction.MeterStop = meterKWH; transaction.StopTagId = idTag; transaction.StopReason = transactionEventRequest.TriggerReason.ToString(); dbContext.SaveChanges(); } } else { Logger.LogError("EndTransaction => Unknown transaction: uid='{0}' / chargepoint='{1}' / tag={2}", transactionEventRequest.TransactionInfo?.TransactionId, ChargePointStatus?.Id, idTag); WriteMessageLog(ChargePointStatus?.Id, connectorId, msgIn.Action, string.Format("UnknownTransaction:UID={0}/Meter={1}", transactionEventRequest.TransactionInfo?.TransactionId, GetMeterValue(transactionEventRequest.MeterValue)), errorCode); errorCode = ErrorCodes.PropertyConstraintViolation; } } #endregion } catch (Exception exp) { Logger.LogError(exp, "EndTransaction => Exception: {0}", exp.Message); transactionEventResponse.IdTokenInfo.Status = AuthorizationStatusEnumType.Invalid; } } msgOut.JsonPayload = JsonConvert.SerializeObject(transactionEventResponse); Logger.LogTrace("TransactionEvent => Response serialized"); } catch (Exception exp) { Logger.LogError(exp, "TransactionEvent => Exception: {0}", exp.Message); errorCode = ErrorCodes.FormationViolation; } WriteMessageLog(ChargePointStatus?.Id, connectorId, msgIn.Action, transactionEventResponse.IdTokenInfo.Status.ToString(), errorCode); return(errorCode); }
public string HandleAuthorize(OCPPMessage msgIn, OCPPMessage msgOut) { string errorCode = null; AuthorizeResponse authorizeResponse = new AuthorizeResponse(); string idTag = null; try { Logger.LogTrace("Processing authorize request..."); AuthorizeRequest authorizeRequest = JsonConvert.DeserializeObject <AuthorizeRequest>(msgIn.JsonPayload); Logger.LogTrace("Authorize => Message deserialized"); idTag = Utils.CleanChargeTagId(authorizeRequest.IdTag, Logger); authorizeResponse.IdTagInfo.ParentIdTag = string.Empty; authorizeResponse.IdTagInfo.ExpiryDate = DateTimeOffset.UtcNow.AddMinutes(5); // default: 5 minutes try { using (OCPPCoreContext dbContext = new OCPPCoreContext(Configuration)) { ChargeTag ct = dbContext.Find <ChargeTag>(idTag); if (ct != null) { if (ct.ExpiryDate.HasValue) { authorizeResponse.IdTagInfo.ExpiryDate = ct.ExpiryDate.Value; } authorizeResponse.IdTagInfo.ParentIdTag = ct.ParentTagId; if (ct.Blocked.HasValue && ct.Blocked.Value) { authorizeResponse.IdTagInfo.Status = IdTagInfoStatus.Blocked; } else if (ct.ExpiryDate.HasValue && ct.ExpiryDate.Value < DateTime.Now) { authorizeResponse.IdTagInfo.Status = IdTagInfoStatus.Expired; } else { authorizeResponse.IdTagInfo.Status = IdTagInfoStatus.Accepted; } } else { authorizeResponse.IdTagInfo.Status = IdTagInfoStatus.Invalid; } Logger.LogInformation("Authorize => Status: {0}", authorizeResponse.IdTagInfo.Status); } } catch (Exception exp) { Logger.LogError(exp, "Authorize => Exception reading charge tag ({0}): {1}", idTag, exp.Message); authorizeResponse.IdTagInfo.Status = IdTagInfoStatus.Invalid; } msgOut.JsonPayload = JsonConvert.SerializeObject(authorizeResponse); Logger.LogTrace("Authorize => Response serialized"); } catch (Exception exp) { Logger.LogError(exp, "Authorize => Exception: {0}", exp.Message); errorCode = ErrorCodes.FormationViolation; } WriteMessageLog(ChargePointStatus?.Id, null, msgIn.Action, $"'{idTag}'=>{authorizeResponse.IdTagInfo?.Status}", errorCode); return(errorCode); }