/// <summary> /// Sends a Unlock-Request to the chargepoint /// </summary> private async Task UnlockConnector16(ChargePointStatus chargePointStatus, HttpContext apiCallerContext) { ILogger logger = _logFactory.CreateLogger("OCPPMiddleware.OCPP16"); ControllerOCPP16 controller16 = new ControllerOCPP16(_configuration, _logFactory, chargePointStatus); Messages_OCPP16.UnlockConnectorRequest unlockConnectorRequest = new Messages_OCPP16.UnlockConnectorRequest(); unlockConnectorRequest.ConnectorId = 0; string jsonResetRequest = JsonConvert.SerializeObject(unlockConnectorRequest); OCPPMessage msgOut = new OCPPMessage(); msgOut.MessageType = "2"; msgOut.Action = "UnlockConnector"; msgOut.UniqueId = Guid.NewGuid().ToString("N"); msgOut.JsonPayload = jsonResetRequest; msgOut.TaskCompletionSource = new TaskCompletionSource <string>(); // store HttpContext with MsgId for later answer processing (=> send anwer to API caller) _requestQueue.Add(msgOut.UniqueId, msgOut); // Send OCPP message with optional logging/dump await SendOcpp16Message(msgOut, logger, chargePointStatus.WebSocket); // Wait for asynchronous chargepoint response and processing string apiResult = await msgOut.TaskCompletionSource.Task; // apiCallerContext.Response.StatusCode = 200; apiCallerContext.Response.ContentType = "application/json"; await apiCallerContext.Response.WriteAsync(apiResult); }
/// <summary> /// Processes the charge point message and returns the answer message /// </summary> public OCPPMessage ProcessRequest(OCPPMessage msgIn) { OCPPMessage msgOut = new OCPPMessage(); msgOut.MessageType = "3"; msgOut.UniqueId = msgIn.UniqueId; string errorCode = null; switch (msgIn.Action) { case "BootNotification": errorCode = HandleBootNotification(msgIn, msgOut); break; case "Heartbeat": errorCode = HandleHeartBeat(msgIn, msgOut); break; case "Authorize": errorCode = HandleAuthorize(msgIn, msgOut); break; case "StartTransaction": errorCode = HandleStartTransaction(msgIn, msgOut); break; case "StopTransaction": errorCode = HandleStopTransaction(msgIn, msgOut); break; case "MeterValues": errorCode = HandleMeterValues(msgIn, msgOut); break; case "StatusNotification": errorCode = HandleStatusNotification(msgIn, msgOut); break; case "DataTransfer": errorCode = HandleDataTransfer(msgIn, msgOut); break; default: errorCode = ErrorCodes.NotSupported; WriteMessageLog(ChargePointStatus.Id, null, msgIn.Action, msgIn.JsonPayload, errorCode); break; } if (!string.IsNullOrEmpty(errorCode)) { // Inavlid message type => return type "4" (CALLERROR) msgOut.MessageType = "4"; msgOut.ErrorCode = errorCode; Logger.LogDebug("ControllerOCPP16 => Return error code messge: ErrorCode={0}", errorCode); } return(msgOut); }
public string HandleHeartBeat(OCPPMessage msgIn, OCPPMessage msgOut) { string errorCode = null; Logger.LogTrace("Processing heartbeat..."); HeartbeatResponse heartbeatResponse = new HeartbeatResponse(); heartbeatResponse.CurrentTime = DateTimeOffset.UtcNow; msgOut.JsonPayload = JsonConvert.SerializeObject(heartbeatResponse); Logger.LogTrace("Heartbeat => Response serialized"); WriteMessageLog(ChargePointStatus?.Id, null, msgIn.Action, null, errorCode); return(errorCode); }
private async Task SendOcpp16Message(OCPPMessage msg, ILogger logger, WebSocket webSocket) { string ocppTextMessage = null; if (string.IsNullOrEmpty(msg.ErrorCode)) { if (msg.MessageType == "2") { // OCPP-Request ocppTextMessage = string.Format("[{0},\"{1}\",\"{2}\",{3}]", msg.MessageType, msg.UniqueId, msg.Action, msg.JsonPayload); } else { // OCPP-Response ocppTextMessage = string.Format("[{0},\"{1}\",{2}]", msg.MessageType, msg.UniqueId, msg.JsonPayload); } } else { ocppTextMessage = string.Format("[{0},\"{1}\",\"{2}\",\"{3}\",{4}]", msg.MessageType, msg.UniqueId, msg.ErrorCode, msg.ErrorDescription, "{}"); } logger.LogTrace("OCPPMiddleware.OCPP16 => SendOcppMessage: {0}", ocppTextMessage); if (string.IsNullOrEmpty(ocppTextMessage)) { // invalid message ocppTextMessage = string.Format("[{0},\"{1}\",\"{2}\",\"{3}\",{4}]", "4", string.Empty, Messages_OCPP16.ErrorCodes.ProtocolError, string.Empty, "{}"); } string dumpDir = _configuration.GetValue <string>("MessageDumpDir"); if (!string.IsNullOrWhiteSpace(dumpDir)) { // Write outgoing message into dump directory string path = Path.Combine(dumpDir, string.Format("{0}_ocpp16-out.txt", DateTime.Now.ToString("yyyy-MM-dd_HH-mm-ss-ffff"))); try { File.WriteAllText(path, ocppTextMessage); } catch (Exception exp) { logger.LogError(exp, "OCPPMiddleware.SendOcpp16Message=> Error dumping message to path: '{0}'", path); } } byte[] binaryMessage = UTF8Encoding.UTF8.GetBytes(ocppTextMessage); await webSocket.SendAsync(new ArraySegment <byte>(binaryMessage, 0, binaryMessage.Length), WebSocketMessageType.Text, true, CancellationToken.None); }
public string HandleBootNotification(OCPPMessage msgIn, OCPPMessage msgOut) { string errorCode = null; string bootReason = null; try { Logger.LogTrace("Processing boot notification..."); BootNotificationRequest bootNotificationRequest = JsonConvert.DeserializeObject <BootNotificationRequest>(msgIn.JsonPayload); Logger.LogTrace("BootNotification => Message deserialized"); bootReason = bootNotificationRequest?.Reason.ToString(); Logger.LogInformation("BootNotification => Reason={0}", bootReason); BootNotificationResponse bootNotificationResponse = new BootNotificationResponse(); bootNotificationResponse.CurrentTime = DateTimeOffset.UtcNow; bootNotificationResponse.Interval = 300; // 300 seconds bootNotificationResponse.StatusInfo = new StatusInfoType(); bootNotificationResponse.StatusInfo.ReasonCode = string.Empty; bootNotificationResponse.StatusInfo.AdditionalInfo = string.Empty; bootNotificationResponse.CustomData = new CustomDataType(); bootNotificationResponse.CustomData.VendorId = VendorId; if (ChargePointStatus != null) { // Known charge station => accept bootNotificationResponse.Status = RegistrationStatusEnumType.Accepted; } else { // Unknown charge station => reject bootNotificationResponse.Status = RegistrationStatusEnumType.Rejected; } msgOut.JsonPayload = JsonConvert.SerializeObject(bootNotificationResponse); Logger.LogTrace("BootNotification => Response serialized"); } catch (Exception exp) { Logger.LogError(exp, "BootNotification => Exception: {0}", exp.Message); errorCode = ErrorCodes.FormationViolation; } WriteMessageLog(ChargePointStatus.Id, null, msgIn.Action, bootReason, errorCode); return(errorCode); }
/// <summary> /// Processes the charge point message and returns the answer message /// </summary> public void ProcessAnswer(OCPPMessage msgIn, OCPPMessage msgOut) { // The response (msgIn) has no action => check action in original request (msgOut) switch (msgOut.Action) { case "Reset": HandleReset(msgIn, msgOut); break; case "UnlockConnector": HandleUnlockConnector(msgIn, msgOut); break; default: WriteMessageLog(ChargePointStatus.Id, null, msgIn.Action, msgIn.JsonPayload, "Unknown answer"); break; } }
public string HandleClearedChargingLimit(OCPPMessage msgIn, OCPPMessage msgOut) { string errorCode = null; Logger.LogTrace("Processing ClearedChargingLimit..."); ClearedChargingLimitResponse clearedChargingLimitResponse = new ClearedChargingLimitResponse(); clearedChargingLimitResponse.CustomData = new CustomDataType(); clearedChargingLimitResponse.CustomData.VendorId = VendorId; string source = null; int connectorId = 0; try { ClearedChargingLimitRequest clearedChargingLimitRequest = JsonConvert.DeserializeObject <ClearedChargingLimitRequest>(msgIn.JsonPayload); Logger.LogTrace("ClearedChargingLimit => Message deserialized"); if (ChargePointStatus != null) { // Known charge station source = clearedChargingLimitRequest.ChargingLimitSource.ToString(); connectorId = clearedChargingLimitRequest.EvseId; Logger.LogInformation("ClearedChargingLimit => Source={0}", source); } else { // Unknown charge station errorCode = ErrorCodes.GenericError; } msgOut.JsonPayload = JsonConvert.SerializeObject(clearedChargingLimitResponse); Logger.LogTrace("ClearedChargingLimit => Response serialized"); } catch (Exception exp) { Logger.LogError(exp, "ClearedChargingLimit => Exception: {0}", exp.Message); errorCode = ErrorCodes.InternalError; } WriteMessageLog(ChargePointStatus.Id, connectorId, msgIn.Action, source, errorCode); return(errorCode); }
public string HandleFirmwareStatusNotification(OCPPMessage msgIn, OCPPMessage msgOut) { string errorCode = null; Logger.LogTrace("Processing FirmwareStatusNotification..."); FirmwareStatusNotificationResponse firmwareStatusNotificationResponse = new FirmwareStatusNotificationResponse(); firmwareStatusNotificationResponse.CustomData = new CustomDataType(); firmwareStatusNotificationResponse.CustomData.VendorId = VendorId; string status = null; try { FirmwareStatusNotificationRequest firmwareStatusNotificationRequest = JsonConvert.DeserializeObject <FirmwareStatusNotificationRequest>(msgIn.JsonPayload); Logger.LogTrace("FirmwareStatusNotification => Message deserialized"); if (ChargePointStatus != null) { // Known charge station status = firmwareStatusNotificationRequest.Status.ToString(); Logger.LogInformation("FirmwareStatusNotification => Status={0}", status); } else { // Unknown charge station errorCode = ErrorCodes.GenericError; } msgOut.JsonPayload = JsonConvert.SerializeObject(firmwareStatusNotificationResponse); Logger.LogTrace("FirmwareStatusNotification => Response serialized"); } catch (Exception exp) { Logger.LogError(exp, "FirmwareStatusNotification => Exception: {0}", exp.Message); errorCode = ErrorCodes.InternalError; } WriteMessageLog(ChargePointStatus.Id, null, msgIn.Action, status, errorCode); return(errorCode); }
public string HandleDataTransfer(OCPPMessage msgIn, OCPPMessage msgOut) { string errorCode = null; DataTransferResponse dataTransferResponse = new DataTransferResponse(); bool msgWritten = false; try { Logger.LogTrace("Processing data transfer..."); DataTransferRequest dataTransferRequest = JsonConvert.DeserializeObject <DataTransferRequest>(msgIn.JsonPayload); Logger.LogTrace("DataTransfer => Message deserialized"); if (ChargePointStatus != null) { // Known charge station msgWritten = WriteMessageLog(ChargePointStatus.Id, null, msgIn.Action, string.Format("VendorId={0} / MessageId={1} / Data={2}", dataTransferRequest.VendorId, dataTransferRequest.MessageId, dataTransferRequest.Data), errorCode); dataTransferResponse.Status = DataTransferResponseStatus.UnknownVendorId; } else { // Unknown charge station errorCode = ErrorCodes.GenericError; } msgOut.JsonPayload = JsonConvert.SerializeObject(dataTransferResponse); Logger.LogTrace("DataTransfer => Response serialized"); } catch (Exception exp) { Logger.LogError(exp, "DataTransfer => Exception: {0}", exp.Message); errorCode = ErrorCodes.InternalError; } if (!msgWritten) { WriteMessageLog(ChargePointStatus.Id, null, msgIn.Action, null, errorCode); } return(errorCode); }
public string HandleBootNotification(OCPPMessage msgIn, OCPPMessage msgOut) { string errorCode = null; try { Logger.LogTrace("Processing boot notification..."); BootNotificationRequest bootNotificationRequest = JsonConvert.DeserializeObject <BootNotificationRequest>(msgIn.JsonPayload); Logger.LogTrace("BootNotification => Message deserialized"); BootNotificationResponse bootNotificationResponse = new BootNotificationResponse(); bootNotificationResponse.CurrentTime = DateTimeOffset.UtcNow; bootNotificationResponse.Interval = 300; // 300 seconds if (ChargePointStatus != null) { // Known charge station => accept bootNotificationResponse.Status = BootNotificationResponseStatus.Accepted; } else { // Unknown charge station => reject bootNotificationResponse.Status = BootNotificationResponseStatus.Rejected; } msgOut.JsonPayload = JsonConvert.SerializeObject(bootNotificationResponse); Logger.LogTrace("BootNotification => Response serialized"); } catch (Exception exp) { Logger.LogError(exp, "BootNotification => Exception: {0}", exp.Message); errorCode = ErrorCodes.FormationViolation; } WriteMessageLog(ChargePointStatus.Id, null, msgIn.Action, null, errorCode); return(errorCode); }
public void HandleReset(OCPPMessage msgIn, OCPPMessage msgOut) { Logger.LogInformation("Reset answer: ChargePointId={0} / MsgType={1} / ErrCode={2}", ChargePointStatus.Id, msgIn.MessageType, msgIn.ErrorCode); try { ResetResponse resetResponse = JsonConvert.DeserializeObject <ResetResponse>(msgIn.JsonPayload); Logger.LogInformation("Reset => Answer status: {0}", resetResponse?.Status); WriteMessageLog(ChargePointStatus?.Id, null, msgOut.Action, resetResponse?.Status.ToString(), msgIn.ErrorCode); if (msgOut.TaskCompletionSource != null) { // Set API response as TaskCompletion-result string apiResult = "{\"status\": " + JsonConvert.ToString(resetResponse.Status.ToString()) + "}"; Logger.LogTrace("HandleReset => API response: {0}", apiResult); msgOut.TaskCompletionSource.SetResult(apiResult); } } catch (Exception exp) { Logger.LogError(exp, "HandleReset => Exception: {0}", exp.Message); } }
/// <summary> /// Waits for new OCPP V1.6 messages on the open websocket connection and delegates processing to a controller /// </summary> private async Task Receive16(ChargePointStatus chargePointStatus, HttpContext context) { ILogger logger = _logFactory.CreateLogger("OCPPMiddleware.OCPP16"); ControllerOCPP16 controller16 = new ControllerOCPP16(_configuration, _logFactory, chargePointStatus); byte[] buffer = new byte[1024 * 4]; MemoryStream memStream = new MemoryStream(buffer.Length); while (chargePointStatus.WebSocket.State == WebSocketState.Open) { WebSocketReceiveResult result = await chargePointStatus.WebSocket.ReceiveAsync(new ArraySegment <byte>(buffer), CancellationToken.None); if (result != null && result.MessageType != WebSocketMessageType.Close) { logger.LogTrace("OCPPMiddleware.Receive16 => Receiving segment: {0} bytes (EndOfMessage={1} / MsgType={2})", result.Count, result.EndOfMessage, result.MessageType); memStream.Write(buffer, 0, result.Count); if (result.EndOfMessage) { // read complete message into byte array byte[] bMessage = memStream.ToArray(); // reset memory stream für next message memStream = new MemoryStream(buffer.Length); string dumpDir = _configuration.GetValue <string>("MessageDumpDir"); if (!string.IsNullOrWhiteSpace(dumpDir)) { string path = Path.Combine(dumpDir, string.Format("{0}_ocpp16-in.txt", DateTime.Now.ToString("yyyy-MM-dd_HH-mm-ss-ffff"))); try { // Write incoming message into dump directory File.WriteAllBytes(path, bMessage); } catch (Exception exp) { logger.LogError(exp, "OCPPMiddleware.Receive16 => Error dumping incoming message to path: '{0}'", path); } } string ocppMessage = UTF8Encoding.UTF8.GetString(bMessage); Match match = Regex.Match(ocppMessage, MessageRegExp); if (match != null && match.Groups != null && match.Groups.Count >= 3) { string messageTypeId = match.Groups[1].Value; string uniqueId = match.Groups[2].Value; string action = match.Groups[3].Value; string jsonPaylod = match.Groups[4].Value; logger.LogInformation("OCPPMiddleware.Receive16 => OCPP-Message: Type={0} / ID={1} / Action={2})", messageTypeId, uniqueId, action); OCPPMessage msgIn = new OCPPMessage(messageTypeId, uniqueId, action, jsonPaylod); if (msgIn.MessageType == "2") { // Request from chargepoint to OCPP server OCPPMessage msgOut = controller16.ProcessRequest(msgIn); // Send OCPP message with optional logging/dump await SendOcpp16Message(msgOut, logger, chargePointStatus.WebSocket); } else if (msgIn.MessageType == "3" || msgIn.MessageType == "4") { // Process answer from chargepoint if (_requestQueue.ContainsKey(msgIn.UniqueId)) { controller16.ProcessAnswer(msgIn, _requestQueue[msgIn.UniqueId]); _requestQueue.Remove(msgIn.UniqueId); } else { logger.LogError("OCPPMiddleware.Receive16 => HttpContext from caller not found / Msg: {0}", ocppMessage); } } else { // Unknown message type logger.LogError("OCPPMiddleware.Receive16 => Unknown message type: {0} / Msg: {1}", msgIn.MessageType, ocppMessage); } } else { logger.LogWarning("OCPPMiddleware.Receive16 => Error in RegEx-Matching: Msg={0})", ocppMessage); } } } else { logger.LogInformation("OCPPMiddleware.Receive16 => WebSocket Closed: CloseStatus={0} / MessageType={1}", result?.CloseStatus, result?.MessageType); await chargePointStatus.WebSocket.CloseOutputAsync((WebSocketCloseStatus)3001, string.Empty, CancellationToken.None); } } logger.LogInformation("OCPPMiddleware.Receive16 => Websocket closed: State={0} / CloseStatus={1}", chargePointStatus.WebSocket.State, chargePointStatus.WebSocket.CloseStatus); ChargePointStatus dummy; _chargePointStatusDict.Remove(chargePointStatus.Id, out dummy); }
public string HandleNotifyEVChargingSchedule(OCPPMessage msgIn, OCPPMessage msgOut) { string errorCode = null; Logger.LogTrace("Processing NotifyEVChargingSchedule..."); NotifyEVChargingScheduleResponse notifyEVChargingScheduleResponse = new NotifyEVChargingScheduleResponse(); notifyEVChargingScheduleResponse.CustomData = new CustomDataType(); notifyEVChargingScheduleResponse.CustomData.VendorId = VendorId; StringBuilder periods = new StringBuilder(); int connectorId = 0; try { NotifyEVChargingScheduleRequest notifyEVChargingScheduleRequest = JsonConvert.DeserializeObject <NotifyEVChargingScheduleRequest>(msgIn.JsonPayload); Logger.LogTrace("NotifyEVChargingSchedule => Message deserialized"); if (ChargePointStatus != null) { // Known charge station if (notifyEVChargingScheduleRequest.ChargingSchedule != null) { if (notifyEVChargingScheduleRequest.ChargingSchedule?.ChargingSchedulePeriod != null) { // Concat all periods and write them in messag log... DateTimeOffset timeBase = notifyEVChargingScheduleRequest.TimeBase; foreach (ChargingSchedulePeriodType period in notifyEVChargingScheduleRequest.ChargingSchedule?.ChargingSchedulePeriod) { if (periods.Length > 0) { periods.Append(" | "); } DateTimeOffset time = timeBase.AddSeconds(period.StartPeriod); periods.Append(string.Format("{0}: {1}{2}", time.ToString("O"), period.Limit, notifyEVChargingScheduleRequest.ChargingSchedule.ChargingRateUnit.ToString())); if (period.NumberPhases > 0) { periods.Append(string.Format(" ({0} Phases)", period.NumberPhases)); } } } } connectorId = notifyEVChargingScheduleRequest.EvseId; Logger.LogInformation("NotifyEVChargingSchedule => {0}", periods.ToString()); } else { // Unknown charge station errorCode = ErrorCodes.GenericError; } msgOut.JsonPayload = JsonConvert.SerializeObject(notifyEVChargingScheduleResponse); Logger.LogTrace("NotifyEVChargingSchedule => Response serialized"); } catch (Exception exp) { Logger.LogError(exp, "NotifyEVChargingSchedule => Exception: {0}", exp.Message); errorCode = ErrorCodes.InternalError; } WriteMessageLog(ChargePointStatus.Id, connectorId, msgIn.Action, periods.ToString(), errorCode); return(errorCode); }
public string HandleMeterValues(OCPPMessage msgIn, OCPPMessage msgOut) { string errorCode = null; MeterValuesResponse meterValuesResponse = new MeterValuesResponse(); int? connectorId = null; string msgMeterValue = string.Empty; try { Logger.LogTrace("Processing meter values..."); MeterValuesRequest meterValueRequest = JsonConvert.DeserializeObject <MeterValuesRequest>(msgIn.JsonPayload); Logger.LogTrace("MeterValues => Message deserialized"); connectorId = meterValueRequest.ConnectorId; if (ChargePointStatus != null) { // Known charge station => process meter values double currentChargeKW = -1; double meterKWH = -1; double stateOfCharge = -1; foreach (MeterValue meterValue in meterValueRequest.MeterValue) { foreach (SampledValue sampleValue in meterValue.SampledValue) { Logger.LogTrace("MeterValues => Context={0} / Format={1} / Value={2} / Unit={3} / Location={4} / Measurand={5} / Phase={6}", sampleValue.Context, sampleValue.Format, sampleValue.Value, sampleValue.Unit, sampleValue.Location, sampleValue.Measurand, sampleValue.Phase); if (sampleValue.Measurand == SampledValueMeasurand.Power_Active_Import) { // current charging power if (double.TryParse(sampleValue.Value, NumberStyles.Float, CultureInfo.InvariantCulture, out currentChargeKW)) { if (sampleValue.Unit == SampledValueUnit.W || sampleValue.Unit == SampledValueUnit.VA || sampleValue.Unit == SampledValueUnit.Var || sampleValue.Unit == null) { Logger.LogTrace("MeterValues => Charging '{0:0.0}' W", currentChargeKW); // convert W => kW currentChargeKW = currentChargeKW / 1000; } else if (sampleValue.Unit == SampledValueUnit.KW || sampleValue.Unit == SampledValueUnit.KVA || sampleValue.Unit == SampledValueUnit.Kvar) { // already kW => OK Logger.LogTrace("MeterValues => Charging '{0:0.0}' kW", currentChargeKW); } else { Logger.LogWarning("MeterValues => Charging: unexpected unit: '{0}' (Value={1})", sampleValue.Unit, sampleValue.Value); } } else { Logger.LogError("MeterValues => Charging: invalid value '{0}' (Unit={1})", sampleValue.Value, sampleValue.Unit); } } else if (sampleValue.Measurand == SampledValueMeasurand.Energy_Active_Import_Register || sampleValue.Measurand == null) { // charged amount of energy if (double.TryParse(sampleValue.Value, NumberStyles.Float, CultureInfo.InvariantCulture, out meterKWH)) { if (sampleValue.Unit == SampledValueUnit.Wh || sampleValue.Unit == SampledValueUnit.Varh || sampleValue.Unit == null) { Logger.LogTrace("MeterValues => Value: '{0:0.0}' Wh", meterKWH); // convert Wh => kWh meterKWH = meterKWH / 1000; } else if (sampleValue.Unit == SampledValueUnit.KWh || sampleValue.Unit == SampledValueUnit.Kvarh) { // already kWh => OK Logger.LogTrace("MeterValues => Value: '{0:0.0}' kWh", meterKWH); } else { Logger.LogWarning("MeterValues => Value: unexpected unit: '{0}' (Value={1})", sampleValue.Unit, sampleValue.Value); } } else { Logger.LogError("MeterValues => Value: invalid value '{0}' (Unit={1})", sampleValue.Value, sampleValue.Unit); } } else if (sampleValue.Measurand == SampledValueMeasurand.SoC) { // state of charge (battery status) if (double.TryParse(sampleValue.Value, NumberStyles.Float, CultureInfo.InvariantCulture, out stateOfCharge)) { Logger.LogTrace("MeterValues => SoC: '{0:0.0}'%", stateOfCharge); } else { Logger.LogError("MeterValues => invalid value '{0}' (SoC)", sampleValue.Value); } } } } // write charging/meter data in chargepoint status ChargingData chargingData = null; if (currentChargeKW >= 0 || meterKWH >= 0 || stateOfCharge >= 0) { chargingData = new ChargingData(); if (currentChargeKW >= 0) { chargingData.ChargeRateKW = currentChargeKW; } if (meterKWH >= 0) { chargingData.MeterKWH = meterKWH; } if (stateOfCharge >= 0) { chargingData.SoC = stateOfCharge; } if (currentChargeKW >= 0) { chargingData.ChargeRateKW = currentChargeKW; } msgMeterValue = $"Meter (kWh): {meterKWH} | Charge (kW): {currentChargeKW} | SoC (%): {stateOfCharge}"; } if (connectorId > 1) { // second connector (or higher!?) ChargePointStatus.ChargingDataEVSE2 = chargingData; } else { // first connector ChargePointStatus.ChargingDataEVSE1 = chargingData; } } else { // Unknown charge station errorCode = ErrorCodes.GenericError; } msgOut.JsonPayload = JsonConvert.SerializeObject(meterValuesResponse); Logger.LogTrace("MeterValues => Response serialized"); } catch (Exception exp) { Logger.LogError(exp, "MeterValues => Exception: {0}", exp.Message); errorCode = ErrorCodes.InternalError; } WriteMessageLog(ChargePointStatus.Id, connectorId, msgIn.Action, msgMeterValue, errorCode); return(errorCode); }
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 HandleStatusNotification(OCPPMessage msgIn, OCPPMessage msgOut) { string errorCode = null; StatusNotificationResponse statusNotificationResponse = new StatusNotificationResponse(); int connectorId = 0; bool msgWritten = false; try { Logger.LogTrace("Processing status notification..."); StatusNotificationRequest statusNotificationRequest = JsonConvert.DeserializeObject <StatusNotificationRequest>(msgIn.JsonPayload); Logger.LogTrace("StatusNotification => Message deserialized"); connectorId = statusNotificationRequest.ConnectorId; // Write raw status in DB msgWritten = WriteMessageLog(ChargePointStatus.Id, connectorId, msgIn.Action, string.Format("Info={0} / Status={1} / ", statusNotificationRequest.Info, statusNotificationRequest.Status), statusNotificationRequest.ErrorCode.ToString()); ConnectorStatus newStatus = ConnectorStatus.Undefined; switch (statusNotificationRequest.Status) { case StatusNotificationRequestStatus.Available: newStatus = ConnectorStatus.Available; break; case StatusNotificationRequestStatus.Preparing: case StatusNotificationRequestStatus.Charging: case StatusNotificationRequestStatus.SuspendedEVSE: case StatusNotificationRequestStatus.SuspendedEV: case StatusNotificationRequestStatus.Finishing: case StatusNotificationRequestStatus.Reserved: newStatus = ConnectorStatus.Occupied; break; case StatusNotificationRequestStatus.Unavailable: newStatus = ConnectorStatus.Unavailable; break; case StatusNotificationRequestStatus.Faulted: newStatus = ConnectorStatus.Faulted; break; } Logger.LogInformation("StatusNotification => ChargePoint={0} / Connector={1} / newStatus={2}", ChargePointStatus?.Id, connectorId, newStatus.ToString()); if (connectorId <= 1) { ChargePointStatus.EVSE1Status = newStatus; } else if (connectorId == 2) { ChargePointStatus.EVSE2Status = newStatus; } msgOut.JsonPayload = JsonConvert.SerializeObject(statusNotificationResponse); Logger.LogTrace("StatusNotification => Response serialized"); } catch (Exception exp) { Logger.LogError(exp, "StatusNotification => ChargePoint={0} / Exception: {1}", ChargePointStatus.Id, exp.Message); errorCode = ErrorCodes.InternalError; } if (!msgWritten) { WriteMessageLog(ChargePointStatus.Id, connectorId, msgIn.Action, null, 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); }
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 HandleNotifyChargingLimit(OCPPMessage msgIn, OCPPMessage msgOut) { string errorCode = null; Logger.LogTrace("Processing NotifyChargingLimit..."); NotifyChargingLimitResponse notifyChargingLimitResponse = new NotifyChargingLimitResponse(); notifyChargingLimitResponse.CustomData = new CustomDataType(); notifyChargingLimitResponse.CustomData.VendorId = VendorId; string source = null; StringBuilder periods = new StringBuilder(); int connectorId = 0; try { NotifyChargingLimitRequest notifyChargingLimitRequest = JsonConvert.DeserializeObject <NotifyChargingLimitRequest>(msgIn.JsonPayload); Logger.LogTrace("NotifyChargingLimit => Message deserialized"); if (ChargePointStatus != null) { // Known charge station source = notifyChargingLimitRequest.ChargingLimit?.ChargingLimitSource.ToString(); if (notifyChargingLimitRequest.ChargingSchedule != null) { foreach (ChargingScheduleType schedule in notifyChargingLimitRequest.ChargingSchedule) { if (schedule.ChargingSchedulePeriod != null) { foreach (ChargingSchedulePeriodType period in schedule.ChargingSchedulePeriod) { if (periods.Length > 0) { periods.Append(" | "); } periods.Append(string.Format("{0}s: {1}{2}", period.StartPeriod, period.Limit, schedule.ChargingRateUnit)); if (period.NumberPhases > 0) { periods.Append(string.Format(" ({0} Phases)", period.NumberPhases)); } } } } } connectorId = notifyChargingLimitRequest.EvseId; Logger.LogInformation("NotifyChargingLimit => {0}", periods); } else { // Unknown charge station errorCode = ErrorCodes.GenericError; } msgOut.JsonPayload = JsonConvert.SerializeObject(notifyChargingLimitResponse); Logger.LogTrace("NotifyChargingLimit => Response serialized"); } catch (Exception exp) { Logger.LogError(exp, "NotifyChargingLimit => Exception: {0}", exp.Message); errorCode = ErrorCodes.InternalError; } WriteMessageLog(ChargePointStatus.Id, connectorId, msgIn.Action, source, 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.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 HandleMeterValues(OCPPMessage msgIn, OCPPMessage msgOut) { string errorCode = null; MeterValuesResponse meterValuesResponse = new MeterValuesResponse(); meterValuesResponse.CustomData = new CustomDataType(); meterValuesResponse.CustomData.VendorId = VendorId; int? connectorId = null; string msgMeterValue = string.Empty; try { Logger.LogTrace("Processing meter values..."); MeterValuesRequest meterValueRequest = JsonConvert.DeserializeObject <MeterValuesRequest>(msgIn.JsonPayload); Logger.LogTrace("MeterValues => Message deserialized"); connectorId = meterValueRequest.EvseId; if (ChargePointStatus != null) { // Known charge station => extract meter values with correct scale double currentChargeKW = -1; double meterKWH = -1; double stateOfCharge = -1; GetMeterValues(meterValueRequest.MeterValue, out meterKWH, out currentChargeKW, out stateOfCharge); // write charging/meter data in chargepoint status ChargingData chargingData = null; if (currentChargeKW >= 0 || meterKWH >= 0 || stateOfCharge >= 0) { chargingData = new ChargingData(); if (currentChargeKW >= 0) { chargingData.ChargeRateKW = currentChargeKW; } if (meterKWH >= 0) { chargingData.MeterKWH = meterKWH; } if (stateOfCharge >= 0) { chargingData.SoC = stateOfCharge; } msgMeterValue = $"Meter (kWh): {meterKWH} | Charge (kW): {currentChargeKW} | SoC (%): {stateOfCharge}"; } if (connectorId > 1) { // second connector (odr higher!?) ChargePointStatus.ChargingDataEVSE2 = chargingData; } else { // first connector ChargePointStatus.ChargingDataEVSE1 = chargingData; } } else { // Unknown charge station errorCode = ErrorCodes.GenericError; } msgOut.JsonPayload = JsonConvert.SerializeObject(meterValuesResponse); Logger.LogTrace("MeterValues => Response serialized"); } catch (Exception exp) { Logger.LogError(exp, "MeterValues => Exception: {0}", exp.Message); errorCode = ErrorCodes.InternalError; } WriteMessageLog(ChargePointStatus.Id, connectorId, msgIn.Action, msgMeterValue, errorCode); return(errorCode); }
/// <summary> /// Processes the charge point message and returns the answer message /// </summary> public OCPPMessage ProcessRequest(OCPPMessage msgIn) { OCPPMessage msgOut = new OCPPMessage(); msgOut.MessageType = "3"; msgOut.UniqueId = msgIn.UniqueId; string errorCode = null; if (msgIn.MessageType == "2") { switch (msgIn.Action) { case "BootNotification": errorCode = HandleBootNotification(msgIn, msgOut); break; case "Heartbeat": errorCode = HandleHeartBeat(msgIn, msgOut); break; case "Authorize": errorCode = HandleAuthorize(msgIn, msgOut); break; case "TransactionEvent": errorCode = HandleTransactionEvent(msgIn, msgOut); break; case "MeterValues": errorCode = HandleMeterValues(msgIn, msgOut); break; case "StatusNotification": errorCode = HandleStatusNotification(msgIn, msgOut); break; case "DataTransfer": errorCode = HandleDataTransfer(msgIn, msgOut); break; case "LogStatusNotification": errorCode = HandleLogStatusNotification(msgIn, msgOut); break; case "FirmwareStatusNotification": errorCode = HandleFirmwareStatusNotification(msgIn, msgOut); break; case "ClearedChargingLimit": errorCode = HandleClearedChargingLimit(msgIn, msgOut); break; case "NotifyChargingLimit": errorCode = HandleNotifyChargingLimit(msgIn, msgOut); break; case "NotifyEVChargingSchedule": errorCode = HandleNotifyEVChargingSchedule(msgIn, msgOut); break; default: errorCode = ErrorCodes.NotSupported; WriteMessageLog(ChargePointStatus.Id, null, msgIn.Action, msgIn.JsonPayload, errorCode); break; } } else { Logger.LogError("ControllerOCPP20 => Protocol error: wrong message type", msgIn.MessageType); errorCode = ErrorCodes.ProtocolError; } if (!string.IsNullOrEmpty(errorCode)) { // Inavlid message type => return type "4" (CALLERROR) msgOut.MessageType = "4"; msgOut.ErrorCode = errorCode; Logger.LogDebug("ControllerOCPP20 => Return error code messge: ErrorCode={0}", errorCode); } return(msgOut); }