diff --git a/.vscode/launch.json b/.vscode/launch.json
index b59b5a8..964f31a 100644
--- a/.vscode/launch.json
+++ b/.vscode/launch.json
@@ -2,25 +2,21 @@
"version": "0.2.0",
"configurations": [
{
- // Use IntelliSense to find out which attributes exist for C# debugging
- // Use hover for the description of the existing attributes
- // For further information visit https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md
- "name": ".NET Core Launch (console)",
- "type": "coreclr",
+ "name": "C#: 02_read_process_data_from_ifm_iotcore Debug",
+ "type": "dotnet",
"request": "launch",
- "preLaunchTask": "build",
- // If you have changed target frameworks, make sure to update the program path.
- "program": "${workspaceFolder}/src/Tests/IODD.Parser.Tests/bin/Debug/net7.0/IODD.Parser.Tests.dll",
- "args": [],
- "cwd": "${workspaceFolder}/src/Tests/IODD.Parser.Tests",
- // For more information about the 'console' field, see https://aka.ms/VSCode-CS-LaunchJson-Console
- "console": "internalConsole",
- "stopAtEntry": false
+ "projectPath": "${workspaceFolder}/samples/02_read_process_data_from_ifm_iotcore/02_read_process_data_from_ifm_iotcore.csproj"
},
{
- "name": ".NET Core Attach",
+ "name": "C#: IOLink.NET.ConsoleRunner",
"type": "coreclr",
- "request": "attach"
+ "request": "launch",
+ "program": "${workspaceFolder}/src/Tests/IOLink.NET.ConsoleRunner/bin/Debug/net9.0/IOLink.NET.ConsoleRunner.dll",
+ "args": [],
+ "cwd": "${workspaceFolder}/src/Tests/IOLink.NET.ConsoleRunner",
+ "console": "integratedTerminal",
+ "stopAtEntry": false,
+ "preLaunchTask": "build: ConsoleRunner"
}
]
}
\ No newline at end of file
diff --git a/.vscode/tasks.json b/.vscode/tasks.json
index 36e46f5..55b442f 100644
--- a/.vscode/tasks.json
+++ b/.vscode/tasks.json
@@ -7,11 +7,62 @@
"type": "process",
"args": [
"build",
- "${workspaceFolder}/src/Tests/IODD.Parser.Tests/IODD.Parser.Tests.csproj",
+ "${file}",
"/property:GenerateFullPaths=true",
"/consoleloggerparameters:NoSummary"
],
- "problemMatcher": "$msCompile"
+ "problemMatcher": "$msCompile",
+ "group": {
+ "kind": "build",
+ "isDefault": true
+ },
+ "presentation": {
+ "echo": true,
+ "reveal": "silent",
+ "focus": false,
+ "panel": "shared",
+ "showReuseMessage": true,
+ "clear": false
+ }
+ },
+ {
+ "label": "build: current project",
+ "command": "dotnet",
+ "type": "process",
+ "args": [
+ "build",
+ "${fileDirname}",
+ "/property:GenerateFullPaths=true",
+ "/consoleloggerparameters:NoSummary"
+ ],
+ "problemMatcher": "$msCompile",
+ "group": "build"
+ },
+ {
+ "label": "build: solution",
+ "command": "dotnet",
+ "type": "process",
+ "args": [
+ "build",
+ "${workspaceFolder}/src/IOLink.NET.sln",
+ "/property:GenerateFullPaths=true",
+ "/consoleloggerparameters:NoSummary"
+ ],
+ "problemMatcher": "$msCompile",
+ "group": "build"
+ },
+ {
+ "label": "build: ConsoleRunner",
+ "command": "dotnet",
+ "type": "process",
+ "args": [
+ "build",
+ "${workspaceFolder}/src/Tests/IOLink.NET.ConsoleRunner/IOLink.NET.ConsoleRunner.csproj",
+ "/property:GenerateFullPaths=true",
+ "/consoleloggerparameters:NoSummary"
+ ],
+ "problemMatcher": "$msCompile",
+ "group": "build"
},
{
"label": "publish",
@@ -19,11 +70,12 @@
"type": "process",
"args": [
"publish",
- "${workspaceFolder}/src/Tests/IODD.Parser.Tests/IODD.Parser.Tests.csproj",
+ "${file}",
"/property:GenerateFullPaths=true",
"/consoleloggerparameters:NoSummary"
],
- "problemMatcher": "$msCompile"
+ "problemMatcher": "$msCompile",
+ "group": "build"
},
{
"label": "watch",
@@ -33,9 +85,11 @@
"watch",
"run",
"--project",
- "${workspaceFolder}/src/Tests/IODD.Parser.Tests/IODD.Parser.Tests.csproj"
+ "${file}"
],
- "problemMatcher": "$msCompile"
+ "problemMatcher": "$msCompile",
+ "group": "build",
+ "isBackground": true
}
]
}
\ No newline at end of file
diff --git a/README.md b/README.md
index c5b0185..6586013 100644
--- a/README.md
+++ b/README.md
@@ -18,7 +18,7 @@ IOLink.NET also provides a parser for the IO Device Description or short [IODD](
## Getting started
-IOLinkNET offers a modular toolset to work with IOLink data and device descriptions. Since we do not know the requirements specific to your project we have provide you with different components that you are free to orchestrate in order to achieve your goals. Otherwise we also maintain a default implementation.
+IOLink.NET offers a modular toolset to work with IOLink data and device descriptions. Since we do not know the requirements specific to your project we have provide you with different components that you are free to orchestrate in order to achieve your goals. Otherwise we also maintain a default implementation.
Different Usage samples are provided in the samples/ folder. It is still work in progress but growing steadily.
@@ -26,21 +26,15 @@ Different Usage samples are provided in the samples/ folder. It is still work in
Decoding IO-Link data requires us to complete different workloads before hands. First of all we need to source the IODD package for the device we are working with. Then the IODD package has to be searched for the correct description file which in turn has be to parsed. The raw IODD format has some shortcomings when it comes to automatic processing so it needs to be preprocessed.
-Based on the preprocessed IODD structure we are able to select the correct data types and decode/encode the given payloads. As you notice there is plenty of work to do for IOLinkNET. In this section we describe the functionality of the different projects.
-
-| Package | Purpose |
-| --------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------ |
-| IOLinkNET.IODD.Structure | Contains model to represent the XML structure of an IODD. |
-| IOLinkNET.IODD.Provider | Functionality to retrieve IODD packages from e.g. the IODDFinderAPI |
-| IOLinkNET.IODD.Parser | Parser components to transform XML IODD to IOLinkNET.IODD.Structure format |
-| IOLinkNET.IODD.Resolution | Defines favorable and self-contained format for data type representation and utility to create those from IOLinkNET.IODD.Structure format. |
-| IOLinkNET.IODD.Standard | Contains IODD Standard components to support IO-Link Standards |
-| IOLinkNET.Conversion | Defines functionality to convert from and to iolink data. |
-| IOLinkNET.Device | Defines contracts how communication with io link masters. |
-| IOLinkNET.Vendors.\* | Provides vendor specific implementation of the Device interfaces. |
-| IOLinkNET.Integration | Orchestrates the modules to functionality that can read process data or parameter data from a device implementation. |
-| IOLinkNET.Visualization | Base implementation to provide easy to use components which are useful for data visualization e.g. device readable menu structure |
-| IOLinkNET.Visualization.Structure | Contains visualization models which are more usable than using IODD structures with references |
+Based on the preprocessed IODD structure we are able to select the correct data types and decode/encode the given payloads. As you notice there is plenty of work to do for IOLink.NET. In this section we describe the functionality of the different projects.
+
+| Package | Purpose |
+| --------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------ |
+| IOLink.NET.Core | Contains core contracts, models, and extensions that define the foundation interfaces for device communication and data structures |
+| IOLink.NET | Main library containing Conversion and Integration functionality for working with IO-Link data |
+| IOLink.NET.IODD | Complete IODD (IO Device Description) handling including Structure, Provider, Parser, Resolution, and Standard components |
+| IOLink.NET.Vendors.Ifm | IFM vendor specific implementation for communicating with IFM IO-Link masters |
+| IOLink.NET.Visualization | Base implementation to provide easy to use components which are useful for data visualization e.g. device readable menu structure |
## Supporters
diff --git a/samples/02_read_process_data_from_ifm_iotcore/02_read_process_data_from_ifm_iotcore.csproj b/samples/02_read_process_data_from_ifm_iotcore/02_read_process_data_from_ifm_iotcore.csproj
index d128917..78521f1 100644
--- a/samples/02_read_process_data_from_ifm_iotcore/02_read_process_data_from_ifm_iotcore.csproj
+++ b/samples/02_read_process_data_from_ifm_iotcore/02_read_process_data_from_ifm_iotcore.csproj
@@ -1,8 +1,12 @@
-
-
+
+
+
+
+
+
diff --git a/samples/02_read_process_data_from_ifm_iotcore/Program.cs b/samples/02_read_process_data_from_ifm_iotcore/Program.cs
index f3719ec..126e666 100644
--- a/samples/02_read_process_data_from_ifm_iotcore/Program.cs
+++ b/samples/02_read_process_data_from_ifm_iotcore/Program.cs
@@ -1,34 +1,61 @@
-using IOLinkNET.Integration;
-using IOLinkNET.Vendors.Ifm;
+// First initialize a master connection with a master device that supports the IoT Core api e.g. IFM AL1350 using the provided ConnectionFactory.
+using IOLink.NET.Integration;
+using IOLink.NET.Vendors.Ifm;
-
-// First initialize a master connection with a master device that supports the IoT Core api e.g. IFM AL1350 using the provided ConnectionFactory.
-var masterConnection = IfmIoTCoreMasterConnectionFactory.Create("http://192.168.0.113");
+var masterConnection = IfmIoTCoreMasterConnectionFactory.Create("http://192.168.1.177");
// Create a port reader with the master connection and configure IOLinkNET to use the public IODD finder api and its default conversion stack.
-var portReader = PortReaderBuilder.NewPortReader()
+var portReader = PortReaderBuilder
+ .NewPortReader()
.WithMasterConnection(masterConnection)
.WithConverterDefaults()
.WithPublicIODDFinderApi()
.Build();
-
// Initialize the port reader for port 1.
-await portReader.InitializeForPortAsync(1);
-
+await portReader.InitializeForPortAsync(4, CancellationToken.None);
Console.WriteLine("Reading Process & Acyclic data from configured master.");
-while (!(Console.KeyAvailable && Console.ReadKey(true).Key == ConsoleKey.Escape))
+while (true)
{
// Read the process data from the device.
- var processData = await portReader.ReadConvertedProcessDataInAsync();
+ var processData = await portReader.ReadConvertedProcessDataInAsync(CancellationToken.None);
// Read the parameter 19, subindex 0 from the device. It is device specific what this parameter represents or if it is even available.
- var parameter = await portReader.ReadConvertedParameterAsync(19, 0);
+ var parameter = await portReader.ReadConvertedParameterAsync(19, 0, CancellationToken.None);
+ if (parameter is List> parameterValues)
+ {
+ // Print the parameter values to the console.
+ Console.WriteLine("Parameter 19, Subindex 0:");
+ foreach (var value in parameterValues)
+ {
+ Console.WriteLine($" {value.Item1}: {value.Item2}");
+ }
+ }
+ else
+ {
+ Console.WriteLine("Parameter 19, Subindex 0 is not available or not convertible.");
+ }
// Print the process data to the console.
Console.WriteLine(processData);
- // Wait 100ms before reading the process data again.
- await Task.Delay(100);
+ // Try to detect an Escape key press, but avoid calling Console.KeyAvailable when there's no console
+ // or when input is redirected. Use Console.IsInputRedirected as a guard and fall back to catching
+ // InvalidOperationException just in case.
+ try
+ {
+ if (!Console.IsInputRedirected && Console.KeyAvailable)
+ {
+ var key = Console.ReadKey(true);
+ if (key.Key == ConsoleKey.Escape)
+ break;
+ }
+ }
+ catch (InvalidOperationException)
+ {
+ // No console available or input redirected - continue without key checking.
+ }
+
+ // Wait 500ms before reading the process data again.
+ await Task.Delay(500);
}
-
diff --git a/src/.editorconfig b/src/.editorconfig
index a1c1f44..ab7b083 100644
--- a/src/.editorconfig
+++ b/src/.editorconfig
@@ -366,4 +366,67 @@ dotnet_naming_style.s_camelcase.word_separator =
dotnet_naming_style.s_camelcase.capitalization = camel_case
dotnet_diagnostic.IDE0130.severity = none
-dotnet_diagnostic.IDE0230.severity = none
\ No newline at end of file
+dotnet_diagnostic.IDE0230.severity = none
+dotnet_diagnostic.CS0618.severity = none
+
+#### Async/Await Conventions ####
+
+# CA2007: Do not directly await a Task without calling ConfigureAwait
+dotnet_diagnostic.CA2007.severity = error
+
+# Require CancellationToken parameter in async methods
+# This is enforced through custom analyzers and code review
+# AsyncFixer01: Unnecessary async/await usage
+dotnet_diagnostic.AsyncFixer01.severity = error
+
+# AsyncFixer02: Long-running or blocking operations under an async method
+dotnet_diagnostic.AsyncFixer02.severity = error
+
+# AsyncFixer03: Fire & forget async void methods
+dotnet_diagnostic.AsyncFixer03.severity = error
+
+# AsyncFixer04: Fire & forget async call inside a using block
+dotnet_diagnostic.AsyncFixer04.severity = error
+
+# AsyncFixer05: Downcasting from a nested task to an outer task
+dotnet_diagnostic.AsyncFixer05.severity = error
+
+# VSTHRD100: Avoid async void methods
+dotnet_diagnostic.VSTHRD100.severity = error
+
+# VSTHRD101: Avoid unsupported async delegates
+dotnet_diagnostic.VSTHRD101.severity = error
+
+# VSTHRD103: Call async methods when in an async method
+dotnet_diagnostic.VSTHRD103.severity = suggestion
+
+# VSTHRD110: Observe result of async calls
+dotnet_diagnostic.VSTHRD110.severity = suggestion
+
+# VSTHRD111: Use ConfigureAwait(bool)
+dotnet_diagnostic.VSTHRD111.severity = error
+
+# VSTHRD114: Avoid returning null from a Task-returning method
+dotnet_diagnostic.VSTHRD114.severity = error
+
+# VSTHRD200: Use Async suffix for async methods
+dotnet_diagnostic.VSTHRD200.severity = suggestion
+
+# CA1849: Call async methods when in an async method
+dotnet_diagnostic.CA1849.severity = suggestion
+
+# CA2016: Forward the CancellationToken parameter to methods that take one
+dotnet_diagnostic.CA2016.severity = error
+
+# Custom rule: Async methods should have CancellationToken parameter
+# This requires custom implementation but documents the requirement
+# dotnet_diagnostic.CustomAsync001.severity = error
+
+#### Test Files - Relaxed Async/Await Rules ####
+[Tests/**/*.cs]
+
+# Allow more flexibility in test files for async/await patterns
+dotnet_diagnostic.AsyncFixer01.severity = suggestion
+dotnet_diagnostic.VSTHRD111.severity = suggestion
+dotnet_diagnostic.CA2007.severity = suggestion
+dotnet_diagnostic.CA2016.severity = suggestion
\ No newline at end of file
diff --git a/src/Directory.Build.props b/src/Directory.Build.props
index 3206055..16b84d8 100644
--- a/src/Directory.Build.props
+++ b/src/Directory.Build.props
@@ -29,4 +29,11 @@
true
true
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/Directory.Packages.props b/src/Directory.Packages.props
index 66a4278..97a6abb 100644
--- a/src/Directory.Packages.props
+++ b/src/Directory.Packages.props
@@ -10,5 +10,10 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/src/IOLink.NET.Core/Contracts/IDeviceDefinitionProvider.cs b/src/IOLink.NET.Core/Contracts/IDeviceDefinitionProvider.cs
index c411730..2f45435 100644
--- a/src/IOLink.NET.Core/Contracts/IDeviceDefinitionProvider.cs
+++ b/src/IOLink.NET.Core/Contracts/IDeviceDefinitionProvider.cs
@@ -8,6 +8,6 @@ Task GetDeviceDefinitionAsync(
ushort vendorId,
uint deviceId,
string productId,
- CancellationToken cancellationToken = default
+ CancellationToken cancellationToken
);
}
diff --git a/src/IOLink.NET.Core/Contracts/IIODDPortReader.cs b/src/IOLink.NET.Core/Contracts/IIODDPortReader.cs
new file mode 100644
index 0000000..f14bd14
--- /dev/null
+++ b/src/IOLink.NET.Core/Contracts/IIODDPortReader.cs
@@ -0,0 +1,41 @@
+using IOLink.NET.Core.Models;
+
+namespace IOLink.NET.Core.Contracts;
+
+///
+/// Interface for reading and converting IODD data with type-safe result wrappers.
+/// Provides methods that return ConversionResult objects distinguishing between scalar and complex values.
+///
+public interface IIODDPortReader
+{
+ ///
+ /// Reads and converts a parameter value, returning a typed result that distinguishes between scalar and complex values.
+ ///
+ /// The parameter index.
+ /// The parameter subindex.
+ /// The cancellation token.
+ /// A ConversionResult containing either a ScalarResult or ComplexResult.
+ Task ReadConvertedParameterResultAsync(
+ ushort index,
+ byte subindex,
+ CancellationToken cancellationToken
+ );
+
+ ///
+ /// Reads and converts process data input, returning a typed result that distinguishes between scalar and complex values.
+ ///
+ /// The cancellation token.
+ /// A ConversionResult containing either a ScalarResult or ComplexResult.
+ Task ReadConvertedProcessDataInResultAsync(
+ CancellationToken cancellationToken
+ );
+
+ ///
+ /// Reads and converts process data output, returning a typed result that distinguishes between scalar and complex values.
+ ///
+ /// The cancellation token.
+ /// A ConversionResult containing either a ScalarResult or ComplexResult.
+ Task ReadConvertedProcessDataOutResultAsync(
+ CancellationToken cancellationToken
+ );
+}
diff --git a/src/IOLink.NET.Core/Contracts/IIODDProvider.cs b/src/IOLink.NET.Core/Contracts/IIODDProvider.cs
index 9bc8ce6..a1828ec 100644
--- a/src/IOLink.NET.Core/Contracts/IIODDProvider.cs
+++ b/src/IOLink.NET.Core/Contracts/IIODDProvider.cs
@@ -6,6 +6,6 @@ Task GetIODDPackageAsync(
ushort vendorId,
uint deviceId,
string productId,
- CancellationToken cancellationToken = default
+ CancellationToken cancellationToken
);
}
diff --git a/src/IOLink.NET.Core/Contracts/IMasterConnection.cs b/src/IOLink.NET.Core/Contracts/IMasterConnection.cs
index 3c69cd2..6c90b20 100644
--- a/src/IOLink.NET.Core/Contracts/IMasterConnection.cs
+++ b/src/IOLink.NET.Core/Contracts/IMasterConnection.cs
@@ -2,31 +2,29 @@ namespace IOLink.NET.Core.Contracts;
public interface IMasterConnection
{
- Task GetPortCountAsync(CancellationToken cancellationToken = default);
+ Task GetPortCountAsync(CancellationToken cancellationToken);
Task GetPortInformationAsync(
byte portNumber,
- CancellationToken cancellationToken = default
+ CancellationToken cancellationToken
);
- Task GetPortInformationsAsync(
- CancellationToken cancellationToken = default
- );
+ Task GetPortInformationsAsync(CancellationToken cancellationToken);
Task> ReadIndexAsync(
byte portNumber,
ushort index,
- byte subIindex = 0,
- CancellationToken cancellationToken = default
+ CancellationToken cancellationToken,
+ byte subIindex = 0
);
Task> ReadProcessDataInAsync(
byte portNumber,
- CancellationToken cancellationToken = default
+ CancellationToken cancellationToken
);
Task> ReadProcessDataOutAsync(
byte portNumber,
- CancellationToken cancellationToken = default
+ CancellationToken cancellationToken
);
}
diff --git a/src/IOLink.NET.Core/Models/ConversionResult.cs b/src/IOLink.NET.Core/Models/ConversionResult.cs
new file mode 100644
index 0000000..f604bc7
--- /dev/null
+++ b/src/IOLink.NET.Core/Models/ConversionResult.cs
@@ -0,0 +1,20 @@
+namespace IOLink.NET.Core.Models;
+
+///
+/// Base class for conversion results from IODD data conversion operations.
+///
+public abstract record ConversionResult;
+
+///
+/// Represents a scalar conversion result containing a primitive or simple value.
+///
+/// The converted scalar value.
+/// The CLR type used to represent the scalar value.
+public sealed record ScalarResult(object Value, Type ClrType) : ConversionResult;
+
+///
+/// Represents a complex conversion result containing a list of key-value mappings.
+///
+/// A list of key-value pairs where each value is a ScalarResult.
+public sealed record ComplexResult(IReadOnlyList> Values)
+ : ConversionResult;
diff --git a/src/IOLink.NET.IODD/Parser/IODDParser.cs b/src/IOLink.NET.IODD/Parser/IODDParser.cs
index 71595b7..d60e0da 100644
--- a/src/IOLink.NET.IODD/Parser/IODDParser.cs
+++ b/src/IOLink.NET.IODD/Parser/IODDParser.cs
@@ -1,6 +1,5 @@
using System.Xml.Linq;
using IOLink.NET.IODD.Helpers;
-using IOLink.NET.IODD.Parser;
using IOLink.NET.IODD.Parser.Parts.ExternalTextCollection;
using IOLink.NET.IODD.Parser.Parts.Menu;
using IOLink.NET.IODD.Parts;
diff --git a/src/IOLink.NET.IODD/Parser/Parts/Menu/MenuSetTParser.cs b/src/IOLink.NET.IODD/Parser/Parts/Menu/MenuSetTParser.cs
index e0461cc..a88a7ec 100644
--- a/src/IOLink.NET.IODD/Parser/Parts/Menu/MenuSetTParser.cs
+++ b/src/IOLink.NET.IODD/Parser/Parts/Menu/MenuSetTParser.cs
@@ -1,34 +1,50 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
using System.Xml.Linq;
-
using IOLink.NET.IODD.Helpers;
using IOLink.NET.IODD.Parts.Constants;
using IOLink.NET.IODD.Structure.Structure.Menu;
namespace IOLink.NET.IODD.Parser.Parts.Menu;
+
internal static class MenuSetTParser
{
public static MenuSetT Parse(XElement element, IEnumerable menuCollections)
{
- var identificationMenuId = element.Elements(IODDDeviceFunctionNames.IdentificationMenuName).Single().ReadMandatoryAttribute("menuId");
- var identificationMenu = menuCollections.Where(x => x.Menu.Id.Equals(identificationMenuId)).Single();
+ var identificationMenuId = element
+ .Elements(IODDDeviceFunctionNames.IdentificationMenuName)
+ .Single()
+ .ReadMandatoryAttribute("menuId");
+ var identificationMenu = menuCollections
+ .Where(x => x.Menu.Id.Equals(identificationMenuId))
+ .Single();
- var parameterMenuId = element.Elements(IODDDeviceFunctionNames.ParameterMenuName).SingleOrDefault()?.ReadMandatoryAttribute("menuId");
- var parameterMenu = menuCollections.Where(x => x.Menu.Id.Equals(parameterMenuId)).SingleOrDefault();
+ var parameterMenuId = element
+ .Elements(IODDDeviceFunctionNames.ParameterMenuName)
+ .SingleOrDefault()
+ ?.ReadMandatoryAttribute("menuId");
+ var parameterMenu = menuCollections
+ .Where(x => x.Menu.Id.Equals(parameterMenuId))
+ .SingleOrDefault();
- var observationMenuId = element.Elements(IODDDeviceFunctionNames.ObservationMenuName).SingleOrDefault()?.ReadMandatoryAttribute("menuId");
- var observationMenu = menuCollections.Where(x => x.Menu.Id.Equals(observationMenuId)).SingleOrDefault();
+ var observationMenuId = element
+ .Elements(IODDDeviceFunctionNames.ObservationMenuName)
+ .SingleOrDefault()
+ ?.ReadMandatoryAttribute("menuId");
+ var observationMenu = menuCollections
+ .Where(x => x.Menu.Id.Equals(observationMenuId))
+ .SingleOrDefault();
- var diagnosisMenuId = element.Elements(IODDDeviceFunctionNames.DiagnosisMenuName).SingleOrDefault()?.ReadMandatoryAttribute("menuId");
- var diagnosisMenu = menuCollections.Where(x => x.Menu.Id.Equals(diagnosisMenuId)).SingleOrDefault();
+ var diagnosisMenuId = element
+ .Elements(IODDDeviceFunctionNames.DiagnosisMenuName)
+ .SingleOrDefault()
+ ?.ReadMandatoryAttribute("menuId");
+ var diagnosisMenu = menuCollections
+ .Where(x => x.Menu.Id.Equals(diagnosisMenuId))
+ .SingleOrDefault();
- return new MenuSetT(new UIMenuRefSimpleT(identificationMenu.Menu.Id, identificationMenu.Menu),
- new UIMenuRefSimpleT(parameterMenu?.Menu.Id, parameterMenu?.Menu),
- new UIMenuRefSimpleT(observationMenu?.Menu.Id, observationMenu?.Menu),
+ return new MenuSetT(
+ new UIMenuRefSimpleT(identificationMenu.Menu.Id, identificationMenu.Menu),
+ new UIMenuRefSimpleT(parameterMenu?.Menu.Id, parameterMenu?.Menu),
+ new UIMenuRefSimpleT(observationMenu?.Menu.Id, observationMenu?.Menu),
new UIMenuRefSimpleT(diagnosisMenu?.Menu.Id, diagnosisMenu?.Menu)
);
}
diff --git a/src/IOLink.NET.IODD/Parser/Parts/Menu/UserInterfaceParser.cs b/src/IOLink.NET.IODD/Parser/Parts/Menu/UserInterfaceParser.cs
index 222d1ad..0d0ed6d 100644
--- a/src/IOLink.NET.IODD/Parser/Parts/Menu/UserInterfaceParser.cs
+++ b/src/IOLink.NET.IODD/Parser/Parts/Menu/UserInterfaceParser.cs
@@ -1,10 +1,9 @@
using System.Xml.Linq;
-
-using IOLink.NET.IODD.Helpers;
using IOLink.NET.IODD.Parts.Constants;
using IOLink.NET.IODD.Structure.Structure.Menu;
namespace IOLink.NET.IODD.Parser.Parts.Menu;
+
internal class UserInterfaceParser : IParserPart
{
private readonly IParserPartLocator _parserLocator;
@@ -14,13 +13,13 @@ public UserInterfaceParser(IParserPartLocator parserLocator)
_parserLocator = parserLocator;
}
- public bool CanParse(XName name)
- => name == IODDDeviceFunctionNames.UserInterfaceName;
-
+ public bool CanParse(XName name) => name == IODDDeviceFunctionNames.UserInterfaceName;
public UserInterfaceT Parse(XElement element)
{
- IEnumerable menuElements = element.Elements(IODDDeviceFunctionNames.MenuCollectionName).Elements(IODDDeviceFunctionNames.MenuName);
+ IEnumerable menuElements = element
+ .Elements(IODDDeviceFunctionNames.MenuCollectionName)
+ .Elements(IODDDeviceFunctionNames.MenuName);
List menuCollections = new();
foreach (var menuElement in menuElements)
@@ -29,15 +28,35 @@ public UserInterfaceT Parse(XElement element)
menuCollections.Add(menuCollection);
}
- XElement observerRoleMenuSetElement = element.Elements(IODDDeviceFunctionNames.ObserverRoleMenuSetName).First();
- MenuSetT observerRoleMenu = MenuSetTParser.Parse(observerRoleMenuSetElement, menuCollections);
-
- XElement maintenanceRoleMenuSetElement = element.Elements(IODDDeviceFunctionNames.MaintenanceRoleMenuSetName).First();
- MenuSetT maintenanceRoleMenu = MenuSetTParser.Parse(maintenanceRoleMenuSetElement, menuCollections);
-
- XElement specialistRoleMenuSetElement = element.Elements(IODDDeviceFunctionNames.SpecialistRoleMenuSetName).First();
- MenuSetT specialistRoleMenu = MenuSetTParser.Parse(specialistRoleMenuSetElement, menuCollections);
-
- return new UserInterfaceT(menuCollections, observerRoleMenu, maintenanceRoleMenu, specialistRoleMenu);
+ XElement observerRoleMenuSetElement = element
+ .Elements(IODDDeviceFunctionNames.ObserverRoleMenuSetName)
+ .First();
+ MenuSetT observerRoleMenu = MenuSetTParser.Parse(
+ observerRoleMenuSetElement,
+ menuCollections
+ );
+
+ XElement maintenanceRoleMenuSetElement = element
+ .Elements(IODDDeviceFunctionNames.MaintenanceRoleMenuSetName)
+ .First();
+ MenuSetT maintenanceRoleMenu = MenuSetTParser.Parse(
+ maintenanceRoleMenuSetElement,
+ menuCollections
+ );
+
+ XElement specialistRoleMenuSetElement = element
+ .Elements(IODDDeviceFunctionNames.SpecialistRoleMenuSetName)
+ .First();
+ MenuSetT specialistRoleMenu = MenuSetTParser.Parse(
+ specialistRoleMenuSetElement,
+ menuCollections
+ );
+
+ return new UserInterfaceT(
+ menuCollections,
+ observerRoleMenu,
+ maintenanceRoleMenu,
+ specialistRoleMenu
+ );
}
}
diff --git a/src/IOLink.NET.IODD/Provider/Contracts/IDeviceDefinitionProvider.cs b/src/IOLink.NET.IODD/Provider/Contracts/IDeviceDefinitionProvider.cs
index a7d946b..46014b4 100644
--- a/src/IOLink.NET.IODD/Provider/Contracts/IDeviceDefinitionProvider.cs
+++ b/src/IOLink.NET.IODD/Provider/Contracts/IDeviceDefinitionProvider.cs
@@ -4,5 +4,10 @@ namespace IOLink.NET.IODD.Provider;
public interface IDeviceDefinitionProvider
{
- Task GetDeviceDefinitionAsync(ushort vendorId, uint deviceId, string productId, CancellationToken cancellationToken = default);
+ Task GetDeviceDefinitionAsync(
+ ushort vendorId,
+ uint deviceId,
+ string productId,
+ CancellationToken cancellationToken
+ );
}
diff --git a/src/IOLink.NET.IODD/Provider/Contracts/IIODDProvider.cs b/src/IOLink.NET.IODD/Provider/Contracts/IIODDProvider.cs
index 21814d0..6103f4f 100644
--- a/src/IOLink.NET.IODD/Provider/Contracts/IIODDProvider.cs
+++ b/src/IOLink.NET.IODD/Provider/Contracts/IIODDProvider.cs
@@ -2,5 +2,10 @@ namespace IOLink.NET.IODD.Provider;
public interface IIODDProvider
{
- Task GetIODDPackageAsync(ushort vendorId, uint deviceId, string productId, CancellationToken cancellationToken = default);
+ Task GetIODDPackageAsync(
+ ushort vendorId,
+ uint deviceId,
+ string productId,
+ CancellationToken cancellationToken
+ );
}
diff --git a/src/IOLink.NET.IODD/Provider/DeviceDefinitionProvider.cs b/src/IOLink.NET.IODD/Provider/DeviceDefinitionProvider.cs
index 2fc81e7..8141c15 100644
--- a/src/IOLink.NET.IODD/Provider/DeviceDefinitionProvider.cs
+++ b/src/IOLink.NET.IODD/Provider/DeviceDefinitionProvider.cs
@@ -19,18 +19,16 @@ public async Task GetDeviceDefinitionAsync(
ushort vendorId,
uint deviceId,
string productId,
- CancellationToken cancellationToken = default
+ CancellationToken cancellationToken
)
{
- var ioddPackage = await _ioddProvider.GetIODDPackageAsync(
- vendorId,
- deviceId,
- productId,
- cancellationToken
- );
+ var ioddPackage = await _ioddProvider
+ .GetIODDPackageAsync(vendorId, deviceId, productId, cancellationToken)
+ .ConfigureAwait(false);
using var zipArchive = new ZipArchive(ioddPackage, ZipArchiveMode.Read);
- var ioddXml = await FindMainIoddEntryAsync(zipArchive, cancellationToken);
+ var ioddXml = await FindMainIoddEntryAsync(zipArchive, cancellationToken)
+ .ConfigureAwait(false);
return _ioddParser.Parse(
ioddXml.Root ?? throw new InvalidOperationException("No root element found")
@@ -49,7 +47,9 @@ CancellationToken cancellationToken
foreach (var xmlFile in xmlFiles)
{
using var xmlFileStream = xmlFile.Open();
- var xml = await XDocument.LoadAsync(xmlFileStream, LoadOptions.None, cancellationToken);
+ var xml = await XDocument
+ .LoadAsync(xmlFileStream, LoadOptions.None, cancellationToken)
+ .ConfigureAwait(false);
if (IODDParser.IsIODDFile(xml))
{
return xml;
diff --git a/src/IOLink.NET.IODD/Provider/IODDFinderPublicClient.cs b/src/IOLink.NET.IODD/Provider/IODDFinderPublicClient.cs
index 132d836..b4242dd 100644
--- a/src/IOLink.NET.IODD/Provider/IODDFinderPublicClient.cs
+++ b/src/IOLink.NET.IODD/Provider/IODDFinderPublicClient.cs
@@ -1,4 +1,3 @@
-using System.Net.Http;
using System.Net.Http.Json;
using IOLink.NET.IODD.Provider.Data;
@@ -34,12 +33,15 @@ public async Task GetIODDPackageAsync(
ushort vendorId,
uint deviceId,
string productId,
- CancellationToken cancellationToken = default
+ CancellationToken cancellationToken
)
{
- var entries = await _httpClient.GetFromJsonAsync(
- $"api/drivers?status=APPROVED&status=UPLOADED&vendorId={vendorId}&deviceId={deviceId}&productId={productId}"
- );
+ var entries = await _httpClient
+ .GetFromJsonAsync(
+ $"api/drivers?status=APPROVED&status=UPLOADED&vendorId={vendorId}&deviceId={deviceId}&productId={productId}",
+ cancellationToken
+ )
+ .ConfigureAwait(false);
if (entries is null)
{
throw new InvalidOperationException("Could not deserialize response");
@@ -47,9 +49,12 @@ public async Task GetIODDPackageAsync(
if (entries.Content.Count() < 1)
{
- entries = await _httpClient.GetFromJsonAsync(
- $"api/drivers?status=APPROVED&status=UPLOADED&vendorId={vendorId}&deviceId={deviceId}"
- );
+ entries = await _httpClient
+ .GetFromJsonAsync(
+ $"api/drivers?status=APPROVED&status=UPLOADED&vendorId={vendorId}&deviceId={deviceId}",
+ cancellationToken
+ )
+ .ConfigureAwait(false);
if (entries?.Content.Count() < 1)
{
@@ -59,9 +64,12 @@ public async Task GetIODDPackageAsync(
var entry = entries.Content.OrderByDescending(x => x.IoLinkRev).First();
- var zipStream = await _httpClient.GetStreamAsync(
- $"api/vendors/{vendorId}/iodds/{entry.IoddId}/files/zip/rated"
- );
+ var zipStream = await _httpClient
+ .GetStreamAsync(
+ $"api/vendors/{vendorId}/iodds/{entry.IoddId}/files/zip/rated",
+ cancellationToken
+ )
+ .ConfigureAwait(false);
return zipStream;
}
diff --git a/src/IOLink.NET.IODD/Standard/Structure/StandardMenuUserRoleReader.cs b/src/IOLink.NET.IODD/Standard/Structure/StandardMenuUserRoleReader.cs
index 3ef76ae..f02eb47 100644
--- a/src/IOLink.NET.IODD/Standard/Structure/StandardMenuUserRoleReader.cs
+++ b/src/IOLink.NET.IODD/Standard/Structure/StandardMenuUserRoleReader.cs
@@ -1,6 +1,5 @@
using System.Reflection;
using System.Xml.Serialization;
-using IOLink.NET.IODD.Standard.Definition;
using IOLink.NET.IODD.Standard.Definition.IODDMenuUserRoleDefinitions;
namespace IOLink.NET.IODD.Standard.Structure;
diff --git a/src/IOLink.NET.IODD/Structure/Interfaces/IIODevice.cs b/src/IOLink.NET.IODD/Structure/Interfaces/IIODevice.cs
index 2d631dc..aac25b0 100644
--- a/src/IOLink.NET.IODD/Structure/Interfaces/IIODevice.cs
+++ b/src/IOLink.NET.IODD/Structure/Interfaces/IIODevice.cs
@@ -1,10 +1,9 @@
using IOLink.NET.IODD.Structure.Datatypes;
using IOLink.NET.IODD.Structure.Interfaces.ExternalTextCollection;
using IOLink.NET.IODD.Structure.Interfaces.Profile;
-using IOLink.NET.IODD.Structure.Profile;
-using IOLink.NET.IODD.Structure.Structure.ExternalTextCollection;
namespace IOLink.NET.IODD.Structure.Interfaces;
+
public interface IIODevice
{
IProfileBodyT ProfileBody { get; }
diff --git a/src/IOLink.NET.IODD/Structure/Structure/Menu/UserInterfaceT.cs b/src/IOLink.NET.IODD/Structure/Structure/Menu/UserInterfaceT.cs
index c99637c..631c353 100644
--- a/src/IOLink.NET.IODD/Structure/Structure/Menu/UserInterfaceT.cs
+++ b/src/IOLink.NET.IODD/Structure/Structure/Menu/UserInterfaceT.cs
@@ -1,5 +1,10 @@
-using IOLink.NET.IODD.Structure.Interfaces;
using IOLink.NET.IODD.Structure.Interfaces.Menu;
namespace IOLink.NET.IODD.Structure.Structure.Menu;
-public record UserInterfaceT(IEnumerable MenuCollection, IMenuSetT ObserverRoleMenuSet, IMenuSetT MaintenanceRoleMenuSet, IMenuSetT SpecialistRoleMenuSet): IUserInterfaceT;
+
+public record UserInterfaceT(
+ IEnumerable MenuCollection,
+ IMenuSetT ObserverRoleMenuSet,
+ IMenuSetT MaintenanceRoleMenuSet,
+ IMenuSetT SpecialistRoleMenuSet
+) : IUserInterfaceT;
diff --git a/src/IOLink.NET.Vendors.Ifm/Data/IfmIoTCoreResponseBase.cs b/src/IOLink.NET.Vendors.Ifm/Data/IfmIoTCoreResponseBase.cs
index 3c3fd3c..b33ff82 100644
--- a/src/IOLink.NET.Vendors.Ifm/Data/IfmIoTCoreResponseBase.cs
+++ b/src/IOLink.NET.Vendors.Ifm/Data/IfmIoTCoreResponseBase.cs
@@ -1,4 +1,3 @@
-using System.Text.Json;
using System.Text.Json.Nodes;
namespace IOLink.NET.Vendors.Ifm.Data;
@@ -7,12 +6,18 @@ public record IfmIoTCoreResponseBase(T Data, int Cid, int Code);
public record IfmIoTCoreValueWrapper(T Value);
-public record IfmIoTCoreScalarResponse(IfmIoTCoreValueWrapper Data, int Cid, int Code) : IfmIoTCoreResponseBase>(Data, Cid, Code);
+public record IfmIoTCoreScalarResponse(IfmIoTCoreValueWrapper Data, int Cid, int Code)
+ : IfmIoTCoreResponseBase>(Data, Cid, Code);
-public record IfmIoTCoreComplexResponse(T Data, int Cid, int Code) : IfmIoTCoreResponseBase(Data, Cid, Code);
+public record IfmIoTCoreComplexResponse(T Data, int Cid, int Code)
+ : IfmIoTCoreResponseBase(Data, Cid, Code);
public record IfmIoTCoreGetDataMultiEntry(int Code, JsonValue Data);
-public record IfmIoTCorePortTreeResponse(IfmIoTCoreTreeStructure Data, int Cid, int Code) : IfmIoTCoreComplexResponse(Data, Cid, Code);
+public record IfmIoTCorePortTreeResponse(IfmIoTCoreTreeStructure Data, int Cid, int Code)
+ : IfmIoTCoreComplexResponse(Data, Cid, Code);
-public record IfmIoTCoreTreeStructure(IEnumerable? Subs, string Identifier);
+public record IfmIoTCoreTreeStructure(
+ IEnumerable? Subs,
+ string Identifier
+);
diff --git a/src/IOLink.NET.Vendors.Ifm/IfmIoTCoreMasterConnection.cs b/src/IOLink.NET.Vendors.Ifm/IfmIoTCoreMasterConnection.cs
index e289333..d673a32 100644
--- a/src/IOLink.NET.Vendors.Ifm/IfmIoTCoreMasterConnection.cs
+++ b/src/IOLink.NET.Vendors.Ifm/IfmIoTCoreMasterConnection.cs
@@ -14,12 +14,11 @@ public IfmIotCoreMasterConnection(IIfmIoTCoreClient client)
_client = client;
}
- public async Task GetPortCountAsync(CancellationToken cancellationToken = default)
+ public async Task GetPortCountAsync(CancellationToken cancellationToken)
{
- var portTree = await _client.GetPortTreeAsync(
- new IfmIoTGetPortTreeRequest(),
- cancellationToken
- );
+ var portTree = await _client
+ .GetPortTreeAsync(new IfmIoTGetPortTreeRequest(), cancellationToken)
+ .ConfigureAwait(false);
if (portTree.Data.Subs == null)
{
@@ -31,7 +30,7 @@ public async Task GetPortCountAsync(CancellationToken cancellationToken =
public async Task GetPortInformationAsync(
byte portNumber,
- CancellationToken cancellationToken = default
+ CancellationToken cancellationToken
)
{
var statusPath = IfmIoTCoreServicePathBuilder.PortDeviceStatusPath(portNumber);
@@ -44,21 +43,23 @@ public async Task GetPortInformationAsync(
var modePath = IfmIoTCoreServicePathBuilder.PortModePath(portNumber);
var comSpeedPath = IfmIoTCoreServicePathBuilder.PortComSpeedPath(portNumber);
- var resp = await _client.GetDataMultiAsync(
- new IfmIoTGetDataMultiRequest(
- new[]
- {
- statusPath,
- productNamePath,
- vendorIdPath,
- deviceIdPath,
- masterCycleTimeActualPath,
- modePath,
- comSpeedPath,
- }
- ),
- cancellationToken
- );
+ var resp = await _client
+ .GetDataMultiAsync(
+ new IfmIoTGetDataMultiRequest(
+ new[]
+ {
+ statusPath,
+ productNamePath,
+ vendorIdPath,
+ deviceIdPath,
+ masterCycleTimeActualPath,
+ modePath,
+ comSpeedPath,
+ }
+ ),
+ cancellationToken
+ )
+ .ConfigureAwait(false);
var mode = resp.Data[modePath].Data.Deserialize();
var status = resp.Data[statusPath].Data.Deserialize();
@@ -88,28 +89,30 @@ public async Task GetPortInformationAsync(
}
public async Task GetPortInformationsAsync(
- CancellationToken cancellationToken = default
+ CancellationToken cancellationToken
)
{
var tasks = new List>();
- for (byte i = 1; i <= await GetPortCountAsync(); i++)
+ for (byte i = 1; i <= await GetPortCountAsync(cancellationToken).ConfigureAwait(false); i++)
{
tasks.Add(GetPortInformationAsync(i, cancellationToken));
}
- return await Task.WhenAll(tasks);
+ return await Task.WhenAll(tasks).ConfigureAwait(false);
}
public async Task> ReadIndexAsync(
byte portNumber,
ushort index,
- byte subIndex = 0,
- CancellationToken cancellationToken = default
+ CancellationToken cancellationToken,
+ byte subIndex = 0
)
{
- var resp = await _client.GetDeviceAcyclicDataAsync(
- new IfmIoTReadAcyclicRequest(portNumber, index, subIndex),
- cancellationToken
- );
+ var resp = await _client
+ .GetDeviceAcyclicDataAsync(
+ new IfmIoTReadAcyclicRequest(portNumber, index, subIndex),
+ cancellationToken
+ )
+ .ConfigureAwait(false);
if (resp?.Data == null)
{
return null;
@@ -120,25 +123,23 @@ public async Task> ReadIndexAsync(
public async Task> ReadProcessDataInAsync(
byte portNumber,
- CancellationToken cancellationToken = default
+ CancellationToken cancellationToken
)
{
- var resp = await _client.GetDevicePdinDataAsync(
- new IfmIoTReadPdInRequest(portNumber),
- cancellationToken
- );
+ var resp = await _client
+ .GetDevicePdinDataAsync(new IfmIoTReadPdInRequest(portNumber), cancellationToken)
+ .ConfigureAwait(false);
return Convert.FromHexString(resp.Data.Value);
}
public async Task> ReadProcessDataOutAsync(
byte portNumber,
- CancellationToken cancellationToken = default
+ CancellationToken cancellationToken
)
{
- var resp = await _client.GetDevicePdoutDataAsync(
- new IfmIoTReadPdOutRequest(portNumber),
- cancellationToken
- );
+ var resp = await _client
+ .GetDevicePdoutDataAsync(new IfmIoTReadPdOutRequest(portNumber), cancellationToken)
+ .ConfigureAwait(false);
return Convert.FromHexString(resp.Data.Value);
}
}
diff --git a/src/IOLink.NET.Visualization/IODDConversion/IODDUserInterfaceConverter.cs b/src/IOLink.NET.Visualization/IODDConversion/IODDUserInterfaceConverter.cs
index 9d6c25a..2f036af 100644
--- a/src/IOLink.NET.Visualization/IODDConversion/IODDUserInterfaceConverter.cs
+++ b/src/IOLink.NET.Visualization/IODDConversion/IODDUserInterfaceConverter.cs
@@ -1,6 +1,6 @@
+using IOLink.NET.Core.Contracts;
using IOLink.NET.Integration;
using IOLink.NET.IODD.Standard.Structure;
-using IOLink.NET.IODD.Structure;
using IOLink.NET.IODD.Structure.Interfaces;
using IOLink.NET.IODD.Structure.Interfaces.Menu;
using IOLink.NET.IODD.Structure.Structure.Menu;
@@ -11,10 +11,13 @@ namespace IOLink.NET.Visualization.IODDConversion;
public class IODDUserInterfaceConverter
{
private readonly IIODevice _ioDevice;
- private readonly IODDPortReader _ioddPortReader;
+ private readonly IIODDPortReader _ioddPortReader;
private readonly IUserInterfaceT _userInterface;
- public IODDUserInterfaceConverter(IIODevice ioDevice, IODDPortReader ioddPortReader)
+ public IODDUserInterfaceConverter(IODDPortReader ioddPortReader)
+ : this(ioddPortReader.Device, ioddPortReader) { }
+
+ public IODDUserInterfaceConverter(IIODevice ioDevice, IIODDPortReader ioddPortReader)
{
_ioDevice = ioDevice;
_ioddPortReader = ioddPortReader;
diff --git a/src/IOLink.NET.Visualization/Menu/MenuDataReader.cs b/src/IOLink.NET.Visualization/Menu/MenuDataReader.cs
index 71ad65d..976a9d9 100644
--- a/src/IOLink.NET.Visualization/Menu/MenuDataReader.cs
+++ b/src/IOLink.NET.Visualization/Menu/MenuDataReader.cs
@@ -3,7 +3,6 @@
using IOLink.NET.IODD.Structure.Interfaces.Menu;
using IOLink.NET.Visualization.IODDConversion;
using IOLink.NET.Visualization.Structure.Structure;
-using static IOLink.NET.Integration.IODDPortReader;
namespace IOLink.NET.Visualization.Menu
{
@@ -18,9 +17,11 @@ public MenuDataReader(IODDPortReader ioddPortReader)
_ioddPortReader = ioddPortReader;
}
- public async Task InitializeForPortAsync(byte port)
+ public async Task InitializeForPortAsync(byte port, CancellationToken cancellationToken)
{
- await _ioddPortReader.InitializeForPortAsync(port);
+ await _ioddPortReader
+ .InitializeForPortAsync(port, cancellationToken)
+ .ConfigureAwait(false);
_device = _ioddPortReader.Device;
_iODDUserInterfaceConverter = new(_device, _ioddPortReader);
}
diff --git a/src/IOLink.NET.Visualization/Structure/Interfaces/IReadable.cs b/src/IOLink.NET.Visualization/Structure/Interfaces/IReadable.cs
index b9f2314..1c4a32c 100644
--- a/src/IOLink.NET.Visualization/Structure/Interfaces/IReadable.cs
+++ b/src/IOLink.NET.Visualization/Structure/Interfaces/IReadable.cs
@@ -1,8 +1,9 @@
-using IOLink.NET.Integration;
+using IOLink.NET.Core.Contracts;
namespace IOLink.NET.Visualization.Structure.Interfaces;
+
internal interface IReadable
{
- IODDPortReader IoddPortReader { get; }
- Task ReadAsync();
+ IIODDPortReader IoddPortReader { get; }
+ Task ReadAsync(CancellationToken cancellationToken);
}
diff --git a/src/IOLink.NET.Visualization/Structure/Structure/MenuSet.cs b/src/IOLink.NET.Visualization/Structure/Structure/MenuSet.cs
index b04e070..54d7ce8 100644
--- a/src/IOLink.NET.Visualization/Structure/Structure/MenuSet.cs
+++ b/src/IOLink.NET.Visualization/Structure/Structure/MenuSet.cs
@@ -1,26 +1,33 @@
-using IOLink.NET.Integration;
+using IOLink.NET.Core.Contracts;
using IOLink.NET.Visualization.Structure.Interfaces;
namespace IOLink.NET.Visualization.Structure.Structure;
-public record MenuSet(UIMenu IdentificationMenu, UIMenu? ParameterMenu, UIMenu? ObservationMenu, UIMenu? DiagnosisMenu, IODDPortReader IoddPortReader) : IReadable
+
+public record MenuSet(
+ UIMenu IdentificationMenu,
+ UIMenu? ParameterMenu,
+ UIMenu? ObservationMenu,
+ UIMenu? DiagnosisMenu,
+ IIODDPortReader IoddPortReader
+) : IReadable
{
- public async Task ReadAsync()
+ public async Task ReadAsync(CancellationToken cancellationToken)
{
- await IdentificationMenu.ReadAsync();
+ await IdentificationMenu.ReadAsync(cancellationToken).ConfigureAwait(false);
if (ParameterMenu is not null)
{
- await ParameterMenu.ReadAsync();
+ await ParameterMenu.ReadAsync(cancellationToken).ConfigureAwait(false);
}
if (ObservationMenu is not null)
{
- await ObservationMenu.ReadAsync();
+ await ObservationMenu.ReadAsync(cancellationToken).ConfigureAwait(false);
}
if (DiagnosisMenu is not null)
{
- await DiagnosisMenu.ReadAsync();
+ await DiagnosisMenu.ReadAsync(cancellationToken).ConfigureAwait(false);
}
}
}
diff --git a/src/IOLink.NET.Visualization/Structure/Structure/RoleMenu.cs b/src/IOLink.NET.Visualization/Structure/Structure/RoleMenu.cs
index 371a289..3f0ca58 100644
--- a/src/IOLink.NET.Visualization/Structure/Structure/RoleMenu.cs
+++ b/src/IOLink.NET.Visualization/Structure/Structure/RoleMenu.cs
@@ -1,27 +1,33 @@
-using IOLink.NET.Integration;
+using IOLink.NET.Core.Contracts;
using IOLink.NET.Visualization.Structure.Interfaces;
namespace IOLink.NET.Visualization.Structure.Structure
{
- public record RoleMenu(UIMenu IdentificationMenu, UIMenu? ParameterMenu, UIMenu? ObservationMenu, UIMenu? DiagnosisMenu, IODDPortReader IoddPortReader) : IReadable
+ public record RoleMenu(
+ UIMenu IdentificationMenu,
+ UIMenu? ParameterMenu,
+ UIMenu? ObservationMenu,
+ UIMenu? DiagnosisMenu,
+ IIODDPortReader IoddPortReader
+ ) : IReadable
{
- public async Task ReadAsync()
+ public async Task ReadAsync(CancellationToken cancellationToken)
{
- await IdentificationMenu.ReadAsync();
+ await IdentificationMenu.ReadAsync(cancellationToken).ConfigureAwait(false);
if (ParameterMenu is not null)
{
- await ParameterMenu.ReadAsync();
+ await ParameterMenu.ReadAsync(cancellationToken).ConfigureAwait(false);
}
if (ObservationMenu is not null)
{
- await ObservationMenu.ReadAsync();
+ await ObservationMenu.ReadAsync(cancellationToken).ConfigureAwait(false);
}
if (DiagnosisMenu is not null)
{
- await DiagnosisMenu.ReadAsync();
+ await DiagnosisMenu.ReadAsync(cancellationToken).ConfigureAwait(false);
}
}
}
diff --git a/src/IOLink.NET.Visualization/Structure/Structure/UIInterface.cs b/src/IOLink.NET.Visualization/Structure/Structure/UIInterface.cs
index 41b959c..4dd45f1 100644
--- a/src/IOLink.NET.Visualization/Structure/Structure/UIInterface.cs
+++ b/src/IOLink.NET.Visualization/Structure/Structure/UIInterface.cs
@@ -1,13 +1,19 @@
-using IOLink.NET.Integration;
+using IOLink.NET.Core.Contracts;
using IOLink.NET.Visualization.Structure.Interfaces;
namespace IOLink.NET.Visualization.Structure.Structure;
-public record UIInterface(MenuSet ObserverRoleMenu, MenuSet MaintenanceRoleMenu, MenuSet SpecialistRoleMenu, IODDPortReader IoddPortReader) : IReadable
+
+public record UIInterface(
+ MenuSet ObserverRoleMenu,
+ MenuSet MaintenanceRoleMenu,
+ MenuSet SpecialistRoleMenu,
+ IIODDPortReader IoddPortReader
+) : IReadable
{
- public async Task ReadAsync()
+ public async Task ReadAsync(CancellationToken cancellationToken)
{
- await ObserverRoleMenu.ReadAsync();
- await MaintenanceRoleMenu.ReadAsync();
- await SpecialistRoleMenu.ReadAsync();
+ await ObserverRoleMenu.ReadAsync(cancellationToken).ConfigureAwait(false);
+ await MaintenanceRoleMenu.ReadAsync(cancellationToken).ConfigureAwait(false);
+ await SpecialistRoleMenu.ReadAsync(cancellationToken).ConfigureAwait(false);
}
}
diff --git a/src/IOLink.NET.Visualization/Structure/Structure/UIMenu.cs b/src/IOLink.NET.Visualization/Structure/Structure/UIMenu.cs
index 9a6c91d..34530f6 100644
--- a/src/IOLink.NET.Visualization/Structure/Structure/UIMenu.cs
+++ b/src/IOLink.NET.Visualization/Structure/Structure/UIMenu.cs
@@ -1,17 +1,26 @@
-using IOLink.NET.Integration;
+using IOLink.NET.Core.Contracts;
using IOLink.NET.IODD.Structure.ProcessData;
using IOLink.NET.Visualization.Structure.Interfaces;
namespace IOLink.NET.Visualization.Structure.Structure;
-public record UIMenu(string Id, string? Name, ConditionT? Condition, IEnumerable? Variables, IEnumerable? SubMenus, IEnumerable? RecordItems, IODDPortReader IoddPortReader) : IReadable
+
+public record UIMenu(
+ string Id,
+ string? Name,
+ ConditionT? Condition,
+ IEnumerable? Variables,
+ IEnumerable? SubMenus,
+ IEnumerable? RecordItems,
+ IIODDPortReader IoddPortReader
+) : IReadable
{
- public async Task ReadAsync()
+ public async Task ReadAsync(CancellationToken cancellationToken)
{
if (Variables is not null)
{
foreach (UIVariable variable in Variables)
{
- await variable.ReadAsync();
+ await variable.ReadAsync(cancellationToken).ConfigureAwait(false);
}
}
@@ -19,7 +28,7 @@ public async Task ReadAsync()
{
foreach (UIMenu subMenu in SubMenus)
{
- await subMenu.ReadAsync();
+ await subMenu.ReadAsync(cancellationToken).ConfigureAwait(false);
}
}
@@ -27,7 +36,7 @@ public async Task ReadAsync()
{
foreach (UIRecordItem recordItem in RecordItems)
{
- await recordItem.ReadAsync();
+ await recordItem.ReadAsync(cancellationToken).ConfigureAwait(false);
}
}
}
diff --git a/src/IOLink.NET.Visualization/Structure/Structure/UIRecordItem.cs b/src/IOLink.NET.Visualization/Structure/Structure/UIRecordItem.cs
index 0039da5..d4c2d1a 100644
--- a/src/IOLink.NET.Visualization/Structure/Structure/UIRecordItem.cs
+++ b/src/IOLink.NET.Visualization/Structure/Structure/UIRecordItem.cs
@@ -1,16 +1,27 @@
-using IOLink.NET.Integration;
+using IOLink.NET.Core.Contracts;
using IOLink.NET.IODD.Structure.Datatypes;
using IOLink.NET.IODD.Structure.DeviceFunction;
using IOLink.NET.IODD.Structure.Structure.Datatypes;
using IOLink.NET.Visualization.Structure.Interfaces;
namespace IOLink.NET.Visualization.Structure.Structure;
-public record UIRecordItem(string VariableId, VariableT? Variable, byte SubIndex, decimal? Gradient, decimal? Offset, uint? UnitCode, AccessRightsT? AccessRights, string? ButtonValue, DisplayFormat? DisplayFormat, IODDPortReader IoddPortReader) : IReadable
+
+public record UIRecordItem(
+ string VariableId,
+ VariableT? Variable,
+ byte SubIndex,
+ decimal? Gradient,
+ decimal? Offset,
+ uint? UnitCode,
+ AccessRightsT? AccessRights,
+ string? ButtonValue,
+ DisplayFormat? DisplayFormat,
+ IIODDPortReader IoddPortReader
+) : IReadable
{
public object? Value;
-
- public async Task ReadAsync()
+ public async Task ReadAsync(CancellationToken cancellationToken)
{
if (Variable == null)
{
@@ -19,15 +30,21 @@ public async Task ReadAsync()
if (VariableId == "V_ProcessDataInput")
{
- Value = await IoddPortReader.ReadConvertedProcessDataInAsync();
+ Value = await IoddPortReader
+ .ReadConvertedProcessDataInResultAsync(cancellationToken)
+ .ConfigureAwait(false);
}
else if (VariableId == "V_ProcessDataOutput")
{
- Value = await IoddPortReader.ReadConvertedProcessDataOutAsync();
+ Value = await IoddPortReader
+ .ReadConvertedProcessDataOutResultAsync(cancellationToken)
+ .ConfigureAwait(false);
}
else
{
- Value = await IoddPortReader.ReadConvertedParameterAsync(Variable.Index, SubIndex);
+ Value = await IoddPortReader
+ .ReadConvertedParameterResultAsync(Variable.Index, SubIndex, cancellationToken)
+ .ConfigureAwait(false);
}
}
}
diff --git a/src/IOLink.NET.Visualization/Structure/Structure/UIVariable.cs b/src/IOLink.NET.Visualization/Structure/Structure/UIVariable.cs
index 5cdeb70..85ca1a6 100644
--- a/src/IOLink.NET.Visualization/Structure/Structure/UIVariable.cs
+++ b/src/IOLink.NET.Visualization/Structure/Structure/UIVariable.cs
@@ -1,21 +1,34 @@
-using IOLink.NET.Integration;
+using IOLink.NET.Core.Contracts;
using IOLink.NET.IODD.Structure.Datatypes;
using IOLink.NET.IODD.Structure.DeviceFunction;
using IOLink.NET.IODD.Structure.Structure.Datatypes;
using IOLink.NET.Visualization.Structure.Interfaces;
namespace IOLink.NET.Visualization.Structure.Structure;
-public record UIVariable(string VariableId, VariableT? Variable, decimal? Gradient, decimal? Offset, uint? UnitCode, AccessRightsT? AccessRights, string? ButtonValue, DisplayFormat? DisplayFormat, IODDPortReader IoddPortReader) : IReadable
+
+public record UIVariable(
+ string VariableId,
+ VariableT? Variable,
+ decimal? Gradient,
+ decimal? Offset,
+ uint? UnitCode,
+ AccessRightsT? AccessRights,
+ string? ButtonValue,
+ DisplayFormat? DisplayFormat,
+ IIODDPortReader IoddPortReader
+) : IReadable
{
public object? Value;
- public async Task ReadAsync()
+ public async Task ReadAsync(CancellationToken cancellationToken)
{
if (Variable == null)
{
return;
}
- Value = await IoddPortReader.ReadConvertedParameterAsync(Variable.Index, 0);
+ Value = await IoddPortReader
+ .ReadConvertedParameterResultAsync(Variable.Index, 0, cancellationToken)
+ .ConfigureAwait(false);
}
}
diff --git a/src/IOLink.NET.sln b/src/IOLink.NET.sln
index 1379fe2..48d664f 100644
--- a/src/IOLink.NET.sln
+++ b/src/IOLink.NET.sln
@@ -21,6 +21,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Vendors.Ifm", "Tests\Vendor
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "IOLink.NET.Core.Tests", "Tests\IOLink.NET.Core.Tests\IOLink.NET.Core.Tests.csproj", "{03519BC5-67C4-40C7-96F8-218B9C4DE1D2}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "IOLink.NET.ConsoleRunner", "Tests\IOLink.NET.ConsoleRunner\IOLink.NET.ConsoleRunner.csproj", "{F6F4E3A7-3A53-428E-8223-F3D66636C5EF}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -127,6 +129,18 @@ Global
{03519BC5-67C4-40C7-96F8-218B9C4DE1D2}.Release|x64.Build.0 = Release|Any CPU
{03519BC5-67C4-40C7-96F8-218B9C4DE1D2}.Release|x86.ActiveCfg = Release|Any CPU
{03519BC5-67C4-40C7-96F8-218B9C4DE1D2}.Release|x86.Build.0 = Release|Any CPU
+ {F6F4E3A7-3A53-428E-8223-F3D66636C5EF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {F6F4E3A7-3A53-428E-8223-F3D66636C5EF}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {F6F4E3A7-3A53-428E-8223-F3D66636C5EF}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {F6F4E3A7-3A53-428E-8223-F3D66636C5EF}.Debug|x64.Build.0 = Debug|Any CPU
+ {F6F4E3A7-3A53-428E-8223-F3D66636C5EF}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {F6F4E3A7-3A53-428E-8223-F3D66636C5EF}.Debug|x86.Build.0 = Debug|Any CPU
+ {F6F4E3A7-3A53-428E-8223-F3D66636C5EF}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {F6F4E3A7-3A53-428E-8223-F3D66636C5EF}.Release|Any CPU.Build.0 = Release|Any CPU
+ {F6F4E3A7-3A53-428E-8223-F3D66636C5EF}.Release|x64.ActiveCfg = Release|Any CPU
+ {F6F4E3A7-3A53-428E-8223-F3D66636C5EF}.Release|x64.Build.0 = Release|Any CPU
+ {F6F4E3A7-3A53-428E-8223-F3D66636C5EF}.Release|x86.ActiveCfg = Release|Any CPU
+ {F6F4E3A7-3A53-428E-8223-F3D66636C5EF}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -135,5 +149,6 @@ Global
{7FD3341D-CD95-4C92-B393-01499B33E713} = {0AB3BF05-4346-4AA6-1389-037BE0695223}
{BC34E0AF-56D8-4ED0-8A3C-575219A0D922} = {0AB3BF05-4346-4AA6-1389-037BE0695223}
{03519BC5-67C4-40C7-96F8-218B9C4DE1D2} = {0AB3BF05-4346-4AA6-1389-037BE0695223}
+ {F6F4E3A7-3A53-428E-8223-F3D66636C5EF} = {0AB3BF05-4346-4AA6-1389-037BE0695223}
EndGlobalSection
EndGlobal
diff --git a/src/IOLink.NET/Integration/ConversionResultWrapper.cs b/src/IOLink.NET/Integration/ConversionResultWrapper.cs
new file mode 100644
index 0000000..4a60fb2
--- /dev/null
+++ b/src/IOLink.NET/Integration/ConversionResultWrapper.cs
@@ -0,0 +1,102 @@
+using IOLink.NET.Core.Models;
+
+namespace IOLink.NET.Integration;
+
+///
+/// Responsible for wrapping conversion results into strongly-typed ConversionResult objects.
+///
+public class ConversionResultWrapper
+{
+ ///
+ /// Wraps a converted object into a ConversionResult that distinguishes between scalar and complex values.
+ ///
+ /// The object to wrap.
+ /// A ConversionResult containing either a ScalarResult or ComplexResult.
+ public ConversionResult WrapConversionResult(object? convertedValue)
+ {
+ if (convertedValue is null)
+ {
+ // null is treated as a scalar value
+ return new ScalarResult(null!, typeof(object));
+ }
+
+ var type = convertedValue.GetType();
+
+ // Check if it's a scalar type
+ if (IsScalarType(type))
+ {
+ return new ScalarResult(convertedValue, type);
+ }
+
+ // If it's a complex type, try to extract key-value pairs
+ if (convertedValue is IDictionary dictionary)
+ {
+ var values = dictionary
+ .Select(kvp =>
+ {
+ var scalarResult =
+ kvp.Value is null ? new ScalarResult(null!, typeof(object))
+ : IsScalarType(kvp.Value.GetType())
+ ? new ScalarResult(kvp.Value, kvp.Value.GetType())
+ : new ScalarResult(kvp.Value, kvp.Value.GetType()); // Complex nested values treated as scalar for now
+
+ return new KeyValuePair(kvp.Key, scalarResult);
+ })
+ .ToList();
+
+ return new ComplexResult(values);
+ }
+
+ // Handle tuple-based complex types (like from IODD conversion)
+ if (convertedValue is IEnumerable<(string key, object value)> tupleEnumerable)
+ {
+ var values = tupleEnumerable
+ .Select(tuple =>
+ {
+ var scalarResult =
+ tuple.value is null ? new ScalarResult(null!, typeof(object))
+ : IsScalarType(tuple.value.GetType())
+ ? new ScalarResult(tuple.value, tuple.value.GetType())
+ : new ScalarResult(tuple.value, tuple.value.GetType());
+
+ return new KeyValuePair(tuple.key, scalarResult);
+ })
+ .ToList();
+
+ return new ComplexResult(values);
+ }
+
+ // For other complex types, use reflection to extract properties as key-value pairs
+ var properties = type.GetProperties()
+ .Where(p => p.CanRead)
+ .Select(p =>
+ {
+ var value = p.GetValue(convertedValue);
+ var scalarResult = value is null
+ ? new ScalarResult(null!, typeof(object))
+ : new ScalarResult(value, value.GetType());
+
+ return new KeyValuePair(p.Name, scalarResult);
+ })
+ .ToList();
+
+ return new ComplexResult(properties);
+ }
+
+ private static bool IsScalarType(Type type)
+ {
+ return type.IsPrimitive
+ || type.IsEnum
+ || type == typeof(string)
+ || type == typeof(decimal)
+ || type == typeof(DateTime)
+ || type == typeof(DateTimeOffset)
+ || type == typeof(TimeSpan)
+ || type == typeof(Guid)
+ || (
+ type.IsGenericType
+ && type.GetGenericTypeDefinition() == typeof(Nullable<>)
+ && IsScalarType(Nullable.GetUnderlyingType(type)!)
+ );
+ }
+}
diff --git a/src/IOLink.NET/Integration/IODDPortReader.cs b/src/IOLink.NET/Integration/IODDPortReader.cs
index 059aee3..9761ba2 100644
--- a/src/IOLink.NET/Integration/IODDPortReader.cs
+++ b/src/IOLink.NET/Integration/IODDPortReader.cs
@@ -1,19 +1,23 @@
using IOLink.NET.Conversion;
using IOLink.NET.Core.Contracts;
+using IOLink.NET.Core.Models;
using IOLink.NET.IODD.Provider;
-using IOLink.NET.IODD.Resolution;
using IOLink.NET.IODD.Resolution.Contracts;
using IOLink.NET.IODD.Structure;
namespace IOLink.NET.Integration;
-public class IODDPortReader
+///
+/// Main orchestrator for reading IODD data from IO-Link devices.
+/// This class coordinates between port initialization, parameter reading, and process data reading.
+///
+public class IODDPortReader : IIODDPortReader
{
- private readonly IMasterConnection _connection;
- private readonly IDeviceDefinitionProvider _deviceDefinitionProvider;
- private readonly IIoddDataConverter _ioddDataConverter;
- private readonly ITypeResolverFactory _typeResolverFactory;
- private PortReaderInitilizationResult? _initilizationState;
+ private readonly PortInitializer _portInitializer;
+ private readonly ParameterDataReader _parameterDataReader;
+ private readonly ProcessDataReader _processDataReader;
+
+ private PortContext? _portContext;
public IODDPortReader(
IMasterConnection connection,
@@ -22,134 +26,105 @@ public IODDPortReader(
ITypeResolverFactory typeResolverFactory
)
{
- _connection = connection;
- _deviceDefinitionProvider = deviceDefinitionProvider;
- _ioddDataConverter = ioddDataConverter;
- _typeResolverFactory = typeResolverFactory;
+ _portInitializer = new PortInitializer(
+ connection,
+ deviceDefinitionProvider,
+ typeResolverFactory
+ );
+ _parameterDataReader = new ParameterDataReader(connection, ioddDataConverter);
+ _processDataReader = new ProcessDataReader(connection, ioddDataConverter);
}
- private PortReaderInitilizationResult InitilizationState =>
- _initilizationState ?? throw new InvalidOperationException("PortReader is not initialized");
+ private PortContext PortContext =>
+ _portContext ?? throw new InvalidOperationException("PortReader is not initialized");
public IODevice Device
{
get
{
_ =
- _initilizationState
+ _portContext
?? throw new InvalidOperationException("PortReader is not initialized");
- return InitilizationState.DeviceDefinition;
+ return PortContext.DeviceDefinition;
}
}
- public async Task InitializeForPortAsync(byte port)
+ public async Task InitializeForPortAsync(byte port, CancellationToken cancellationToken)
{
- var portInfo = await _connection.GetPortInformationAsync(port);
- if (!portInfo.Status.HasFlag(PortStatus.IOLink))
- {
- throw new InvalidOperationException("Port is not in IO-Link mode");
- }
-
- if (portInfo.DeviceInformation is null)
- {
- throw new InvalidOperationException(
- $"Device information is not available for requested port {port}"
- );
- }
-
- var deviceDefinition = await _deviceDefinitionProvider.GetDeviceDefinitionAsync(
- portInfo.DeviceInformation.VendorId,
- portInfo.DeviceInformation.DeviceId,
- portInfo.DeviceInformation.ProductId
- );
- var pdDataResolver = _typeResolverFactory.CreateProcessDataTypeResolver(deviceDefinition);
- var paramDataResolver = _typeResolverFactory.CreateParameterTypeResolver(deviceDefinition);
+ _portContext = await _portInitializer
+ .InitializePortAsync(port, cancellationToken)
+ .ConfigureAwait(false);
+ }
- var (pdInType, pdOutType) = await GetProcessDataTypesAsync(port, pdDataResolver);
- _initilizationState = new PortReaderInitilizationResult(
- pdInType,
- pdOutType,
- port,
- pdDataResolver,
- paramDataResolver,
- deviceDefinition
+ [Obsolete("Use ReadConvertedParameterResultAsync instead for better type safety.")]
+ public virtual Task