From 3903df4ffb4175621b8031327986e744fe47e61d Mon Sep 17 00:00:00 2001 From: Achim Friedland Date: Tue, 29 Oct 2024 13:20:42 +0100 Subject: [PATCH] Added CLI test command 'reset' for OCPP v1.6/v2.1 --- .../PredefinedStrings/ResetType.cs | 12 +- .../CLI/CLICommands/DefaultStrings.cs | 28 ++++ .../CLI/CLICommands/ResetCommand.cs | 147 ++++++++++++++++++ .../CLI/ICentralSystemCLI.cs | 100 ++++++++++++ .../PredefinedStrings/ResetType.cs | 12 +- .../CLI/CLICommands/DefaultStrings.cs | 29 ++++ .../CLI/CLICommands/ResetCommand.cs | 145 +++++++++++++++++ WWCP_OCPPv2.1_CSMS/CLI/ICSMSCLI.cs | 100 ++++++++++++ 8 files changed, 567 insertions(+), 6 deletions(-) create mode 100644 WWCP_OCPPv1.6_CentralSystem/CLI/CLICommands/DefaultStrings.cs create mode 100644 WWCP_OCPPv1.6_CentralSystem/CLI/CLICommands/ResetCommand.cs create mode 100644 WWCP_OCPPv1.6_CentralSystem/CLI/ICentralSystemCLI.cs create mode 100644 WWCP_OCPPv2.1_CSMS/CLI/CLICommands/DefaultStrings.cs create mode 100644 WWCP_OCPPv2.1_CSMS/CLI/CLICommands/ResetCommand.cs create mode 100644 WWCP_OCPPv2.1_CSMS/CLI/ICSMSCLI.cs diff --git a/WWCP_OCPPv1.6/DataStructures/PredefinedStrings/ResetType.cs b/WWCP_OCPPv1.6/DataStructures/PredefinedStrings/ResetType.cs index 19eabea52..cd25cc8ae 100644 --- a/WWCP_OCPPv1.6/DataStructures/PredefinedStrings/ResetType.cs +++ b/WWCP_OCPPv1.6/DataStructures/PredefinedStrings/ResetType.cs @@ -67,21 +67,27 @@ public static Boolean IsNotNullOrEmpty(this ResetType? ResetType) /// /// Indicates whether this reset type is null or empty. /// - public readonly Boolean IsNullOrEmpty + public readonly Boolean IsNullOrEmpty => InternalId.IsNullOrEmpty(); /// /// Indicates whether this reset type is NOT null or empty. /// - public readonly Boolean IsNotNullOrEmpty + public readonly Boolean IsNotNullOrEmpty => InternalId.IsNotNullOrEmpty(); /// /// The length of the reset type. /// - public readonly UInt64 Length + public readonly UInt64 Length => (UInt64) (InternalId?.Length ?? 0); + /// + /// All registered reset types. + /// + public static IEnumerable All + => lookup.Values; + #endregion #region Constructor(s) diff --git a/WWCP_OCPPv1.6_CentralSystem/CLI/CLICommands/DefaultStrings.cs b/WWCP_OCPPv1.6_CentralSystem/CLI/CLICommands/DefaultStrings.cs new file mode 100644 index 000000000..1209b9874 --- /dev/null +++ b/WWCP_OCPPv1.6_CentralSystem/CLI/CLICommands/DefaultStrings.cs @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2014-2024 GraphDefined GmbH + * This file is part of WWCP OCPP + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace cloud.charging.open.protocols.OCPPv1_6.CentralSystem.CommandLine +{ + + public static class DefaultStrings + { + + public const String OCPPv1_6 = "OCPPv1.6"; + + } + +} diff --git a/WWCP_OCPPv1.6_CentralSystem/CLI/CLICommands/ResetCommand.cs b/WWCP_OCPPv1.6_CentralSystem/CLI/CLICommands/ResetCommand.cs new file mode 100644 index 000000000..68e3934fe --- /dev/null +++ b/WWCP_OCPPv1.6_CentralSystem/CLI/CLICommands/ResetCommand.cs @@ -0,0 +1,147 @@ +/* + * Copyright (c) 2014-2024 GraphDefined GmbH + * This file is part of WWCP OCPP + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#region Usings + +using org.GraphDefined.Vanaheimr.CLI; +using org.GraphDefined.Vanaheimr.Illias; + +using cloud.charging.open.protocols.OCPPv1_6.CS; + +#endregion + +namespace cloud.charging.open.protocols.OCPPv1_6.CentralSystem.CommandLine +{ + + /// + /// Reset the current networking node + /// + /// The command line interface + //[CLIContext([ DefaultStrings.OCPPv1_6 ])] + public class ResetCommand(ICentralSystemCLI CLI) : ACLICommand(CLI), + ICLICommand + { + + #region Data + + public static readonly String CommandName = nameof(ResetCommand)[..^7].ToLowerFirstChar(); + + #endregion + + #region Suggest(Arguments) + + public override IEnumerable Suggest(String[] Arguments) + { + + // No suggestions without a defined RemoteSystemId and matching OCPP version! + if (!cli.RemoteSystemIdIsSet() || + cli.GetRemoteSystemOCPPVersion() != DefaultStrings.OCPPv1_6) + { + return []; + } + + + if (Arguments.Length > 2 && + CommandName.Equals(Arguments[0], StringComparison.OrdinalIgnoreCase)) + { + Arguments = Arguments.Take(2).ToArray(); + } + + + if (Arguments.Length == 2 && + CommandName.Equals(Arguments[0], StringComparison.OrdinalIgnoreCase)) + { + + foreach (var resetType in ResetType.All) + { + + if (resetType.ToString().Equals (Arguments[1], StringComparison.OrdinalIgnoreCase)) + return [ SuggestionResponse.ParameterCompleted($"{Arguments[0]} {resetType.ToString().ToLower()}") ]; + + if (resetType.ToString().StartsWith(Arguments[1], StringComparison.OrdinalIgnoreCase)) + return [ SuggestionResponse.ParameterPrefix ($"{Arguments[0]} {resetType.ToString().ToLower()}") ]; + + } + + return [ SuggestionResponse.CommandCompleted(CommandName) ]; + + } + + + if (Arguments.Length == 1) + { + + if (CommandName.Equals (Arguments[0], StringComparison.OrdinalIgnoreCase)) + return [ SuggestionResponse.CommandHelp(Help()) ]; + + if (CommandName.StartsWith(Arguments[0], StringComparison.OrdinalIgnoreCase)) + return [ SuggestionResponse.CommandCompleted(CommandName) ]; + + } + + return []; + + } + + #endregion + + #region Execute(Arguments, CancellationToken) + + public override async Task Execute(String[] Arguments, + CancellationToken CancellationToken) + { + + // No execution without a defined RemoteSystemId! + var sourceRoute = cli.GetRemoteSystemSourceRoute(); + if (sourceRoute is null) + return []; + + + if (Arguments.Length == 2 && + ResetType.TryParse(Arguments[1], out var resetType)) + { + + var response = await cli.OCPP.OUT.Reset( + new ResetRequest( + Destination: sourceRoute, + ResetType: resetType + ) + ); + + return [ + $"{Arguments.AggregateWith(" ")} => {response.Runtime.TotalMilliseconds} ms", + response.ToJSON().ToString(Newtonsoft.Json.Formatting.Indented) + ]; + + } + + return [ $"Usage: {CommandName} <{ResetType.All.Select(_ => _.ToString()).AggregateWith("|")}>" ]; + + } + + #endregion + + #region Help() + + public override String Help() + => $"{CommandName} <{ResetType.All.Select(_ => _.ToString()).AggregateWith("|")}> - Reset the current networking node"; + + #endregion + + } + +} diff --git a/WWCP_OCPPv1.6_CentralSystem/CLI/ICentralSystemCLI.cs b/WWCP_OCPPv1.6_CentralSystem/CLI/ICentralSystemCLI.cs new file mode 100644 index 000000000..cd2728086 --- /dev/null +++ b/WWCP_OCPPv1.6_CentralSystem/CLI/ICentralSystemCLI.cs @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2014-2024 GraphDefined GmbH + * This file is part of WWCP OCPP + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#region Usings + +using org.GraphDefined.Vanaheimr.CLI; + +using cloud.charging.open.protocols.WWCP.NetworkingNode; +using cloud.charging.open.protocols.OCPPv1_6.NetworkingNode; + +#endregion + +namespace cloud.charging.open.protocols.OCPPv1_6.CentralSystem.CommandLine +{ + + public static class ICentralSystemCLIExtensions + { + + public static Boolean RemoteSystemIdIsSet(this ICentralSystemCLI CLI) + { + + if (CLI.Environment.TryGetValue(EnvironmentKey.RemoteSystemId, out var values) && + values.Count > 0) + { + return true; + } + + return false; + + } + + public static NetworkingNode_Id? GetRemoteSystemId(this ICentralSystemCLI CLI) + { + + if (CLI.Environment.TryGetValue(EnvironmentKey.RemoteSystemId, out var values) && + values.Count > 0 && + NetworkingNode_Id.TryParse(values.First(), out var remoteSystemId)) + { + return remoteSystemId; + } + + return null; + + } + + public static SourceRouting? GetRemoteSystemSourceRoute(this ICentralSystemCLI CLI) + { + + if (CLI.Environment.TryGetValue(EnvironmentKey.RemoteSystemId, out var values) && + values.Count > 0 && + NetworkingNode_Id.TryParse(values.First(), out var remoteSystemId)) + { + return SourceRouting.To(remoteSystemId); + } + + return null; + + } + + public static String? GetRemoteSystemOCPPVersion(this ICentralSystemCLI CLI) + { + + if (CLI.Environment.TryGetValue(EnvironmentKey.RemoteSystemOCPPVersion, out var values) && + values.Count > 0) + { + return values.First(); + } + + return null; + + } + + } + + + public interface ICentralSystemCLI : ICLI + { + + OCPPAdapter OCPP { get; } + + IEnumerable ConnectedNetworkingNodeIds { get; } + + + } + +} diff --git a/WWCP_OCPPv2.1/DataStructures/PredefinedStrings/ResetType.cs b/WWCP_OCPPv2.1/DataStructures/PredefinedStrings/ResetType.cs index c9bc7f908..4603f9bf9 100644 --- a/WWCP_OCPPv2.1/DataStructures/PredefinedStrings/ResetType.cs +++ b/WWCP_OCPPv2.1/DataStructures/PredefinedStrings/ResetType.cs @@ -67,21 +67,27 @@ public static Boolean IsNotNullOrEmpty(this ResetType? ResetType) /// /// Indicates whether this reset type is null or empty. /// - public readonly Boolean IsNullOrEmpty + public readonly Boolean IsNullOrEmpty => InternalId.IsNullOrEmpty(); /// /// Indicates whether this reset type is NOT null or empty. /// - public readonly Boolean IsNotNullOrEmpty + public readonly Boolean IsNotNullOrEmpty => InternalId.IsNotNullOrEmpty(); /// /// The length of the reset type. /// - public readonly UInt64 Length + public readonly UInt64 Length => (UInt64) (InternalId?.Length ?? 0); + /// + /// All registered reset types. + /// + public static IEnumerable All + => lookup.Values; + #endregion #region Constructor(s) diff --git a/WWCP_OCPPv2.1_CSMS/CLI/CLICommands/DefaultStrings.cs b/WWCP_OCPPv2.1_CSMS/CLI/CLICommands/DefaultStrings.cs new file mode 100644 index 000000000..24016f169 --- /dev/null +++ b/WWCP_OCPPv2.1_CSMS/CLI/CLICommands/DefaultStrings.cs @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2014-2024 GraphDefined GmbH + * This file is part of WWCP OCPP + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace cloud.charging.open.protocols.OCPPv2_1.CSMS.CommandLine +{ + + public static class DefaultStrings + { + + public const String OCPPv2_0_1 = "OCPPv2.0.1"; + public const String OCPPv2_1 = "OCPPv2.1"; + + } + +} diff --git a/WWCP_OCPPv2.1_CSMS/CLI/CLICommands/ResetCommand.cs b/WWCP_OCPPv2.1_CSMS/CLI/CLICommands/ResetCommand.cs new file mode 100644 index 000000000..a6a0088bf --- /dev/null +++ b/WWCP_OCPPv2.1_CSMS/CLI/CLICommands/ResetCommand.cs @@ -0,0 +1,145 @@ +/* + * Copyright (c) 2014-2024 GraphDefined GmbH + * This file is part of WWCP OCPP + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#region Usings + +using org.GraphDefined.Vanaheimr.CLI; +using org.GraphDefined.Vanaheimr.Illias; + +#endregion + +namespace cloud.charging.open.protocols.OCPPv2_1.CSMS.CommandLine +{ + + /// + /// Reset the current networking node + /// + /// The command line interface + //[CLIContext([ DefaultStrings.OCPPv2_0_1, + // DefaultStrings.OCPPv2_1 ])] + public class ResetCommand(ICSMSCLI CLI) : ACLICommand(CLI), + ICLICommand + { + + #region Data + + public static readonly String CommandName = nameof(ResetCommand)[..^7].ToLowerFirstChar(); + + #endregion + + #region Suggest(Arguments) + + public override IEnumerable Suggest(String[] Arguments) + { + + // No suggestions without a defined RemoteSystemId and matching OCPP version! + if (!cli.RemoteSystemIdIsSet() || + cli.GetRemoteSystemOCPPVersion() != DefaultStrings.OCPPv2_1) + { + return []; + } + + if (Arguments.Length > 2 && + CommandName.Equals(Arguments[0], StringComparison.OrdinalIgnoreCase)) + { + Arguments = Arguments.Take(2).ToArray(); + } + + + if (Arguments.Length == 2 && + CommandName.Equals(Arguments[0], StringComparison.OrdinalIgnoreCase)) + { + + foreach (var resetType in ResetType.All) + { + + if (resetType.ToString().Equals (Arguments[1], StringComparison.OrdinalIgnoreCase)) + return [ SuggestionResponse.ParameterCompleted($"{Arguments[0]} {resetType.ToString().ToLower()}") ]; + + if (resetType.ToString().StartsWith(Arguments[1], StringComparison.OrdinalIgnoreCase)) + return [ SuggestionResponse.ParameterPrefix ($"{Arguments[0]} {resetType.ToString().ToLower()}") ]; + + } + + return [ SuggestionResponse.CommandCompleted(CommandName) ]; + + } + + + if (Arguments.Length == 1) + { + + if (CommandName.Equals (Arguments[0], StringComparison.OrdinalIgnoreCase)) + return [ SuggestionResponse.CommandHelp(Help()) ]; + + if (CommandName.StartsWith(Arguments[0], StringComparison.OrdinalIgnoreCase)) + return [ SuggestionResponse.CommandCompleted(CommandName) ]; + + } + + return []; + + } + + #endregion + + #region Execute(Arguments, CancellationToken) + + public override async Task Execute(String[] Arguments, + CancellationToken CancellationToken) + { + + // No execution without a defined RemoteSystemId! + var sourceRoute = cli.GetRemoteSystemSourceRoute(); + if (sourceRoute is null) + return []; + + + if (Arguments.Length == 2 && + ResetType.TryParse(Arguments[1], out var resetType)) + { + + var response = await cli.OCPP.OUT.Reset( + new ResetRequest( + Destination: sourceRoute, + ResetType: resetType + ) + ); + + return [ + $"{Arguments.AggregateWith(" ")} => {response.Runtime.TotalMilliseconds} ms", + response.ToJSON().ToString(Newtonsoft.Json.Formatting.Indented) + ]; + + } + + return [ $"Usage: {CommandName} <{ResetType.All.Select(_ => _.ToString()).AggregateWith("|")}>" ]; + + } + + #endregion + + #region Help() + + public override String Help() + => $"{CommandName} <{ResetType.All.Select(_ => _.ToString()).AggregateWith("|")}> - Reset the current networking node"; + + #endregion + + } + +} diff --git a/WWCP_OCPPv2.1_CSMS/CLI/ICSMSCLI.cs b/WWCP_OCPPv2.1_CSMS/CLI/ICSMSCLI.cs new file mode 100644 index 000000000..8230455c9 --- /dev/null +++ b/WWCP_OCPPv2.1_CSMS/CLI/ICSMSCLI.cs @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2014-2024 GraphDefined GmbH + * This file is part of WWCP OCPP + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#region Usings + +using org.GraphDefined.Vanaheimr.CLI; + +using cloud.charging.open.protocols.WWCP.NetworkingNode; +using cloud.charging.open.protocols.OCPPv2_1.NetworkingNode; + +#endregion + +namespace cloud.charging.open.protocols.OCPPv2_1.CSMS.CommandLine +{ + + public static class ICSMSCLIExtensions + { + + public static Boolean RemoteSystemIdIsSet(this ICSMSCLI CLI) + { + + if (CLI.Environment.TryGetValue(EnvironmentKey.RemoteSystemId, out var values) && + values.Count > 0) + { + return true; + } + + return false; + + } + + public static NetworkingNode_Id? GetRemoteSystemId(this ICSMSCLI CLI) + { + + if (CLI.Environment.TryGetValue(EnvironmentKey.RemoteSystemId, out var values) && + values.Count > 0 && + NetworkingNode_Id.TryParse(values.First(), out var remoteSystemId)) + { + return remoteSystemId; + } + + return null; + + } + + public static SourceRouting? GetRemoteSystemSourceRoute(this ICSMSCLI CLI) + { + + if (CLI.Environment.TryGetValue(EnvironmentKey.RemoteSystemId, out var values) && + values.Count > 0 && + NetworkingNode_Id.TryParse(values.First(), out var remoteSystemId)) + { + return SourceRouting.To(remoteSystemId); + } + + return null; + + } + + public static String? GetRemoteSystemOCPPVersion(this ICSMSCLI CLI) + { + + if (CLI.Environment.TryGetValue(EnvironmentKey.RemoteSystemOCPPVersion, out var values) && + values.Count > 0) + { + return values.First(); + } + + return null; + + } + + } + + + public interface ICSMSCLI : ICLI + { + + OCPPAdapter OCPP { get; } + + IEnumerable ConnectedNetworkingNodeIds { get; } + + + } + +}