diff --git a/src/Webserver.API/Enums/ApiErrorCode.cs b/src/Webserver.API/Enums/ApiErrorCode.cs
index 4764e60..b429af8 100644
--- a/src/Webserver.API/Enums/ApiErrorCode.cs
+++ b/src/Webserver.API/Enums/ApiErrorCode.cs
@@ -235,8 +235,5 @@ public enum ApiErrorCode
/// Invalid Parameters provided (null/empty string that is forbidden?, invalid ticket length?, wrong datetime string format (rfc3339)? ...)
///
InvalidParams = -32602
-
-
-
}
-}
+}
\ No newline at end of file
diff --git a/src/Webserver.API/Enums/ApiPlcProgramBlockType.cs b/src/Webserver.API/Enums/ApiPlcProgramBlockType.cs
new file mode 100644
index 0000000..5f5d59f
--- /dev/null
+++ b/src/Webserver.API/Enums/ApiPlcProgramBlockType.cs
@@ -0,0 +1,47 @@
+// Copyright (c) 2023, Siemens AG
+//
+// SPDX-License-Identifier: MIT
+
+using Newtonsoft.Json;
+using Newtonsoft.Json.Converters;
+using System.Runtime.Serialization;
+
+namespace Siemens.Simatic.S7.Webserver.API.Enums
+{
+ ///
+ /// Block type enumeration.
+ ///
+ [JsonConverter(typeof(StringEnumConverter))]
+ public enum ApiPlcProgramBlockType
+ {
+ ///
+ /// OB block.
+ ///
+ [EnumMember(Value = "ob")]
+ Ob,
+
+ ///
+ /// FB block.
+ ///
+ [EnumMember(Value = "fc")]
+ Fc,
+
+ ///
+ /// FC block.
+ ///
+ [EnumMember(Value = "fb")]
+ Fb,
+
+ ///
+ /// SFB block.
+ ///
+ [EnumMember(Value = "sfc")]
+ Sfc,
+
+ ///
+ /// SFC block.
+ ///
+ [EnumMember(Value = "sfb")]
+ Sfb,
+ }
+}
diff --git a/src/Webserver.API/Models/ApiPlcProgramBrowseCodeBlocksData.cs b/src/Webserver.API/Models/ApiPlcProgramBrowseCodeBlocksData.cs
index caf709a..33d5560 100644
--- a/src/Webserver.API/Models/ApiPlcProgramBrowseCodeBlocksData.cs
+++ b/src/Webserver.API/Models/ApiPlcProgramBrowseCodeBlocksData.cs
@@ -3,6 +3,7 @@
// SPDX-License-Identifier: MIT
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
+using Siemens.Simatic.S7.Webserver.API.Enums;
using System;
namespace Siemens.Simatic.S7.Webserver.API.Models
@@ -18,7 +19,7 @@ public class ApiPlcProgramBrowseCodeBlocksData : IEquatable
public ApiPlcProgramBrowseCodeBlocksData ShallowCopy()
{
- return (ApiPlcProgramBrowseCodeBlocksData) this.MemberwiseClone();
+ return (ApiPlcProgramBrowseCodeBlocksData)this.MemberwiseClone();
}
///
@@ -37,13 +38,13 @@ public ApiPlcProgramBrowseCodeBlocksData ShallowCopy()
/// Type of the code block.
///
[JsonProperty("block_type")]
- public string BlockType { get; set; }
+ public ApiPlcProgramBlockType BlockType { get; set; }
///
/// Constructor.
///
[JsonConstructor]
- public ApiPlcProgramBrowseCodeBlocksData(string name, ushort blockNumber, string blockType)
+ public ApiPlcProgramBrowseCodeBlocksData(string name, ushort blockNumber, ApiPlcProgramBlockType blockType)
{
Name = name;
BlockNumber = blockNumber;
diff --git a/tests/Webserver.API.UnitTests/ApiRequestTests.cs b/tests/Webserver.API.UnitTests/ApiRequestTests.cs
index df5eb12..441fadc 100644
--- a/tests/Webserver.API.UnitTests/ApiRequestTests.cs
+++ b/tests/Webserver.API.UnitTests/ApiRequestTests.cs
@@ -8,7 +8,6 @@
using Siemens.Simatic.S7.Webserver.API.Enums;
using Siemens.Simatic.S7.Webserver.API.Exceptions;
using Siemens.Simatic.S7.Webserver.API.Models;
-using Siemens.Simatic.S7.Webserver.API.Models.ApiDiagnosticBuffer;
using Siemens.Simatic.S7.Webserver.API.Models.FailsafeParameters;
using Siemens.Simatic.S7.Webserver.API.Models.Requests;
using Siemens.Simatic.S7.Webserver.API.Models.Responses;
@@ -1075,6 +1074,69 @@ public void T013_06_ApiPlcProgramBrowse_InvalidArrayIndex_ExcThrown()
Assert.ThrowsAsync(async () => await TestHandler.PlcProgramBrowseAsync(ApiPlcProgramBrowseMode.Children, "\"DataTypes\".\"Bool\"a"));
}
+ ///
+ ///
+ ///
+ ///
+ [Test]
+ public async Task T014_01_PlcProgramDownloadProfilingData_Success()
+ {
+ var mockHttp = new MockHttpMessageHandler();
+ // Setup a respond for the user api (including a wildcard in the URL)
+ mockHttp.When(HttpMethod.Post, $"https://{Ip.ToString()}/api/jsonrpc")
+ .Respond("application/json", ResponseStrings.PlcProgramDownloadProfilingDataSuccess); // Respond with JSON
+ // Inject the handler or client into your application code
+ var client = new HttpClient(mockHttp);
+ client.BaseAddress = new Uri($"https://{Ip.ToString()}");
+ TestHandler = new ApiHttpClientRequestHandler(client, ApiRequestFactory, ApiResponseChecker);
+
+ ApiSingleStringResponse response = await TestHandler.PlcProgramDownloadProfilingDataAsync();
+
+ Assert.That(response.Result.Equals("jgxikeMgLryvP0YoHc.eqt8BY787"));
+ }
+
+ ///
+ ///
+ ///
+ ///
+ [Test]
+ public void T014_02_PlcProgramDownloadProfilingData_NoResources()
+ {
+ // This case could happen if the user downloads the profiling
+ // data twice without clearing the ticket or downloading the actual data.
+
+ var mockHttp = new MockHttpMessageHandler();
+ // Setup a respond for the user api (including a wildcard in the URL)
+ mockHttp.When(HttpMethod.Post, $"https://{Ip.ToString()}/api/jsonrpc")
+ .Respond("application/json", ResponseStrings.PlcProgramDownloadProfilingDataNoResources); // Respond with JSON
+ // Inject the handler or client into your application code
+ var client = new HttpClient(mockHttp);
+ client.BaseAddress = new Uri($"https://{Ip.ToString()}");
+ TestHandler = new ApiHttpClientRequestHandler(client, ApiRequestFactory, ApiResponseChecker);
+
+ Assert.ThrowsAsync(async () => await TestHandler.PlcProgramDownloadProfilingDataAsync());
+ }
+
+ ///
+ ///
+ ///
+ ///
+ [Test]
+ public void T014_03_PlcProgramDownloadProfilingData_PermissionDenied()
+ {
+
+ var mockHttp = new MockHttpMessageHandler();
+ // Setup a respond for the user api (including a wildcard in the URL)
+ mockHttp.When(HttpMethod.Post, $"https://{Ip.ToString()}/api/jsonrpc")
+ .Respond("application/json", ResponseStrings.PlcProgramDownloadProfilingDataPermissionDenied); // Respond with JSON
+ // Inject the handler or client into your application code
+ var client = new HttpClient(mockHttp);
+ client.BaseAddress = new Uri($"https://{Ip.ToString()}");
+ TestHandler = new ApiHttpClientRequestHandler(client, ApiRequestFactory, ApiResponseChecker);
+
+ Assert.ThrowsAsync(async () => await TestHandler.PlcProgramDownloadProfilingDataAsync());
+ }
+
///
///
///
@@ -1093,6 +1155,148 @@ public void T015_ApiPlcProgramBrowse_PermissionDenied_ExcThrown()
Assert.ThrowsAsync(async () => await TestHandler.PlcProgramBrowseAsync(ApiPlcProgramBrowseMode.Children, "\"DataTypes\".\"Bool\""));
}
+ ///
+ ///
+ ///
+ ///
+ [Test]
+ public async Task T015_02_ApiPlcProgramBrowseCodeBlocks_EmptyResult()
+ {
+ var mockHttp = new MockHttpMessageHandler();
+ // Setup a respond for the user api (including a wildcard in the URL)
+ mockHttp.When(HttpMethod.Post, $"https://{Ip.ToString()}/api/jsonrpc")
+ .Respond("application/json", ResponseStrings.PlcProgramBrowseCodeBlocksEmptyResult); // Respond with JSON
+ // Inject the handler or client into your application code
+ var client = new HttpClient(mockHttp);
+ client.BaseAddress = new Uri($"https://{Ip.ToString()}");
+ TestHandler = new ApiHttpClientRequestHandler(client, ApiRequestFactory, ApiResponseChecker);
+
+ ApiPlcProgramBrowseCodeBlocksResponse response = await TestHandler.PlcProgramBrowseCodeBlocksAsync();
+ Assert.That(response.Result.Count == 0);
+ }
+
+ ///
+ ///
+ ///
+ ///
+ [Test]
+ public void T015_03_ApiPlcProgramBrowseCodeBlocks_InvalidParams()
+ {
+ var mockHttp = new MockHttpMessageHandler();
+ // Setup a respond for the user api (including a wildcard in the URL)
+ mockHttp.When(HttpMethod.Post, $"https://{Ip.ToString()}/api/jsonrpc")
+ .Respond("application/json", ResponseStrings.PlcProgramBrowseCodeBlocksInvalidParams); // Respond with JSON
+ // Inject the handler or client into your application code
+ var client = new HttpClient(mockHttp);
+ client.BaseAddress = new Uri($"https://{Ip.ToString()}");
+ TestHandler = new ApiHttpClientRequestHandler(client, ApiRequestFactory, ApiResponseChecker);
+
+ Assert.ThrowsAsync(async () => await TestHandler.PlcProgramBrowseCodeBlocksAsync());
+ }
+
+ ///
+ ///
+ ///
+ ///
+ [Test]
+ public async Task T015_04_ApiPlcProgramBrowseCodeBlocks_Success()
+ {
+ var mockHttp = new MockHttpMessageHandler();
+ // Setup a respond for the user api (including a wildcard in the URL)
+ mockHttp.When(HttpMethod.Post, $"https://{Ip.ToString()}/api/jsonrpc")
+ .Respond("application/json", ResponseStrings.PlcProgramBrowseCodeBlocksSuccess); // Respond with JSON
+ // Inject the handler or client into your application code
+ var client = new HttpClient(mockHttp);
+ client.BaseAddress = new Uri($"https://{Ip.ToString()}");
+ TestHandler = new ApiHttpClientRequestHandler(client, ApiRequestFactory, ApiResponseChecker);
+
+ ApiPlcProgramBrowseCodeBlocksResponse response = await TestHandler.PlcProgramBrowseCodeBlocksAsync();
+
+ Assert.That(response.Result.Count == 5);
+
+ Assert.That(response.Result[0].Name == "Main");
+ Assert.That(response.Result[0].BlockType == ApiPlcProgramBlockType.Ob);
+ Assert.That(response.Result[0].BlockNumber == 1);
+
+ Assert.That(response.Result[1].Name == "USEND");
+ Assert.That(response.Result[1].BlockType == ApiPlcProgramBlockType.Sfb);
+ Assert.That(response.Result[1].BlockNumber == 8);
+
+ Assert.That(response.Result[2].Name == "COPY_HW");
+ Assert.That(response.Result[2].BlockType == ApiPlcProgramBlockType.Sfc);
+ Assert.That(response.Result[2].BlockNumber == 65509);
+
+ Assert.That(response.Result[3].Name == "PRODTEST");
+ Assert.That(response.Result[3].BlockType == ApiPlcProgramBlockType.Fb);
+ Assert.That(response.Result[3].BlockNumber == 65522);
+
+ Assert.That(response.Result[4].Name == "FC_14325");
+ Assert.That(response.Result[4].BlockType == ApiPlcProgramBlockType.Fc);
+ Assert.That(response.Result[4].BlockNumber == 14325);
+ }
+
+ ///
+ ///
+ ///
+ ///
+ [Test]
+ public async Task T015_05_ApiPlcProgramBrowseCodeBlocks_EmptyBlockName()
+ {
+ var mockHttp = new MockHttpMessageHandler();
+ // Setup a respond for the user api (including a wildcard in the URL)
+ mockHttp.When(HttpMethod.Post, $"https://{Ip.ToString()}/api/jsonrpc")
+ .Respond("application/json", ResponseStrings.PlcProgramBrowseCodeBlocksEmptyBlockName); // Respond with JSON
+ // Inject the handler or client into your application code
+ var client = new HttpClient(mockHttp);
+ client.BaseAddress = new Uri($"https://{Ip.ToString()}");
+ TestHandler = new ApiHttpClientRequestHandler(client, ApiRequestFactory, ApiResponseChecker);
+
+ ApiPlcProgramBrowseCodeBlocksResponse response = await TestHandler.PlcProgramBrowseCodeBlocksAsync();
+
+ Assert.That(response.Result.Count == 1);
+ Assert.That(response.Result[0].Name == String.Empty);
+ Assert.That(response.Result[0].BlockType == ApiPlcProgramBlockType.Ob);
+ Assert.That(response.Result[0].BlockNumber == 1);
+ }
+
+ ///
+ ///
+ ///
+ ///
+ [Test]
+ public void T015_06_ApiPlcProgramBrowseCodeBlocks_StringAsBlockNumber()
+ {
+ var mockHttp = new MockHttpMessageHandler();
+ // Setup a respond for the user api (including a wildcard in the URL)
+ mockHttp.When(HttpMethod.Post, $"https://{Ip.ToString()}/api/jsonrpc")
+ .Respond("application/json", ResponseStrings.PlcProgramBrowseCodeBlocksStringAsNumber); // Respond with JSON
+ // Inject the handler or client into your application code
+ var client = new HttpClient(mockHttp);
+ client.BaseAddress = new Uri($"https://{Ip.ToString()}");
+ TestHandler = new ApiHttpClientRequestHandler(client, ApiRequestFactory, ApiResponseChecker);
+
+ Assert.ThrowsAsync(async () => await TestHandler.PlcProgramBrowseCodeBlocksAsync());
+ }
+
+ ///
+ ///
+ ///
+ ///
+ [Test]
+ public void T015_07_ApiPlcProgramBrowseCodeBlocks_PermissionDenied()
+ {
+ var mockHttp = new MockHttpMessageHandler();
+ // Setup a respond for the user api (including a wildcard in the URL)
+ mockHttp.When(HttpMethod.Post, $"https://{Ip.ToString()}/api/jsonrpc")
+ .Respond("application/json", ResponseStrings.PlcProgramBrowseCodeBlocksPermissionDenied); // Respond with JSON
+ // Inject the handler or client into your application code
+ var client = new HttpClient(mockHttp);
+ client.BaseAddress = new Uri($"https://{Ip.ToString()}");
+ TestHandler = new ApiHttpClientRequestHandler(client, ApiRequestFactory, ApiResponseChecker);
+
+ Assert.ThrowsAsync(async () => await TestHandler.PlcProgramBrowseCodeBlocksAsync());
+ }
+
///
///
///
diff --git a/tests/Webserver.API.UnitTests/ResponseStrings.cs b/tests/Webserver.API.UnitTests/ResponseStrings.cs
index 95963a4..e5e423c 100644
--- a/tests/Webserver.API.UnitTests/ResponseStrings.cs
+++ b/tests/Webserver.API.UnitTests/ResponseStrings.cs
@@ -48,10 +48,21 @@ public static class ResponseStrings
public const string PlcProgramBrowseErrorStruct = "{\"jsonrpc\":\"2.0\",\"id\":\"ibf8wom\",\"result\":[{\"name\":\"ERROR_ID\",\"db_number\":1,\"datatype\":\"word\"},{\"name\":\"FLAGS\",\"db_number\":1,\"datatype\":\"byte\"},{\"name\":\"REACTION\",\"db_number\":1,\"datatype\":\"byte\"},{\"name\":\"CODE_ADDRESS\",\"has_children\":true,\"db_number\":1,\"datatype\":\"cref\"},{\"name\":\"MODE\",\"db_number\":1,\"datatype\":\"byte\"},{\"name\":\"OPERAND_NUMBER\",\"db_number\":1,\"datatype\":\"uint\"},{\"name\":\"POINTER_NUMBER_LOCATION\",\"db_number\":1,\"datatype\":\"uint\"},{\"name\":\"SLOT_NUMBER_SCOPE\",\"db_number\":1,\"datatype\":\"uint\"},{\"name\":\"DATA_ADDRESS\",\"has_children\":true,\"db_number\":1,\"datatype\":\"nref\"}]}";
public const string PlcProgramBrowseVarIsNotAStructure = "{\"jsonrpc\":\"2.0\",\"id\":\"5q27h2n\",\"error\":{\"code\":202,\"message\":\"Variable is not a structure\"}}";
+ public const string PlcProgramBrowseCodeBlocksEmptyResult = "{\"jsonrpc\":\"2.0\",\"id\":\"y9vedkb9\",\"result\":[]}";
+ public const string PlcProgramBrowseCodeBlocksInvalidParams = "{\"jsonrpc\":\"2.0\",\"id\":55,\"error\":{\"code\":-32602,\"message\":\"Invalid Params\"}}";
+ public const string PlcProgramBrowseCodeBlocksSuccess = "{\"jsonrpc\":\"2.0\",\"id\":96,\"result\":[{\"name\":\"Main\",\"block_number\":1,\"block_type\":\"ob\"},{\"name\":\"USEND\",\"block_number\":8,\"block_type\":\"sfb\"},{\"name\":\"COPY_HW\",\"block_number\":65509,\"block_type\":\"sfc\"},{\"name\":\"PRODTEST\",\"block_number\":65522,\"block_type\":\"fb\"},{\"name\":\"FC_14325\",\"block_number\":14325,\"block_type\":\"fc\"}]}";
+ public const string PlcProgramBrowseCodeBlocksEmptyBlockName = "{\"jsonrpc\":\"2.0\",\"id\":96,\"result\":[{\"name\":\"\",\"block_number\":1,\"block_type\":\"ob\"}]}";
+ public const string PlcProgramBrowseCodeBlocksStringAsNumber = "{\"jsonrpc\":\"2.0\",\"id\":96,\"result\":[{\"name\":\"Main\",\"block_number\":\"abcdefg\",\"block_type\":\"ob\"},{\"name\":\"USEND\",\"block_number\":8,\"block_type\":\"sfb\"},{\"name\":\"COPY_HW\",\"block_number\":65509,\"block_type\":\"sfc\"},{\"name\":\"PRODTEST\",\"block_number\":65522,\"block_type\":\"sfc\"}]}";
+ public const string PlcProgramBrowseCodeBlocksPermissionDenied = "{\"jsonrpc\":\"2.0\",\"id\":50,\"error\":{\"code\":2,\"message\":\"Permission denied\"}}";
+
public const string PlcProgramInvalidAddress = "{\"jsonrpc\":\"2.0\",\"id\":\"5uxrl166\",\"error\":{\"code\":201,\"message\":\"Invalid address\"}}";
public const string PlcProgramAddressDoesNotExist = "{\"jsonrpc\":\"2.0\",\"id\":\"8buk8ryn\",\"error\":{\"code\":200,\"message\":\"Address does not exist\"}}";
public const string PlcProgramnInvalidArrayIndex = "{\"jsonrpc\":\"2.0\",\"id\":\"f5eqwla\",\"error\":{\"code\":203,\"message\":\"Invalid array index\"}}";
+ public const string PlcProgramDownloadProfilingDataSuccess = "{\"jsonrpc\":\"2.0\",\"id\": 6,\"result\":\"jgxikeMgLryvP0YoHc.eqt8BY787\"}";
+ public const string PlcProgramDownloadProfilingDataPermissionDenied = "{\"jsonrpc\":\"2.0\",\"id\": 18,\"error\":{\"code\": 2,\"message\":\"Permission denied\"}}";
+ public const string PlcProgramDownloadProfilingDataNoResources = "{\"jsonrpc\":\"2.0\",\"id\":28,\"error\":{\"code\":4,\"message\":\"No Resources\"}}";
+
public const string PlcProgramUnsupportedAddress = "{\"jsonrpc\":\"2.0\",\"id\":\"1wwe2z8j\",\"error\":{\"code\":204,\"message\":\"Unsupported address\"}}";
public const string PlcProgramReadFalseBool = "{\"jsonrpc\":\"2.0\",\"id\":\"aax109lc\",\"result\":false}";