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 ReadConvertedParameterAsync( + ushort index, + byte subindex, + CancellationToken cancellationToken + ) + { + return _parameterDataReader.ReadParameterRawAsync( + PortContext, + index, + subindex, + cancellationToken ); } - public virtual async Task ReadConvertedParameterAsync(ushort index, byte subindex) + /// + /// 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. + public virtual Task ReadConvertedParameterResultAsync( + ushort index, + byte subindex, + CancellationToken cancellationToken + ) { - var paramTypeDef = InitilizationState.ParameterTypeResolver.GetParameter(index, subindex); - - var value = await _connection.ReadIndexAsync( - InitilizationState.Port, + return _parameterDataReader.ReadParameterAsync( + PortContext, index, - paramTypeDef.SubindexAccessSupported ? subindex : (byte)0 + subindex, + cancellationToken ); - - var convertedValue = _ioddDataConverter.Convert(paramTypeDef, value.Span); - - return convertedValue; } - public async Task ReadConvertedProcessDataInAsync() + [Obsolete("Use ReadConvertedProcessDataInResultAsync instead for better type safety.")] + public Task ReadConvertedProcessDataInAsync(CancellationToken cancellationToken) { - if (InitilizationState.PdIn is null) - { - throw new InvalidOperationException("Device has no process data in declared."); - } - - var value = await _connection.ReadProcessDataInAsync(InitilizationState.Port); - var convertedValue = _ioddDataConverter.Convert(InitilizationState.PdIn, value.Span); - - return convertedValue; + return _processDataReader.ReadProcessDataInRawAsync(PortContext, cancellationToken); } - public async Task ReadConvertedProcessDataOutAsync() + /// + /// 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. + public Task ReadConvertedProcessDataInResultAsync( + CancellationToken cancellationToken + ) { - if (InitilizationState.PdOut is null) - { - throw new InvalidOperationException("Device has no process data out declared."); - } - - var value = await _connection.ReadProcessDataOutAsync(InitilizationState.Port); - var convertedValue = _ioddDataConverter.Convert(InitilizationState.PdOut, value.Span); + return _processDataReader.ReadProcessDataInAsync(PortContext, cancellationToken); + } - return convertedValue; + [Obsolete("Use ReadConvertedProcessDataOutResultAsync instead for better type safety.")] + public Task ReadConvertedProcessDataOutAsync(CancellationToken cancellationToken) + { + return _processDataReader.ReadProcessDataOutRawAsync(PortContext, cancellationToken); } - private async Task<(ParsableDatatype? PdIn, ParsableDatatype? PdOut)> GetProcessDataTypesAsync( - byte port, - IProcessDataTypeResolver processDataTypeResolver + /// + /// 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. + public Task ReadConvertedProcessDataOutResultAsync( + CancellationToken cancellationToken ) { - ParsableDatatype? pdInType; - ParsableDatatype? pdOutType; - if (processDataTypeResolver.HasCondition()) - { - var condition = processDataTypeResolver.ResolveCondition(); - var conditionValue = await _connection.ReadIndexAsync( - port, - condition.VariableDef.Index, - condition.ConditionDef.Subindex ?? 0 - ); - pdInType = processDataTypeResolver.ResolveProcessDataIn(conditionValue.Span[0]); - pdOutType = processDataTypeResolver.ResolveProcessDataOut(conditionValue.Span[0]); - } - else - { - pdInType = processDataTypeResolver.ResolveProcessDataIn(); - pdOutType = processDataTypeResolver.ResolveProcessDataOut(); - } - - return (pdInType, pdOutType); + return _processDataReader.ReadProcessDataOutAsync(PortContext, cancellationToken); } - - public record PortReaderInitilizationResult( - ParsableDatatype? PdIn, - ParsableDatatype? PdOut, - byte Port, - IProcessDataTypeResolver ProcessDataTypeResolver, - IParameterTypeResolver ParameterTypeResolver, - IODevice DeviceDefinition - ); } diff --git a/src/IOLink.NET/Integration/ParameterDataReader.cs b/src/IOLink.NET/Integration/ParameterDataReader.cs new file mode 100644 index 0000000..f904a49 --- /dev/null +++ b/src/IOLink.NET/Integration/ParameterDataReader.cs @@ -0,0 +1,82 @@ +using IOLink.NET.Conversion; +using IOLink.NET.Core.Contracts; +using IOLink.NET.Core.Models; + +namespace IOLink.NET.Integration; + +/// +/// Responsible for reading parameter data from an IO-Link device and converting it to typed results. +/// +public class ParameterDataReader +{ + private readonly IMasterConnection _connection; + private readonly IIoddDataConverter _converter; + private readonly ConversionResultWrapper _resultWrapper; + + public ParameterDataReader(IMasterConnection connection, IIoddDataConverter converter) + { + _connection = connection; + _converter = converter; + _resultWrapper = new ConversionResultWrapper(); + } + + /// + /// Reads and converts a parameter value, returning a typed result. + /// + /// The port context. + /// The parameter index. + /// The parameter subindex. + /// A ConversionResult containing either a ScalarResult or ComplexResult. + public async Task ReadParameterAsync( + PortContext context, + ushort index, + byte subindex, + CancellationToken cancellationToken + ) + { + var paramTypeDef = context.ParameterTypeResolver.GetParameter(index, subindex); + + var value = await _connection + .ReadIndexAsync( + context.Port, + index, + cancellationToken, + paramTypeDef.SubindexAccessSupported ? subindex : (byte)0 + ) + .ConfigureAwait(false); + + var convertedValue = _converter.Convert(paramTypeDef, value.Span); + return _resultWrapper.WrapConversionResult(convertedValue); + } + + /// + /// Reads and converts a parameter value, returning the raw object (obsolete method). + /// + /// The port context. + /// The parameter index. + /// The parameter subindex. + /// The cancellation token. + /// The raw converted object. + [Obsolete("Use ReadParameterAsync instead for better type safety.")] + public async Task ReadParameterRawAsync( + PortContext context, + ushort index, + byte subindex, + CancellationToken cancellationToken + ) + { + var paramTypeDef = context.ParameterTypeResolver.GetParameter(index, subindex); + + var value = await _connection + .ReadIndexAsync( + context.Port, + index, + cancellationToken, + paramTypeDef.SubindexAccessSupported ? subindex : (byte)0 + ) + .ConfigureAwait(false); + + var convertedValue = _converter.Convert(paramTypeDef, value.Span); + return convertedValue; + } +} diff --git a/src/IOLink.NET/Integration/PortContext.cs b/src/IOLink.NET/Integration/PortContext.cs new file mode 100644 index 0000000..014da42 --- /dev/null +++ b/src/IOLink.NET/Integration/PortContext.cs @@ -0,0 +1,23 @@ +using IOLink.NET.IODD.Resolution; +using IOLink.NET.IODD.Resolution.Contracts; +using IOLink.NET.IODD.Structure; + +namespace IOLink.NET.Integration; + +/// +/// Contains all the context information for an initialized port. +/// +/// The port number. +/// The resolved device definition. +/// The process data type resolver. +/// The parameter type resolver. +/// The resolved process data input type, if available. +/// The resolved process data output type, if available. +public record PortContext( + byte Port, + IODevice DeviceDefinition, + IProcessDataTypeResolver ProcessDataTypeResolver, + IParameterTypeResolver ParameterTypeResolver, + ParsableDatatype? PdIn, + ParsableDatatype? PdOut +); diff --git a/src/IOLink.NET/Integration/PortInitializer.cs b/src/IOLink.NET/Integration/PortInitializer.cs new file mode 100644 index 0000000..91333be --- /dev/null +++ b/src/IOLink.NET/Integration/PortInitializer.cs @@ -0,0 +1,118 @@ +using IOLink.NET.Core.Contracts; +using IOLink.NET.IODD.Provider; +using IOLink.NET.IODD.Resolution; +using IOLink.NET.IODD.Resolution.Contracts; + +namespace IOLink.NET.Integration; + +/// +/// Responsible for initializing a port by resolving device definitions and type resolvers. +/// +public class PortInitializer +{ + private readonly IMasterConnection _connection; + private readonly IDeviceDefinitionProvider _deviceDefinitionProvider; + private readonly ITypeResolverFactory _typeResolverFactory; + + public PortInitializer( + IMasterConnection connection, + IDeviceDefinitionProvider deviceDefinitionProvider, + ITypeResolverFactory typeResolverFactory + ) + { + _connection = connection; + _deviceDefinitionProvider = deviceDefinitionProvider; + _typeResolverFactory = typeResolverFactory; + } + + /// + /// Initializes the port by getting device information and creating type resolvers. + /// + /// The port number to initialize. + /// A token to monitor for cancellation requests. + /// A PortContext containing all resolved information. + /// Thrown when port is not in IO-Link mode or device information is not available. + public async Task InitializePortAsync( + byte port, + CancellationToken cancellationToken + ) + { + var portInfo = await _connection + .GetPortInformationAsync(port, cancellationToken) + .ConfigureAwait(false); + 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, + cancellationToken + ) + .ConfigureAwait(false); + + var pdDataResolver = _typeResolverFactory.CreateProcessDataTypeResolver(deviceDefinition); + var paramDataResolver = _typeResolverFactory.CreateParameterTypeResolver(deviceDefinition); + + var (pdInType, pdOutType) = await ResolveProcessDataTypesAsync( + port, + pdDataResolver, + cancellationToken + ) + .ConfigureAwait(false); + + return new PortContext( + port, + deviceDefinition, + pdDataResolver, + paramDataResolver, + pdInType, + pdOutType + ); + } + + private async Task<( + ParsableDatatype? PdIn, + ParsableDatatype? PdOut + )> ResolveProcessDataTypesAsync( + byte port, + IProcessDataTypeResolver processDataTypeResolver, + CancellationToken cancellationToken + ) + { + ParsableDatatype? pdInType; + ParsableDatatype? pdOutType; + + if (processDataTypeResolver.HasCondition()) + { + var condition = processDataTypeResolver.ResolveCondition(); + var conditionValue = await _connection + .ReadIndexAsync( + port, + condition.VariableDef.Index, + cancellationToken, + condition.ConditionDef.Subindex ?? 0 + ) + .ConfigureAwait(false); + pdInType = processDataTypeResolver.ResolveProcessDataIn(conditionValue.Span[0]); + pdOutType = processDataTypeResolver.ResolveProcessDataOut(conditionValue.Span[0]); + } + else + { + pdInType = processDataTypeResolver.ResolveProcessDataIn(); + pdOutType = processDataTypeResolver.ResolveProcessDataOut(); + } + + return (pdInType, pdOutType); + } +} diff --git a/src/IOLink.NET/Integration/ProcessDataReader.cs b/src/IOLink.NET/Integration/ProcessDataReader.cs new file mode 100644 index 0000000..046411e --- /dev/null +++ b/src/IOLink.NET/Integration/ProcessDataReader.cs @@ -0,0 +1,118 @@ +using IOLink.NET.Conversion; +using IOLink.NET.Core.Contracts; +using IOLink.NET.Core.Models; + +namespace IOLink.NET.Integration; + +/// +/// Responsible for reading process data from an IO-Link device and converting it to typed results. +/// +public class ProcessDataReader +{ + private readonly IMasterConnection _connection; + private readonly IIoddDataConverter _converter; + private readonly ConversionResultWrapper _resultWrapper; + + public ProcessDataReader(IMasterConnection connection, IIoddDataConverter converter) + { + _connection = connection; + _converter = converter; + _resultWrapper = new ConversionResultWrapper(); + } + + /// + /// Reads and converts process data input, returning a typed result. + /// + /// The port context. + /// The cancellation token. + /// A ConversionResult containing either a ScalarResult or ComplexResult. + /// Thrown when device has no process data in declared. + public async Task ReadProcessDataInAsync( + PortContext context, + CancellationToken cancellationToken + ) + { + if (context.PdIn is null) + { + throw new InvalidOperationException("Device has no process data in declared."); + } + + var value = await _connection + .ReadProcessDataInAsync(context.Port, cancellationToken) + .ConfigureAwait(false); + var convertedValue = _converter.Convert(context.PdIn, value.Span); + return _resultWrapper.WrapConversionResult(convertedValue); + } + + /// + /// Reads and converts process data output, returning a typed result. + /// + /// The port context. + /// The cancellation token. + /// A ConversionResult containing either a ScalarResult or ComplexResult. + /// Thrown when device has no process data out declared. + public async Task ReadProcessDataOutAsync( + PortContext context, + CancellationToken cancellationToken + ) + { + if (context.PdOut is null) + { + throw new InvalidOperationException("Device has no process data out declared."); + } + + var value = await _connection + .ReadProcessDataOutAsync(context.Port, cancellationToken) + .ConfigureAwait(false); + var convertedValue = _converter.Convert(context.PdOut, value.Span); + return _resultWrapper.WrapConversionResult(convertedValue); + } + + /// + /// Reads and converts process data input, returning the raw object (obsolete method). + /// + /// The port context. + /// The cancellation token. + /// The raw converted object. + [Obsolete("Use ReadProcessDataInAsync instead for better type safety.")] + public async Task ReadProcessDataInRawAsync( + PortContext context, + CancellationToken cancellationToken + ) + { + if (context.PdIn is null) + { + throw new InvalidOperationException("Device has no process data in declared."); + } + + var value = await _connection + .ReadProcessDataInAsync(context.Port, cancellationToken) + .ConfigureAwait(false); + var convertedValue = _converter.Convert(context.PdIn, value.Span); + return convertedValue; + } + + /// + /// Reads and converts process data output, returning the raw object (obsolete method). + /// + /// The port context. + /// The cancellation token. + /// The raw converted object. + [Obsolete("Use ReadProcessDataOutAsync instead for better type safety.")] + public async Task ReadProcessDataOutRawAsync( + PortContext context, + CancellationToken cancellationToken + ) + { + if (context.PdOut is null) + { + throw new InvalidOperationException("Device has no process data out declared."); + } + + var value = await _connection + .ReadProcessDataOutAsync(context.Port, cancellationToken) + .ConfigureAwait(false); + var convertedValue = _converter.Convert(context.PdOut, value.Span); + return convertedValue; + } +} diff --git a/src/Tests/IOLink.NET.ConsoleRunner/IOLink.NET.ConsoleRunner.csproj b/src/Tests/IOLink.NET.ConsoleRunner/IOLink.NET.ConsoleRunner.csproj new file mode 100644 index 0000000..475142c --- /dev/null +++ b/src/Tests/IOLink.NET.ConsoleRunner/IOLink.NET.ConsoleRunner.csproj @@ -0,0 +1,16 @@ + + + + + + + + + + Exe + net9.0 + enable + enable + + + diff --git a/src/Tests/IOLink.NET.ConsoleRunner/Program.cs b/src/Tests/IOLink.NET.ConsoleRunner/Program.cs new file mode 100644 index 0000000..2ced095 --- /dev/null +++ b/src/Tests/IOLink.NET.ConsoleRunner/Program.cs @@ -0,0 +1,70 @@ +// 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; +using IOLink.NET.Visualization.IODDConversion; + +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() + .WithMasterConnection(masterConnection) + .WithConverterDefaults() + .WithPublicIODDFinderApi() + .Build(); + +// Initialize the port reader for port 1. +await portReader.InitializeForPortAsync(4, CancellationToken.None); + +var ioddUserInterfaceReader = new IODDUserInterfaceConverter(portReader); + +var convertedUI = ioddUserInterfaceReader.Convert(); + +await convertedUI.SpecialistRoleMenu.ReadAsync(default); +Console.WriteLine("User Interface converted successfully."); + + +// Console.WriteLine("Reading Process & Acyclic data from configured master."); +// while (true) +// { +// // Read the process data from the device. +// var processData = await portReader.ReadConvertedProcessDataInResultAsync(default); + +// // 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, default); +// 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); + +// // 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/Tests/IOLink.NET.Core.Tests/GlobalUsings.cs b/src/Tests/IOLink.NET.Core.Tests/GlobalUsings.cs index 9d7ba5f..bf97a29 100644 --- a/src/Tests/IOLink.NET.Core.Tests/GlobalUsings.cs +++ b/src/Tests/IOLink.NET.Core.Tests/GlobalUsings.cs @@ -1,6 +1,5 @@ global using IOLink.NET.Core.Contracts; global using IOLink.NET.Core.Extensions; global using IOLink.NET.Core.Models; -global using NSubstitute; global using Shouldly; global using Xunit; diff --git a/src/Tests/IOLink.NET.Tests/DeviceDefinitionProviderTests.cs b/src/Tests/IOLink.NET.Tests/DeviceDefinitionProviderTests.cs index 7ddf9af..7292596 100644 --- a/src/Tests/IOLink.NET.Tests/DeviceDefinitionProviderTests.cs +++ b/src/Tests/IOLink.NET.Tests/DeviceDefinitionProviderTests.cs @@ -11,7 +11,12 @@ public async Task DoesLoadDeviceDefinitionAsync() { var client = new IODDFinderPublicClient(_baseUrl); var provider = new DeviceDefinitionProvider(client); - var definition = await provider.GetDeviceDefinitionAsync(888, 200710, "50142212"); + var definition = await provider.GetDeviceDefinitionAsync( + 888, + 200710, + "50142212", + CancellationToken.None + ); definition.ProfileBody.DeviceIdentity.VendorId.ShouldBe((ushort)888); } diff --git a/src/Tests/IOLink.NET.Tests/IODDFinderPublicClientTests.cs b/src/Tests/IOLink.NET.Tests/IODDFinderPublicClientTests.cs index f77b99c..893e866 100644 --- a/src/Tests/IOLink.NET.Tests/IODDFinderPublicClientTests.cs +++ b/src/Tests/IOLink.NET.Tests/IODDFinderPublicClientTests.cs @@ -10,16 +10,9 @@ public class IODDFinderPublicClientTests public async Task DoesLoadIoddAsync() { var client = new IODDFinderPublicClient(_baseUrl); - var iodd = await client.GetIODDPackageAsync(888, 131329, ""); + var iodd = await client.GetIODDPackageAsync(888, 131329, "", CancellationToken.None); iodd.ShouldNotBeNull(); iodd.CanRead.ShouldBeTrue(); } } - - - - - - - diff --git a/src/Tests/IOLink.NET.Tests/IODDPortReaderTests.cs b/src/Tests/IOLink.NET.Tests/IODDPortReaderTests.cs index 0462c14..43186d5 100644 --- a/src/Tests/IOLink.NET.Tests/IODDPortReaderTests.cs +++ b/src/Tests/IOLink.NET.Tests/IODDPortReaderTests.cs @@ -1,14 +1,11 @@ using System.Xml.Linq; -using IOLink.NET.Conversion; using IOLink.NET.Core.Contracts; using IOLink.NET.Integration; -using IOLink.NET.IODD; using IOLink.NET.IODD.Provider; using IOLink.NET.IODD.Resolution; using IOLink.NET.IODD.Resolution.Contracts; using IOLink.NET.IODD.Structure; using NSubstitute; -using Shouldly; namespace IOLink.NET.Tests; @@ -45,7 +42,7 @@ string ioddPath ioddPath ); - await portReader.InitializeForPortAsync(1); + await portReader.InitializeForPortAsync(1, CancellationToken.None); await ioddProvider .Received() @@ -54,7 +51,7 @@ await ioddProvider } [Fact] - public async Task ShouldThrowIfPortIsNotInIOLinkModeAsync() + public Task ShouldThrowIfPortIsNotInIOLinkModeAsync() { var (portReader, _, masterConnection) = PreparePortReader( 888, @@ -67,13 +64,13 @@ public async Task ShouldThrowIfPortIsNotInIOLinkModeAsync() portInfo.Status.Returns(PortStatus.Connected); masterConnection.GetPortInformationAsync(1, Arg.Any()).Returns(portInfo); - var initTask = () => portReader.InitializeForPortAsync(1); + var initTask = () => portReader.InitializeForPortAsync(1, CancellationToken.None); - await Should.ThrowAsync(initTask); + return Should.ThrowAsync(initTask); } [Fact] - public async Task ShouldThrowIfNoDeviceInfoAsync() + public Task ShouldThrowIfNoDeviceInfoAsync() { var (portReader, _, masterConnection) = PreparePortReader( 888, @@ -88,13 +85,13 @@ public async Task ShouldThrowIfNoDeviceInfoAsync() masterConnection.GetPortInformationAsync(1, Arg.Any()).Returns(portInfo); - var initTask = () => portReader.InitializeForPortAsync(1); + var initTask = () => portReader.InitializeForPortAsync(1, CancellationToken.None); - await Should.ThrowAsync(initTask); + return Should.ThrowAsync(initTask); } [Fact] - public async Task ShouldThrowIfUninitializedParameterReadAsync() + public Task ShouldThrowIfUninitializedParameterReadAsync() { var (portReader, _, _) = PreparePortReader( 888, @@ -103,9 +100,10 @@ public async Task ShouldThrowIfUninitializedParameterReadAsync() "Balluff", "TestData/Balluff-BCS_R08RRE-PIM80C-20150206-IODD1.1.xml" ); - var readParamTask = () => portReader.ReadConvertedParameterAsync(58, 0); + var readParamTask = () => + portReader.ReadConvertedParameterAsync(58, 0, CancellationToken.None); - await Should.ThrowAsync(readParamTask); + return Should.ThrowAsync(readParamTask); } [Fact] @@ -118,7 +116,8 @@ public async Task ShouldThrowIfUninitializedProcessDataInReadAsync() "Balluff", "TestData/Balluff-BCS_R08RRE-PIM80C-20150206-IODD1.1.xml" ); - var readParamTask = portReader.ReadConvertedProcessDataInAsync; + var readParamTask = async () => + await portReader.ReadConvertedProcessDataInAsync(CancellationToken.None); await Should.ThrowAsync(readParamTask); } @@ -133,7 +132,8 @@ public async Task ShouldThrowIfUninitializedProcessDataOutReadAsync() "Balluff", "TestData/Balluff-BCS_R08RRE-PIM80C-20150206-IODD1.1.xml" ); - var readParamTask = portReader.ReadConvertedProcessDataOutAsync; + var readParamTask = async () => + await portReader.ReadConvertedProcessDataOutAsync(CancellationToken.None); await Should.ThrowAsync(readParamTask); } @@ -148,14 +148,17 @@ public async Task CanReadConvertedProcessDataInWithConditionAsync() "STEGO", "TestData/STEGO-SmartSensor-CSS014-08-20190726-IODD1.1.xml" ); - masterConnection.ReadIndexAsync(1, 66).Returns(new byte[] { 0x00 }); masterConnection - .ReadProcessDataInAsync(1) + .ReadIndexAsync(1, 66, Arg.Any()) + .Returns(new byte[] { 0x00 }); + masterConnection + .ReadProcessDataInAsync(1, Arg.Any()) .Returns(new byte[] { 0x00, 0x00, 0x00, 0x00, 0x0, 0x0, 0x0, 0x0 }); - await portReader.InitializeForPortAsync(1); + await portReader.InitializeForPortAsync(1, CancellationToken.None); var pd = - (await portReader.ReadConvertedProcessDataInAsync()) as IEnumerable<(string, object)>; + (await portReader.ReadConvertedProcessDataInAsync(CancellationToken.None)) + as IEnumerable<(string, object)>; pd.ShouldNotBeNull(); // Debug: Let's examine what's actually in the collection @@ -188,10 +191,12 @@ string ioddPath vendorName, ioddPath ); - masterConnection.ReadIndexAsync(1, 58).Returns(new byte[] { 0x00, 0x00, 0x00, 0x04 }); - await portReader.InitializeForPortAsync(1); + masterConnection + .ReadIndexAsync(1, 58, Arg.Any()) + .Returns(new byte[] { 0x00, 0x00, 0x00, 0x04 }); + await portReader.InitializeForPortAsync(1, CancellationToken.None); - var converted = await portReader.ReadConvertedParameterAsync(58, 0); + var converted = await portReader.ReadConvertedParameterAsync(58, 0, CancellationToken.None); converted.ShouldBe(4); } diff --git a/src/Tests/IOLink.NET.Tests/IODDUserInterfaceConverterTests.cs b/src/Tests/IOLink.NET.Tests/IODDUserInterfaceConverterTests.cs index e66f7f4..b1c897a 100644 --- a/src/Tests/IOLink.NET.Tests/IODDUserInterfaceConverterTests.cs +++ b/src/Tests/IOLink.NET.Tests/IODDUserInterfaceConverterTests.cs @@ -1,14 +1,9 @@ -using IOLink.NET.Conversion; using IOLink.NET.Core.Contracts; -using IOLink.NET.Integration; -using IOLink.NET.IODD.Provider; -using IOLink.NET.IODD.Resolution.Contracts; using IOLink.NET.IODD.Structure.Interfaces; using IOLink.NET.IODD.Structure.Structure.Menu; using IOLink.NET.Visualization.IODDConversion; using NSubstitute; using NSubstitute.ReturnsExtensions; -using Shouldly; namespace IOLink.NET.Tests; @@ -109,13 +104,8 @@ public void MissingSpecialistRoleMenuIdentificationSubMenuShouldThrow() ); } - private static IODDPortReader GetSubstituteForIODDPortReader() + private static IIODDPortReader GetSubstituteForIODDPortReader() { - return Substitute.For( - Substitute.For(), - Substitute.For(), - Substitute.For(), - Substitute.For() - ); + return Substitute.For(); } } diff --git a/src/Tests/IOLink.NET.Tests/IoddComplexWriterTests.cs b/src/Tests/IOLink.NET.Tests/IoddComplexWriterTests.cs index daf1c8f..4d5ba43 100644 --- a/src/Tests/IOLink.NET.Tests/IoddComplexWriterTests.cs +++ b/src/Tests/IOLink.NET.Tests/IoddComplexWriterTests.cs @@ -1,6 +1,4 @@ using IOLink.NET.IODD.Resolution; -using IOLink.NET.IODD.Structure.Datatypes; -using Shouldly; namespace IOLink.NET.Tests; diff --git a/src/Tests/IOLink.NET.Tests/IoddConverterWriterIntegrationTests.cs b/src/Tests/IOLink.NET.Tests/IoddConverterWriterIntegrationTests.cs index 66e8a02..6bf5d17 100644 --- a/src/Tests/IOLink.NET.Tests/IoddConverterWriterIntegrationTests.cs +++ b/src/Tests/IOLink.NET.Tests/IoddConverterWriterIntegrationTests.cs @@ -1,9 +1,6 @@ using System.Xml.Linq; -using IOLink.NET.Conversion; -using IOLink.NET.IODD; using IOLink.NET.IODD.Resolution; using IOLink.NET.IODD.Structure.Datatypes; -using Shouldly; namespace IOLink.NET.Tests; diff --git a/src/Tests/IOLink.NET.Tests/IoddConverterWriterTests.cs b/src/Tests/IOLink.NET.Tests/IoddConverterWriterTests.cs index 0f00907..b9b091b 100644 --- a/src/Tests/IOLink.NET.Tests/IoddConverterWriterTests.cs +++ b/src/Tests/IOLink.NET.Tests/IoddConverterWriterTests.cs @@ -1,7 +1,5 @@ -using IOLink.NET.Conversion; using IOLink.NET.IODD.Resolution; using IOLink.NET.IODD.Structure.Datatypes; -using Shouldly; namespace IOLink.NET.Tests; diff --git a/src/Tests/IOLink.NET.Tests/IoddScalarConverterTests.cs b/src/Tests/IOLink.NET.Tests/IoddScalarConverterTests.cs index c5732f1..c0ceccd 100644 --- a/src/Tests/IOLink.NET.Tests/IoddScalarConverterTests.cs +++ b/src/Tests/IOLink.NET.Tests/IoddScalarConverterTests.cs @@ -1,5 +1,3 @@ -using Shouldly; -using IOLink.NET.Conversion; using IOLink.NET.IODD.Resolution; using IOLink.NET.IODD.Structure.Datatypes; @@ -155,10 +153,3 @@ public static void CanConvertUtf8String() _ = result.ShouldBeOfType(); } } - - - - - - - diff --git a/src/Tests/IOLink.NET.Tests/IoddScalarWriterTests.cs b/src/Tests/IOLink.NET.Tests/IoddScalarWriterTests.cs index 96f31ce..4f3a474 100644 --- a/src/Tests/IOLink.NET.Tests/IoddScalarWriterTests.cs +++ b/src/Tests/IOLink.NET.Tests/IoddScalarWriterTests.cs @@ -1,7 +1,5 @@ -using System.Reflection.Metadata; using IOLink.NET.IODD.Resolution; using IOLink.NET.IODD.Structure.Datatypes; -using Shouldly; namespace IOLink.NET.Tests; diff --git a/src/Tests/IOLink.NET.Tests/MenuDataReaderTests.cs b/src/Tests/IOLink.NET.Tests/MenuDataReaderTests.cs index 0061e18..d6557d5 100644 --- a/src/Tests/IOLink.NET.Tests/MenuDataReaderTests.cs +++ b/src/Tests/IOLink.NET.Tests/MenuDataReaderTests.cs @@ -1,15 +1,12 @@ using System.Xml.Linq; -using IOLink.NET.Conversion; using IOLink.NET.Core.Contracts; using IOLink.NET.Integration; -using IOLink.NET.IODD; using IOLink.NET.IODD.Provider; using IOLink.NET.IODD.Resolution; using IOLink.NET.IODD.Resolution.Contracts; using IOLink.NET.IODD.Structure; using IOLink.NET.Visualization.Menu; using NSubstitute; -using Shouldly; namespace IOLink.NET.Tests; @@ -79,11 +76,11 @@ string ioddPath ioddPath ); masterConnection - .ReadProcessDataInAsync(1) + .ReadProcessDataInAsync(1, Arg.Any()) .Returns(new byte[] { 0x00, 0x00, 0x00, 0x00, 0x0, 0x0, 0x0, 0x0 }); - await menuDataReader.InitializeForPortAsync(1); + await menuDataReader.InitializeForPortAsync(1, CancellationToken.None); var readableMenus = menuDataReader.GetReadableMenus(); - await readableMenus.ReadAsync(); + await readableMenus.ReadAsync(CancellationToken.None); readableMenus.ShouldNotBeNull(); } @@ -129,7 +126,7 @@ int menuCollectionCount vendorName, ioddPath ); - await menuDataReader.InitializeForPortAsync(1); + await menuDataReader.InitializeForPortAsync(1, CancellationToken.None); var rawMenuStructure = menuDataReader.GetIODDRawMenuStructure(); rawMenuStructure.ShouldNotBeNull(); @@ -189,7 +186,11 @@ string ioddPath var menuDataReader = new MenuDataReader(portReader); portReader - .ReadConvertedParameterAsync(Arg.Any(), Arg.Any()) + .ReadConvertedParameterAsync( + Arg.Any(), + Arg.Any(), + Arg.Any() + ) .Returns(string.Empty); return (portReader, ioddProvider, masterConnection, menuDataReader); @@ -216,7 +217,7 @@ string vendorName masterConnection.GetPortInformationAsync(1, Arg.Any()).Returns(portInfo); masterConnection - .ReadIndexAsync(portInfo.PortNumber, Arg.Any()) + .ReadIndexAsync(portInfo.PortNumber, Arg.Any(), Arg.Any()) .Returns(new byte[] { 0 }); return masterConnection; diff --git a/src/Tests/IOLink.NET.Tests/ParameterResolverTests.cs b/src/Tests/IOLink.NET.Tests/ParameterResolverTests.cs index 5e2f2dc..b7992aa 100644 --- a/src/Tests/IOLink.NET.Tests/ParameterResolverTests.cs +++ b/src/Tests/IOLink.NET.Tests/ParameterResolverTests.cs @@ -1,5 +1,4 @@ using System.Xml.Linq; -using IOLink.NET.IODD; using IOLink.NET.IODD.Resolution; using IOLink.NET.IODD.Structure; diff --git a/src/Tests/IOLink.NET.Tests/ParserTest.cs b/src/Tests/IOLink.NET.Tests/ParserTest.cs index a286ddd..196fc60 100644 --- a/src/Tests/IOLink.NET.Tests/ParserTest.cs +++ b/src/Tests/IOLink.NET.Tests/ParserTest.cs @@ -1,6 +1,4 @@ using System.Xml.Linq; -using IOLink.NET.IODD.Standard.Structure; -using IOLink.NET.IODD.Structure; namespace IOLink.NET.Tests; diff --git a/src/Tests/IOLink.NET.Tests/PortReaderBuilderTests.cs b/src/Tests/IOLink.NET.Tests/PortReaderBuilderTests.cs index a7ce1a8..361b162 100644 --- a/src/Tests/IOLink.NET.Tests/PortReaderBuilderTests.cs +++ b/src/Tests/IOLink.NET.Tests/PortReaderBuilderTests.cs @@ -1,10 +1,8 @@ -using IOLink.NET.Conversion; using IOLink.NET.Core.Contracts; using IOLink.NET.Integration; using IOLink.NET.IODD.Provider; using IOLink.NET.IODD.Resolution.Contracts; using NSubstitute; -using Shouldly; namespace IOLink.NET.Tests; diff --git a/src/Tests/IOLink.NET.Tests/ProcessDataResolverTests.cs b/src/Tests/IOLink.NET.Tests/ProcessDataResolverTests.cs index a82a7c7..08af2b1 100644 --- a/src/Tests/IOLink.NET.Tests/ProcessDataResolverTests.cs +++ b/src/Tests/IOLink.NET.Tests/ProcessDataResolverTests.cs @@ -1,5 +1,4 @@ using System.Xml.Linq; -using IOLink.NET.IODD; using IOLink.NET.IODD.Resolution; namespace IOLink.NET.Tests; @@ -21,10 +20,3 @@ public void CanResolveProcessData(string iodd, bool hasCondition, int? condition parsablePdIn.ShouldBeOfType(); } } - - - - - - - diff --git a/src/Tests/Vendors.Ifm/GlobalUsings.cs b/src/Tests/Vendors.Ifm/GlobalUsings.cs index ee88cb0..d289eaf 100644 --- a/src/Tests/Vendors.Ifm/GlobalUsings.cs +++ b/src/Tests/Vendors.Ifm/GlobalUsings.cs @@ -1,5 +1,4 @@ global using System.Reflection; -global using System.Text.Json; global using IOLink.NET.Core.Contracts; global using IOLink.NET.Core.Models; global using IOLink.NET.Vendors.Ifm; diff --git a/src/Tests/Vendors.Ifm/IfmIoTCoreIntegrationTest.cs b/src/Tests/Vendors.Ifm/IfmIoTCoreIntegrationTest.cs index 84c5dba..4e8c8b4 100644 --- a/src/Tests/Vendors.Ifm/IfmIoTCoreIntegrationTest.cs +++ b/src/Tests/Vendors.Ifm/IfmIoTCoreIntegrationTest.cs @@ -2,10 +2,8 @@ using IOLink.NET.Integration; using IOLink.NET.IODD.Provider; using IOLink.NET.IODD.Resolution.Common; -using IOLink.NET.Vendors.Ifm; using IOLink.NET.Visualization.Menu; using IOLink.NET.Visualization.Structure.Structure; -using Shouldly; using Vendors.Ifm.Configuration; namespace Vendors.Ifm; @@ -30,9 +28,9 @@ public async Task ShouldConvertProcessDataAsync() new DefaultTypeResolverFactory() ); - await portReader.InitializeForPortAsync(3); + await portReader.InitializeForPortAsync(3, CancellationToken.None); - var result = await portReader.ReadConvertedProcessDataInAsync(); + var result = await portReader.ReadConvertedProcessDataInResultAsync(CancellationToken.None); result.ShouldNotBeNull(); } @@ -50,9 +48,13 @@ public async Task ShouldConvertParameterDataAsync() new DefaultTypeResolverFactory() ); - await portReader.InitializeForPortAsync(3); + await portReader.InitializeForPortAsync(3, CancellationToken.None); - var result500 = await portReader.ReadConvertedParameterAsync(561, 0); + var result500 = await portReader.ReadConvertedParameterResultAsync( + 561, + 0, + CancellationToken.None + ); result500.ShouldNotBeNull(); } @@ -70,12 +72,33 @@ public async Task ShouldReadMenuValues() new DefaultTypeResolverFactory() ); - await portReader.InitializeForPortAsync(3); + await portReader.InitializeForPortAsync(3, CancellationToken.None); + + var V_SP_FH1_result = await portReader.ReadConvertedParameterResultAsync( + 583, + 0, + CancellationToken.None + ); + var rP_FL1_result = await portReader.ReadConvertedParameterResultAsync( + 584, + 0, + CancellationToken.None + ); + var dS1_result = await portReader.ReadConvertedParameterResultAsync( + 581, + 0, + CancellationToken.None + ); + var dr1_result = await portReader.ReadConvertedParameterResultAsync( + 582, + 0, + CancellationToken.None + ); - var V_SP_FH1 = await portReader.ReadConvertedParameterAsync(583, 0); - var rP_FL1 = await portReader.ReadConvertedParameterAsync(584, 0); - var dS1 = await portReader.ReadConvertedParameterAsync(581, 0); - var dr1 = await portReader.ReadConvertedParameterAsync(582, 0); + var V_SP_FH1 = (V_SP_FH1_result as ScalarResult)?.Value; + var rP_FL1 = (rP_FL1_result as ScalarResult)?.Value; + var dS1 = (dS1_result as ScalarResult)?.Value; + var dr1 = (dr1_result as ScalarResult)?.Value; V_SP_FH1.ShouldBe(600); rP_FL1.ShouldBe(500); @@ -98,11 +121,11 @@ public async Task ShouldReadAllMenuValuesWithMenuDataReader() ); var menuDataReader = new MenuDataReader(portReader); - await portReader.InitializeForPortAsync(3); - await menuDataReader.InitializeForPortAsync(3); + await portReader.InitializeForPortAsync(3, CancellationToken.None); + await menuDataReader.InitializeForPortAsync(3, CancellationToken.None); var readableMenus = menuDataReader.GetReadableMenus(); - await readableMenus.ReadAsync(); + await readableMenus.ReadAsync(CancellationToken.None); readableMenus.ObserverRoleMenu.IdentificationMenu.ShouldNotBeNull(); readableMenus.MaintenanceRoleMenu.IdentificationMenu.ShouldNotBeNull(); diff --git a/src/Tests/Vendors.Ifm/IfmIoTCoreMasterInformationTests.cs b/src/Tests/Vendors.Ifm/IfmIoTCoreMasterInformationTests.cs index 7d42291..6946084 100644 --- a/src/Tests/Vendors.Ifm/IfmIoTCoreMasterInformationTests.cs +++ b/src/Tests/Vendors.Ifm/IfmIoTCoreMasterInformationTests.cs @@ -1,6 +1,3 @@ -using Shouldly; -using IOLink.NET.Vendors.Ifm; -using IOLink.NET.Vendors.Ifm.Data; using Vendors.Ifm.Configuration; namespace Vendors.Ifm; @@ -76,10 +73,3 @@ public async Task CanGetPortTreeAsync() result.ShouldNotBeNull(); } } - - - - - - - diff --git a/src/Tests/Vendors.Ifm/IfmIotCoreMasterConnectionTests.cs b/src/Tests/Vendors.Ifm/IfmIotCoreMasterConnectionTests.cs index d18d3d5..345515a 100644 --- a/src/Tests/Vendors.Ifm/IfmIotCoreMasterConnectionTests.cs +++ b/src/Tests/Vendors.Ifm/IfmIotCoreMasterConnectionTests.cs @@ -1,5 +1,3 @@ -using IOLink.NET.Vendors.Ifm; -using Shouldly; using Vendors.Ifm.Configuration; namespace Vendors.Ifm; @@ -16,7 +14,7 @@ public async Task CanGetPortCountAsync() var masterClient = IfmIoTCoreClientFactory.Create(_baseUrl); var masterConnection = new IfmIotCoreMasterConnection(masterClient); - var result = await masterConnection.GetPortCountAsync(); + var result = await masterConnection.GetPortCountAsync(CancellationToken.None); result.ShouldBe((byte)4); } @@ -26,7 +24,7 @@ public async Task CanIdentifyPortAsync() var masterClient = IfmIoTCoreClientFactory.Create(_baseUrl); var masterConnection = new IfmIotCoreMasterConnection(masterClient); - var result = await masterConnection.GetPortInformationAsync(3); + var result = await masterConnection.GetPortInformationAsync(3, CancellationToken.None); result.ShouldNotBeNull(); } @@ -36,7 +34,7 @@ public async Task CanGetPortInformationsAsync() var masterClient = IfmIoTCoreClientFactory.Create(_baseUrl); var masterConnection = new IfmIotCoreMasterConnection(masterClient); - var result = await masterConnection.GetPortInformationsAsync(); + var result = await masterConnection.GetPortInformationsAsync(CancellationToken.None); result.ShouldNotBeNull(); result.Length.ShouldBe(4); }