diff --git a/OCPP.Core.Server/ControllerOCPP16.GetLocalListVersion.cs b/OCPP.Core.Server/ControllerOCPP16.GetLocalListVersion.cs new file mode 100644 index 0000000..f98f409 --- /dev/null +++ b/OCPP.Core.Server/ControllerOCPP16.GetLocalListVersion.cs @@ -0,0 +1,36 @@ +using Microsoft.Extensions.Logging; +using Newtonsoft.Json; +using OCPP.Core.Server.Messages_OCPP16; +using System; + +namespace OCPP.Core.Server +{ + public partial class ControllerOCPP16 + { + public void HandleGetLocalListVersion(OCPPMessage msgIn, OCPPMessage msgOut) + { + Logger.LogInformation("GetLocalListVersion answer: ChargePointId={0} / MsgType={1} / ErrCode={2}", + ChargePointStatus.Id, msgIn.MessageType, msgIn.ErrorCode); + + try + { + GetLocalListVersionResponse response = JsonConvert.DeserializeObject(msgIn.JsonPayload); + Logger.LogInformation("HandleGetLocalListVersion => Answer status: {0}", response?.ListVersion); + WriteMessageLog(ChargePointStatus?.Id, null, msgOut.Action, response?.ListVersion.ToString(), msgIn.ErrorCode); + + if (msgOut.TaskCompletionSource != null) + { + // set API response as TaskCompletion result + string apiResult = "{\"listVersion\": " + JsonConvert.ToString(response?.ListVersion.ToString()) + "}"; + Logger.LogTrace("HandleGetLocalListVersion => API response: {0}", apiResult); + + msgOut.TaskCompletionSource.SetResult(apiResult); + } + } + catch (Exception exp) + { + Logger.LogError(exp, "HandleGetLocalListVersion => Exception: {0}", exp.Message); + } + } + } +} diff --git a/OCPP.Core.Server/ControllerOCPP16.SendLocalList.cs b/OCPP.Core.Server/ControllerOCPP16.SendLocalList.cs new file mode 100644 index 0000000..762eaf3 --- /dev/null +++ b/OCPP.Core.Server/ControllerOCPP16.SendLocalList.cs @@ -0,0 +1,36 @@ +using Microsoft.Extensions.Logging; +using Newtonsoft.Json; +using OCPP.Core.Server.Messages_OCPP16; +using System; + +namespace OCPP.Core.Server +{ + public partial class ControllerOCPP16 + { + public void HandleSendLocalList(OCPPMessage msgIn, OCPPMessage msgOut) + { + Logger.LogInformation("SendLocalList answer: ChargePointId={0} / MsgType={1} / ErrCode={2}", + ChargePointStatus.Id, msgIn.MessageType, msgIn.ErrorCode); + + try + { + SendLocalListResponse response = JsonConvert.DeserializeObject(msgIn.JsonPayload); + Logger.LogInformation("HandleSendLocalList => Answer status: {0}", response?.Status); + WriteMessageLog(ChargePointStatus?.Id, null, msgOut.Action, response?.Status.ToString(), msgIn.ErrorCode); + + if (msgOut.TaskCompletionSource != null) + { + // set API response as TaskCompletion result + string apiResult = "{\"status\": " + JsonConvert.ToString(response?.Status.ToString()) + "}"; + Logger.LogTrace("HandleSendLocalList => API response: {0}", apiResult); + + msgOut.TaskCompletionSource.SetResult(apiResult); + } + } + catch (Exception exp) + { + Logger.LogError(exp, "HandleSendLocalList => Exception: {0}", exp.Message); + } + } + } +} diff --git a/OCPP.Core.Server/ControllerOCPP16.cs b/OCPP.Core.Server/ControllerOCPP16.cs index af7286b..5b62c23 100644 --- a/OCPP.Core.Server/ControllerOCPP16.cs +++ b/OCPP.Core.Server/ControllerOCPP16.cs @@ -118,6 +118,14 @@ public void ProcessAnswer(OCPPMessage msgIn, OCPPMessage msgOut) HandleUnlockConnector(msgIn, msgOut); break; + case "GetLocalListVersion": + HandleGetLocalListVersion(msgIn, msgOut); + break; + + case "SendLocalList": + HandleSendLocalList(msgIn, msgOut); + break; + default: WriteMessageLog(ChargePointStatus.Id, null, msgIn.Action, msgIn.JsonPayload, "Unknown answer"); break; diff --git a/OCPP.Core.Server/ControllerOCPP20.GetLocalListVersion.cs b/OCPP.Core.Server/ControllerOCPP20.GetLocalListVersion.cs new file mode 100644 index 0000000..7929a83 --- /dev/null +++ b/OCPP.Core.Server/ControllerOCPP20.GetLocalListVersion.cs @@ -0,0 +1,36 @@ +using Microsoft.Extensions.Logging; +using Newtonsoft.Json; +using OCPP.Core.Server.Messages_OCPP20; +using System; + +namespace OCPP.Core.Server +{ + public partial class ControllerOCPP20 + { + public void HandleGetLocalListVersion(OCPPMessage msgIn, OCPPMessage msgOut) + { + Logger.LogInformation("GetLocalListVersion answer: ChargePointId={0} / MsgType={1} / ErrCode={2}", + ChargePointStatus.Id, msgIn.MessageType, msgIn.ErrorCode); + + try + { + GetLocalListVersionResponse response = JsonConvert.DeserializeObject(msgIn.JsonPayload); + Logger.LogInformation("HandleGetLocalListVersion => Answer status: {0}", response?.VersionNumber); + WriteMessageLog(ChargePointStatus?.Id, null, msgOut.Action, response?.VersionNumber.ToString(), msgIn.ErrorCode); + + if (msgOut.TaskCompletionSource != null) + { + // set API response as TaskCompletion result + string apiResult = "{\"versionNumber\": " + JsonConvert.ToString(response?.VersionNumber.ToString()) + "}"; + Logger.LogTrace("HandleGetLocalListVersion => API response: {0}", apiResult); + + msgOut.TaskCompletionSource.SetResult(apiResult); + } + } + catch (Exception exp) + { + Logger.LogError(exp, "HandleGetLocalListVersion => Exception: {0}", exp.Message); + } + } + } +} diff --git a/OCPP.Core.Server/ControllerOCPP20.SendLocalList.cs b/OCPP.Core.Server/ControllerOCPP20.SendLocalList.cs new file mode 100644 index 0000000..68b5d59 --- /dev/null +++ b/OCPP.Core.Server/ControllerOCPP20.SendLocalList.cs @@ -0,0 +1,36 @@ +using Microsoft.Extensions.Logging; +using Newtonsoft.Json; +using OCPP.Core.Server.Messages_OCPP20; +using System; + +namespace OCPP.Core.Server +{ + public partial class ControllerOCPP20 + { + public void HandleSendLocalList(OCPPMessage msgIn, OCPPMessage msgOut) + { + Logger.LogInformation("SendLocalList answer: ChargePointId={0} / MsgType={1} / ErrCode={2}", + ChargePointStatus.Id, msgIn.MessageType, msgIn.ErrorCode); + + try + { + SendLocalListResponse response = JsonConvert.DeserializeObject(msgIn.JsonPayload); + Logger.LogInformation("HandleSendLocalList => Answer status: {0}", response?.Status); + WriteMessageLog(ChargePointStatus?.Id, null, msgOut.Action, response?.Status.ToString(), msgIn.ErrorCode); + + if (msgOut.TaskCompletionSource != null) + { + // set API response as TaskCompletion result + string apiResult = "{\"status\": " + JsonConvert.ToString(response?.Status.ToString()) + "}"; + Logger.LogTrace("HandleSendLocalList => API response: {0}", apiResult); + + msgOut.TaskCompletionSource.SetResult(apiResult); + } + } + catch (Exception exp) + { + Logger.LogError(exp, "HandleSendLocalList => Exception: {0}", exp.Message); + } + } + } +} diff --git a/OCPP.Core.Server/ControllerOCPP20.cs b/OCPP.Core.Server/ControllerOCPP20.cs index c0ea712..e7834a7 100644 --- a/OCPP.Core.Server/ControllerOCPP20.cs +++ b/OCPP.Core.Server/ControllerOCPP20.cs @@ -138,6 +138,14 @@ public void ProcessAnswer(OCPPMessage msgIn, OCPPMessage msgOut) HandleReset(msgIn, msgOut); break; + case "GetLocalListVersion": + HandleGetLocalListVersion(msgIn, msgOut); + break; + + case "SendLocalList": + HandleSendLocalList(msgIn, msgOut); + break; + case "UnlockConnector": HandleUnlockConnector(msgIn, msgOut); break; diff --git a/OCPP.Core.Server/Messages_OCPP16/GetLocalListVersionRequest.cs b/OCPP.Core.Server/Messages_OCPP16/GetLocalListVersionRequest.cs new file mode 100644 index 0000000..0aa255f --- /dev/null +++ b/OCPP.Core.Server/Messages_OCPP16/GetLocalListVersionRequest.cs @@ -0,0 +1,11 @@ +using System.Drawing; + +namespace OCPP.Core.Server.Messages_OCPP16 +{ + /// + /// This contains the field definition of the PDU + /// sent by the Central System to the Charge Point. + /// + public class GetLocalListVersionRequest + { } +} diff --git a/OCPP.Core.Server/Messages_OCPP16/GetLocalListVersionResponse.cs b/OCPP.Core.Server/Messages_OCPP16/GetLocalListVersionResponse.cs new file mode 100644 index 0000000..2d54cdb --- /dev/null +++ b/OCPP.Core.Server/Messages_OCPP16/GetLocalListVersionResponse.cs @@ -0,0 +1,19 @@ +namespace OCPP.Core.Server.Messages_OCPP16 +{ + /// + /// This contains the field definition of the PDU + /// sent by the Charge Point to Central System + /// in response to a + /// (i.e. ) PDU. + /// + public class GetLocalListVersionResponse + { + /// + /// Required.
+ /// This contains the current version number of the local authorization list in the Charge Point. + ///
+ [Newtonsoft.Json.JsonProperty("listVersion", Required = Newtonsoft.Json.Required.Always)] + [System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)] + public System.Int32 ListVersion { get; set; } + } +} diff --git a/OCPP.Core.Server/Messages_OCPP16/SendLocalListRequest.cs b/OCPP.Core.Server/Messages_OCPP16/SendLocalListRequest.cs new file mode 100644 index 0000000..40d3f96 --- /dev/null +++ b/OCPP.Core.Server/Messages_OCPP16/SendLocalListRequest.cs @@ -0,0 +1,86 @@ +using Newtonsoft.Json; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Runtime.Serialization; + +namespace OCPP.Core.Server.Messages_OCPP16 +{ + #region SendLocalListRequest Specific Members + /// + /// Type of update for a (i.e. ). + /// + public enum UpdateType + { + /// + /// Indicates that the current Local Authorization List must be updated with the values in this message. + /// + [EnumMember(Value = @"Differential")] + Differential, + + /// + /// Indicates that the current Local Authorization List must be replaced by the values in this message. + /// + [EnumMember(Value = @"Full")] + Full + } + + /// + /// Elements that constitute an entry of a Local Authorization List update. + /// + public class AuthorizationData + { + /// + /// Required.
+ /// The identifier to which this authorization applies. + ///
+ [JsonProperty("idTag", Required = Required.DisallowNull, NullValueHandling = NullValueHandling.Ignore)] + [StringLength(20)] + public string IdTag { get; set; } + + [JsonProperty("idTagInfo", Required = Required.Default)] + public IdTagInfo IdTagInfo { get; set; } + } + #endregion + + /// + /// This contains the field definition of the PDU + /// sent by the Central System to the Charge Point. + /// + /// + /// If no (empty) is given + /// and the is Full, all identifications are removed from the list. + /// Requesting a Differential update without (empty) will have no effect on the list. + /// All idTags in the MUST be unique, no duplicate values are + /// allowed. + /// + public class SendLocalListRequest + { + /// + /// Required.
+ /// In case of a full update this is the version number of the full list. + /// In case of a differential update it is the version number of the list after the update has been applied. + ///
+ [JsonProperty("listVersion", Required = Required.Always)] + [Required(AllowEmptyStrings = true)] + public int ListVersion { get; set; } + + /// + /// Optional.
+ /// In case of a full update this contains the list of values that form the new local authorization list. + /// In case of a differential update it contains the changes to be applied to the local authorization list in the Charge Point. + /// Maximum number of AuthorizationData elements is available in the configuration key: SendLocalListMaxLength. + ///
+ [JsonProperty("localAuthorizationList", Required = Required.Always)] + [Required(AllowEmptyStrings = true)] + public List LocalAuthorizationList { get; set; } + + /// + /// Required.
+ /// This contains the type of update (full or differential) of this request. + ///
+ [JsonProperty("updateType", Required = Required.Always)] + [Required(AllowEmptyStrings = true)] + [JsonConverter(typeof(Newtonsoft.Json.Converters.StringEnumConverter))] + public UpdateType Type { get; set; } + } +} diff --git a/OCPP.Core.Server/Messages_OCPP16/SendLocalListResponse.cs b/OCPP.Core.Server/Messages_OCPP16/SendLocalListResponse.cs new file mode 100644 index 0000000..ff1c506 --- /dev/null +++ b/OCPP.Core.Server/Messages_OCPP16/SendLocalListResponse.cs @@ -0,0 +1,55 @@ +using Newtonsoft.Json; +using System.ComponentModel.DataAnnotations; +using System.Runtime.Serialization; + +namespace OCPP.Core.Server.Messages_OCPP16 +{ + #region SendLocalListResponse Specific Members + /// + /// Type of update for a + /// (i.e. ). + /// + public enum UpdateStatus + { + /// + /// Local Authorization List successfully updated. + /// + [EnumMember(Value = @"Accepted")] + Accepted, + + /// + /// Failed to update the Local Authorization List. + /// + [EnumMember(Value = @"Failed")] + Failed, + + /// + /// Update of Local Authorization List is not supported by Charge Point. + /// + [EnumMember(Value = @"NotSupported")] + NotSupported, + + /// + /// Version number in the request for a differential update is less or equal then version number of current list + /// + [EnumMember(Value = @"VersionMismatch")] + VersionMismatch + } + #endregion + + /// + /// This contains the field definition of the PDU + /// sent by the Charge Point to the Central System + /// in response to a PDU (i.e. ). + /// + public class SendLocalListResponse + { + /// + /// Required.
+ /// This indicates whether the Charge Point has successfully received and applied the update of the local authorization list. + ///
+ [JsonProperty("status", Required = Required.Always)] + [Required(AllowEmptyStrings = true)] + public UpdateStatus Status { get; set; } + } +} diff --git a/OCPP.Core.Server/Messages_OCPP20/GetLocalListVersionRequest.cs b/OCPP.Core.Server/Messages_OCPP20/GetLocalListVersionRequest.cs new file mode 100644 index 0000000..7969e55 --- /dev/null +++ b/OCPP.Core.Server/Messages_OCPP20/GetLocalListVersionRequest.cs @@ -0,0 +1,9 @@ +namespace OCPP.Core.Server.Messages_OCPP20 +{ + /// + /// This contains the field definition of the GetLocalListVersionRequest PDU sent by the CSMS to the Charging Station. + /// No fields are defined. + /// + public class GetLocalListVersionRequest + { } +} diff --git a/OCPP.Core.Server/Messages_OCPP20/GetLocalListVersionResponse.cs b/OCPP.Core.Server/Messages_OCPP20/GetLocalListVersionResponse.cs new file mode 100644 index 0000000..6350327 --- /dev/null +++ b/OCPP.Core.Server/Messages_OCPP20/GetLocalListVersionResponse.cs @@ -0,0 +1,19 @@ +using Newtonsoft.Json; +using System.ComponentModel.DataAnnotations; + +namespace OCPP.Core.Server.Messages_OCPP20 +{ + /// + /// This contains the field definition of the GetLocalListVersionResponse PDU sent by the Charging Station to CSMS + /// in response to a . + /// + public class GetLocalListVersionResponse + { + /// + /// Required. This contains the current version number of the local authorization list in the Charging Station. + /// + [JsonProperty("versionNumber", Required = Required.Always)] + [Required(AllowEmptyStrings = false)] + public int VersionNumber { get; set; } + } +} diff --git a/OCPP.Core.Server/Messages_OCPP20/SendLocalListRequest.cs b/OCPP.Core.Server/Messages_OCPP20/SendLocalListRequest.cs new file mode 100644 index 0000000..c9705ea --- /dev/null +++ b/OCPP.Core.Server/Messages_OCPP20/SendLocalListRequest.cs @@ -0,0 +1,60 @@ +using Newtonsoft.Json; +using OCPP.Core.Server.Messages_OCPP16; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Runtime.Serialization; + +namespace OCPP.Core.Server.Messages_OCPP20 +{ + #region SendLocalListRequest Specific Members + public enum UpdateEnumType + { + /// + /// Indicates that the current Local Authorization List must be updated with the values in this message. + /// + [EnumMember(Value = @"Differential")] + Differential, + + /// + /// Indicates that the current Local Authorization List must be replaced by the values in this message. + /// + [EnumMember(Value = @"Full")] + Full + } + #endregion + + /// + /// This contains the field definition of the SendLocalListRequest PDU sent by the CSMS to the Charging Station. + /// + /// + /// If no (empty) localAuthorizationList is given and the updateType is Full, all IdTokens are removed from the list. + /// Requesting a Differential update without or with empty localAuthorizationList will have no effect on the list. + /// All IdTokens in the localAuthorizationList MUST be unique, no duplicate values are allowed. + /// + public class SendLocalListRequest + { + /// + /// Required.
+ /// In case of a full update this is the version number of the full list. + /// In case of a differential update it is the version number of the list after the update has been applied. + ///
+ [JsonProperty("versionNumber", Required = Required.Always)] + [Required(AllowEmptyStrings = false)] + public int VersionNumber { get; set; } + + /// + /// Required.
+ /// This contains the type of update (full or differential) of this request. + ///
+ [JsonProperty("updateType", Required = Required.Always)] + [Required(AllowEmptyStrings = false)] + public UpdateEnumType UpdateType { get; set; } + + /// + /// Optional.
+ /// This contains the Local Authorization List entries. + ///
+ [JsonProperty("localAuthorizationList", Required = Required.Default)] + public List LocalAuthorizationList { get; set; } + } +} diff --git a/OCPP.Core.Server/Messages_OCPP20/SendLocalListResponse.cs b/OCPP.Core.Server/Messages_OCPP20/SendLocalListResponse.cs new file mode 100644 index 0000000..0d8786d --- /dev/null +++ b/OCPP.Core.Server/Messages_OCPP20/SendLocalListResponse.cs @@ -0,0 +1,50 @@ +using Newtonsoft.Json; +using System.Runtime.Serialization; +using System.ComponentModel.DataAnnotations; + +namespace OCPP.Core.Server.Messages_OCPP20 +{ + #region SendLocalListResponse Specific Members + public enum SendLocalListStatusEnumType + { + /// + /// Local Authorization List successfully updated. + /// + [EnumMember(Value = @"Accepted")] + Accepted, + + /// + /// Failed to update the Local Authorization List. + /// + [EnumMember(Value = @"Failed")] + Failed, + + /// + /// Version number in the request for a differential update is less or equal then version number of current list. + /// + [EnumMember(Value = @"VersionMismatch")] + VersionMismatch + } + #endregion + + /// + /// This contains the field definition of the SendLocalListResponse PDU sent by the Charging Station to the CSMS + /// in response to PDU. + /// + public class SendLocalListResponse + { + /// + /// Required. This indicates whether the Charging Station has successfully received + /// and applied the update of the Local Authorization List. + /// + [JsonProperty("status", Required = Required.Always)] + [Required(AllowEmptyStrings = false)] + public SendLocalListStatusEnumType Status { get; set; } + + /// + /// Optional. Detailed status information. + /// + [JsonProperty("statusInfo", Required = Required.Default)] + public StatusInfoType StatusInfo { get; set; } + } +} diff --git a/OCPP.Core.Server/Models/SendLocalListModel.cs b/OCPP.Core.Server/Models/SendLocalListModel.cs new file mode 100644 index 0000000..c9ae248 --- /dev/null +++ b/OCPP.Core.Server/Models/SendLocalListModel.cs @@ -0,0 +1,17 @@ +using Newtonsoft.Json; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; + +namespace OCPP.Core.Server.Models +{ + public class SendLocalListModel + { + [JsonProperty("listVersion", Required = Required.Always)] + [Required(AllowEmptyStrings = false)] + public int ListVersion { get; set; } + + [JsonProperty("tags", Required = Required.Always)] + [Required(AllowEmptyStrings = false)] + public List Tags { get; set; } + } +} diff --git a/OCPP.Core.Server/Models/TagUpdateModel.cs b/OCPP.Core.Server/Models/TagUpdateModel.cs new file mode 100644 index 0000000..8f29c78 --- /dev/null +++ b/OCPP.Core.Server/Models/TagUpdateModel.cs @@ -0,0 +1,16 @@ +using Newtonsoft.Json; +using System; +using System.ComponentModel.DataAnnotations; + +namespace OCPP.Core.Server.Models +{ + public class TagUpdateModel + { + [JsonProperty("tagId", Required = Required.Always)] + [Required(AllowEmptyStrings = false)] + public string TagId { get; set; } + + [JsonProperty("expiryDate", Required = Required.Default)] + public DateTimeOffset ExpiryDate { get; set; } = DateTimeOffset.MaxValue; + } +} diff --git a/OCPP.Core.Server/OCPPMiddleware.OCPP16.cs b/OCPP.Core.Server/OCPPMiddleware.OCPP16.cs index 697f2ce..67461de 100644 --- a/OCPP.Core.Server/OCPPMiddleware.OCPP16.cs +++ b/OCPP.Core.Server/OCPPMiddleware.OCPP16.cs @@ -2,6 +2,7 @@ using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Logging; using Newtonsoft.Json; +using OCPP.Core.Server.Models; using System; using System.Collections.Generic; using System.IO; @@ -147,6 +148,100 @@ private async Task Reset16(ChargePointStatus chargePointStatus, HttpContext apiC await apiCallerContext.Response.WriteAsync(apiResult); } + /// + /// Sends a GetLocalListVersion request to the chargepoint. + /// + private async Task GetLocalListVersion16(ChargePointStatus chargePointStatus, HttpContext apiCallerContext) + { + ILogger logger = _logFactory.CreateLogger("OCPPMiddleware.OCPP16"); + ControllerOCPP16 controller16 = new ControllerOCPP16(_configuration, _logFactory, chargePointStatus); + + Messages_OCPP16.GetLocalListVersionRequest request = new Messages_OCPP16.GetLocalListVersionRequest(); + string jsonRequest = JsonConvert.SerializeObject(request); + + OCPPMessage msgOut = new OCPPMessage(); + msgOut.MessageType = "2"; + msgOut.Action = "GetLocalListVersion"; + msgOut.UniqueId = Guid.NewGuid().ToString("N"); + msgOut.JsonPayload = jsonRequest; + msgOut.TaskCompletionSource = new TaskCompletionSource(); + + // store HttpContext with MsgId for later answer processing (=> send answer 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; + + logger.LogInformation("GetLocalListVersion returned: " + apiResult); + + apiCallerContext.Response.StatusCode = 200; + apiCallerContext.Response.ContentType = "application/json"; + await apiCallerContext.Response.WriteAsync(apiResult); + } + + private async Task SendLocalList16(ChargePointStatus chargePointStatus, HttpContext apiCallerContext) + { + ILogger logger = _logFactory.CreateLogger("OCPPMiddleware.OCPP16"); + ControllerOCPP16 controller16 = new ControllerOCPP16(_configuration, _logFactory, chargePointStatus); + + // read values from request body + apiCallerContext.Request.EnableBuffering(); + apiCallerContext.Request.Body.Seek(0, SeekOrigin.Begin); + + SendLocalListModel inputModel = new SendLocalListModel(); + using (StreamReader stream = new StreamReader(apiCallerContext.Request.Body)) + { + string body = await stream.ReadToEndAsync(); + inputModel = JsonConvert.DeserializeObject(body); + } + + // prepare request + Messages_OCPP16.SendLocalListRequest request = new Messages_OCPP16.SendLocalListRequest(); + request.ListVersion = inputModel.ListVersion; + request.Type = Messages_OCPP16.UpdateType.Full; + + request.LocalAuthorizationList = new List(); + foreach (var tag in inputModel.Tags) + { + request.LocalAuthorizationList.Add(new Messages_OCPP16.AuthorizationData + { + IdTag = tag.TagId, + IdTagInfo = new Messages_OCPP16.IdTagInfo + { + Status = Messages_OCPP16.IdTagInfoStatus.Accepted, + ExpiryDate = tag.ExpiryDate + } + }); + } + + string jsonRequest = JsonConvert.SerializeObject(request); + + OCPPMessage msgOut = new OCPPMessage(); + msgOut.MessageType = "2"; + msgOut.Action = "SendLocalList"; + msgOut.UniqueId = Guid.NewGuid().ToString("N"); + msgOut.JsonPayload = jsonRequest; + msgOut.TaskCompletionSource = new TaskCompletionSource(); + + // store HttpContext with MsgId for later answer processing (=> send answer 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; + + logger.LogInformation("SendLocalList returned: " + apiResult); + + apiCallerContext.Response.StatusCode = 200; + apiCallerContext.Response.ContentType = "application/json"; + await apiCallerContext.Response.WriteAsync(apiResult); + } + /// /// Sends a Unlock-Request to the chargepoint /// diff --git a/OCPP.Core.Server/OCPPMiddleware.OCPP20.cs b/OCPP.Core.Server/OCPPMiddleware.OCPP20.cs index 2ff1822..044547c 100644 --- a/OCPP.Core.Server/OCPPMiddleware.OCPP20.cs +++ b/OCPP.Core.Server/OCPPMiddleware.OCPP20.cs @@ -12,6 +12,7 @@ using System.Threading; using System.Threading.Tasks; using OCPP.Core.Server.Messages_OCPP20; +using OCPP.Core.Server.Models; namespace OCPP.Core.Server { @@ -151,6 +152,99 @@ private async Task Reset20(ChargePointStatus chargePointStatus, HttpContext apiC await apiCallerContext.Response.WriteAsync(apiResult); } + /// + /// Sends a GetLocalListVersion request to the chargepoint. + /// + private async Task GetLocalListVersion20(ChargePointStatus chargePointStatus, HttpContext apiCallerContext) + { + ILogger logger = _logFactory.CreateLogger("OCPPMiddleware.OCPP20"); + ControllerOCPP20 controller20 = new ControllerOCPP20(_configuration, _logFactory, chargePointStatus); + + Messages_OCPP20.GetLocalListVersionRequest request = new GetLocalListVersionRequest(); + string jsonRequest = JsonConvert.SerializeObject(request); + + OCPPMessage msgOut = new OCPPMessage(); + msgOut.MessageType = "2"; + msgOut.Action = "GetLocalListVersion"; + msgOut.UniqueId = Guid.NewGuid().ToString("N"); + msgOut.JsonPayload = jsonRequest; + msgOut.TaskCompletionSource = new TaskCompletionSource(); + + // 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 SendOcpp20Message(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); + } + + private async Task SendLocalList20(ChargePointStatus chargePointStatus, HttpContext apiCallerContext) + { + ILogger logger = _logFactory.CreateLogger("OCPPMiddleware.OCPP20"); + ControllerOCPP20 controller20 = new ControllerOCPP20(_configuration, _logFactory, chargePointStatus); + + // read values from request body + apiCallerContext.Request.EnableBuffering(); + apiCallerContext.Request.Body.Seek(0, SeekOrigin.Begin); + + SendLocalListModel inputModel = new SendLocalListModel(); + using (StreamReader stream = new StreamReader(apiCallerContext.Request.Body)) + { + string body = await stream.ReadToEndAsync(); + inputModel = JsonConvert.DeserializeObject(body); + } + + // prepare request + Messages_OCPP20.SendLocalListRequest request = new Messages_OCPP20.SendLocalListRequest(); + request.VersionNumber = inputModel.ListVersion; + request.UpdateType = UpdateEnumType.Full; + + request.LocalAuthorizationList = new List(); + foreach (var tag in inputModel.Tags) + { + request.LocalAuthorizationList.Add(new Messages_OCPP16.AuthorizationData + { + IdTag = tag.TagId, + IdTagInfo = new Messages_OCPP16.IdTagInfo + { + Status = Messages_OCPP16.IdTagInfoStatus.Accepted, + ExpiryDate = tag.ExpiryDate + } + }); + } + + string jsonRequest = JsonConvert.SerializeObject(request); + + OCPPMessage msgOut = new OCPPMessage(); + msgOut.MessageType = "2"; + msgOut.Action = "SendLocalList"; + msgOut.UniqueId = Guid.NewGuid().ToString("N"); + msgOut.JsonPayload = jsonRequest; + msgOut.TaskCompletionSource = new TaskCompletionSource(); + + // store HttpContext with MsgId for later answer processing (=> send answer to API caller) + _requestQueue.Add(msgOut.UniqueId, msgOut); + + // send OCPP message with optional logging/dump + await SendOcpp20Message(msgOut, logger, chargePointStatus.WebSocket); + + // wait for asynchronous chargepoint response and processing + string apiResult = await msgOut.TaskCompletionSource.Task; + + logger.LogInformation("SendLocalList returned: " + apiResult); + + apiCallerContext.Response.StatusCode = 200; + apiCallerContext.Response.ContentType = "application/json"; + await apiCallerContext.Response.WriteAsync(apiResult); + } + /// /// Sends a Unlock-Request to the chargepoint /// diff --git a/OCPP.Core.Server/OCPPMiddleware.cs b/OCPP.Core.Server/OCPPMiddleware.cs index ee1816b..fe15ac9 100644 --- a/OCPP.Core.Server/OCPPMiddleware.cs +++ b/OCPP.Core.Server/OCPPMiddleware.cs @@ -341,6 +341,86 @@ public async Task Invoke(HttpContext context) context.Response.StatusCode = (int)HttpStatusCode.BadRequest; } } + else if (cmd == "GetLocalListVersion") + { + if (!string.IsNullOrEmpty(urlChargePointId)) + { + try + { + ChargePointStatus status = null; + if (_chargePointStatusDict.TryGetValue(urlChargePointId, out status)) + { + // send message to chargepoint + if (status.Protocol == Protocol_OCPP20) + { + // OCPP 2.0 + await GetLocalListVersion20(status, context); + } + else + { + // OCPP 1.6 + await GetLocalListVersion16(status, context); + } + } + else + { + // chargepoint offline + _logger.LogError("OCPPMiddleware GetLocalListVersion => ChargePoint offline: {0}", urlChargePointId); + context.Response.StatusCode = (int)HttpStatusCode.NotFound; + } + } + catch (Exception exp) + { + _logger.LogError("OCPPMiddleware GetLocalListVersion => Error: {0}", exp.Message); + context.Response.StatusCode = (int)HttpStatusCode.InternalServerError; + } + } + else + { + _logger.LogError("OCPPMiddleware GetLocalListVersion => Missing chargepoint ID."); + context.Response.StatusCode = (int)HttpStatusCode.BadRequest; + } + } + else if (cmd == "SendLocalList") + { + if (!string.IsNullOrEmpty(urlChargePointId)) + { + try + { + ChargePointStatus status = null; + if (_chargePointStatusDict.TryGetValue(urlChargePointId, out status)) + { + // send message to chargepoint + if (status.Protocol == Protocol_OCPP20) + { + // OCPP 2.0 + await SendLocalList20(status, context); + } + else + { + // OCPP 1.6 + await SendLocalList16(status, context); + } + } + else + { + // chargepoint offline + _logger.LogError("OCPPMiddleware SendLocalList => ChargePoint offline: {0}", urlChargePointId); + context.Response.StatusCode = (int)HttpStatusCode.NotFound; + } + } + catch (Exception exp) + { + _logger.LogError(exp, "OCPPMiddleware SendLocalList => Error: {0}", exp.Message); + context.Response.StatusCode = (int)HttpStatusCode.InternalServerError; + } + } + else + { + _logger.LogError("OCPPMiddleware SendLocalList => Missing chargepoint ID"); + context.Response.StatusCode = (int)HttpStatusCode.BadRequest; + } + } else if (cmd == "UnlockConnector") { if (!string.IsNullOrEmpty(urlChargePointId))