{"id":16318409,"url":"https://github.com/chrispulman/s7plcrx","last_synced_at":"2026-04-28T12:05:28.188Z","repository":{"id":49715461,"uuid":"517806874","full_name":"ChrisPulman/S7PlcRx","owner":"ChrisPulman","description":"S7PlcRx is a comprehensive, production-ready reactive library for communicating with Siemens S7 PLCs. Built on Reactive Extensions (Rx.NET), it provides real-time data streaming, advanced performance optimizations, enterprise-grade reliability, and comprehensive industrial automation features.","archived":false,"fork":false,"pushed_at":"2026-04-18T22:27:52.000Z","size":15767,"stargazers_count":21,"open_issues_count":0,"forks_count":5,"subscribers_count":1,"default_branch":"main","last_synced_at":"2026-04-19T00:32:44.839Z","etag":null,"topics":["plc-programming","reactive","s7-plc","siemens"],"latest_commit_sha":null,"homepage":"","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ChrisPulman.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null},"funding":{"github":"ChrisPulman"}},"created_at":"2022-07-25T20:12:00.000Z","updated_at":"2026-04-18T22:27:42.000Z","dependencies_parsed_at":"2026-01-18T23:11:30.673Z","dependency_job_id":"34a4dd1c-6b82-4183-be0b-55caf484d918","html_url":"https://github.com/ChrisPulman/S7PlcRx","commit_stats":null,"previous_names":[],"tags_count":19,"template":false,"template_full_name":null,"purl":"pkg:github/ChrisPulman/S7PlcRx","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ChrisPulman%2FS7PlcRx","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ChrisPulman%2FS7PlcRx/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ChrisPulman%2FS7PlcRx/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ChrisPulman%2FS7PlcRx/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ChrisPulman","download_url":"https://codeload.github.com/ChrisPulman/S7PlcRx/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ChrisPulman%2FS7PlcRx/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32010957,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-18T20:23:30.271Z","status":"online","status_checked_at":"2026-04-19T02:00:07.110Z","response_time":55,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["plc-programming","reactive","s7-plc","siemens"],"created_at":"2024-10-10T22:23:27.337Z","updated_at":"2026-04-28T12:05:28.162Z","avatar_url":"https://github.com/ChrisPulman.png","language":"C#","funding_links":["https://github.com/sponsors/ChrisPulman"],"categories":[],"sub_categories":[],"readme":"![License](https://img.shields.io/github/license/ChrisPulman/S7PlcRx.svg) [![Build](https://github.com/ChrisPulman/S7PlcRx/actions/workflows/BuildOnly.yml/badge.svg)](https://github.com/ChrisPulman/S7PlcRx/actions/workflows/BuildOnly.yml) ![Nuget](https://img.shields.io/nuget/dt/S7PlcRx?color=pink\u0026style=plastic) [![NuGet](https://img.shields.io/nuget/v/S7PlcRx.svg?style=plastic)](https://www.nuget.org/packages/S7PlcRx)\n\n\u003cp align=\"left\"\u003e\n  \u003ca href=\"https://github.com/ChrisPulman/S7PlcRx\"\u003e\n    \u003cimg alt=\"S7PlcRx\" src=\"./Images/S7PlcRx.png\" width=\"200\"/\u003e\n  \u003c/a\u003e\n\u003c/p\u003e\n\n# S7PlcRx\n\nReactive Siemens S7 PLC communication for .NET. S7PlcRx provides tag-based S7 reads/writes, Rx observables, optimized batch helpers, caching, diagnostics, production reliability helpers, high-availability helpers, PLC byte conversion utilities, and source-generator assisted property bindings.\n\n\u003e Siemens and S7 are trademarks of Siemens AG. This project is independent and is not affiliated with or endorsed by Siemens AG. Test all automation code against a simulator or safe test rig before using production equipment.\n\n## Contents\n\n- [Supported PLCs and frameworks](#supported-plcs-and-frameworks)\n- [PLC prerequisites](#plc-prerequisites)\n- [Installation](#installation)\n- [Quick start](#quick-start)\n- [Addressing and data types](#addressing-and-data-types)\n- [Core tag API](#core-tag-api)\n- [Reactive reading](#reactive-reading)\n- [Manual reads and writes](#manual-reads-and-writes)\n- [Batch, async, and optimized APIs](#batch-async-and-optimized-apis)\n- [Source generator property binding](#source-generator-property-binding)\n- [Performance and cache features](#performance-and-cache-features)\n- [Enterprise features](#enterprise-features)\n- [Production reliability and diagnostics](#production-reliability-and-diagnostics)\n- [PLC type conversion helpers](#plc-type-conversion-helpers)\n- [Public API reference](#public-api-reference)\n- [Build and test](#build-and-test)\n\n## Supported PLCs and frameworks\n\n| PLC family | API |\n|---|---|\n| S7-1500 | `CpuType.S71500`, `S71500.Create(...)` |\n| S7-1200 | `CpuType.S71200`, `S71200.Create(...)` |\n| S7-400 | `CpuType.S7400`, `S7400.Create(...)` |\n| S7-300 | `CpuType.S7300`, `S7300.Create(...)` |\n| S7-200 | `CpuType.S7200`, `S7200.Create(...)` |\n| Logo 0BA8 | enum support where supported by the protocol path |\n\n`S7PlcRx` targets `net462`, `net472`, `net481`, `net8.0`, `net9.0`, and `net10.0`.\n\n`S7PlcRx.SourceGenerators` targets `netstandard2.0` and runs as a Roslyn analyzer/source generator.\n\n## PLC prerequisites\n\nFor absolute DB addressing such as `DB1.DBD0` on modern Siemens CPUs:\n\n1. Enable PUT/GET communication.\n2. Use non-optimized DB layout for directly addressed DBs.\n3. Confirm IP address, rack, and slot.\n4. Keep write tests away from live actuators until proven safe.\n5. For source-generated byte-array batching, place related tags in the same DB and adjacent byte ranges.\n\n## Installation\n\n```powershell\nInstall-Package S7PlcRx\n```\n\n```bash\ndotnet add package S7PlcRx\n```\n\nRepository/analyzer usage for the source generator:\n\n```xml\n\u003cProjectReference Include=\"..\\S7PlcRx.SourceGenerators\\S7PlcRx.SourceGenerators.csproj\"\n                  OutputItemType=\"Analyzer\"\n                  ReferenceOutputAssembly=\"false\"\n                  PrivateAssets=\"all\" /\u003e\n```\n\n## Quick start\n\n```csharp\nusing System.Reactive.Linq;\nusing S7PlcRx;\nusing S7PlcRx.Enums;\n\nusing var plc = new RxS7(CpuType.S71500, \"192.168.1.100\", rack: 0, slot: 1, interval: 100);\n\nplc.AddUpdateTagItem\u003cfloat\u003e(\"Temperature\", \"DB1.DBD0\");\nplc.AddUpdateTagItem\u003cbool\u003e(\"Running\", \"DB1.DBX4.0\");\nplc.AddUpdateTagItem\u003cfloat\u003e(\"TemperatureSetPoint\", \"DB1.DBD8\");\n\nusing var connected = plc.IsConnected\n    .DistinctUntilChanged()\n    .Subscribe(x =\u003e Console.WriteLine($\"Connected: {x}\"));\n\nusing var temperature = plc.Observe\u003cfloat\u003e(\"Temperature\")\n    .Where(x =\u003e x.HasValue)\n    .Subscribe(x =\u003e Console.WriteLine($\"Temperature: {x:F1} °C\"));\n\nplc.Value(\"TemperatureSetPoint\", 72.5f);\n```\n\nFactory equivalent:\n\n```csharp\nusing IRxS7 plc = S71500.Create(\"192.168.1.100\", rack: 0, slot: 1, interval: 100);\n```\n\n## Addressing and data types\n\n| Area | Examples | Notes |\n|---|---|---|\n| DB bit | `DB1.DBX0.0`, `DB1.DBX4.7` | Bit index must be 0-7. |\n| DB byte | `DB1.DBB0` | `byte`, `byte[]`, generated raw ranges. |\n| DB word | `DB1.DBW2` | `short`, `ushort`. |\n| DB double word | `DB1.DBD4` | `int`, `uint`, `float`; `double` uses 8 bytes from the start offset. |\n| Inputs | `I0.0`, `IB0`, `IW0`, `ID0`; `E` aliases | Input area. |\n| Outputs | `Q0.0`, `QB0`, `QW0`, `QD0`; `A` aliases | Output area. |\n| Memory | `M0.0`, `MB0`, `MW0`, `MD0` | Marker memory. |\n| Timers | `T1` | S7 timers. |\n| Counters | `C1`, `Z1` | S7 counters. |\n\n| S7 representation | C# type | Address example | Bytes |\n|---|---:|---|---:|\n| BOOL | `bool` | `DB1.DBX0.0` | bit in byte |\n| BYTE | `byte` | `DB1.DBB1` | 1 |\n| BYTE array | `byte[]` | `DB1.DBB100`, `arrayLength: 64` | length |\n| INT | `short` | `DB1.DBW2` | 2 |\n| WORD | `ushort` | `DB1.DBW4` | 2 |\n| DINT | `int` | `DB1.DBD6` | 4 |\n| DWORD | `uint` | `DB1.DBD10` | 4 |\n| REAL | `float` | `DB1.DBD14` | 4 |\n| LREAL | `double` | `DB1.DBD18` | 8 |\n| STRING | `string` | `DB1.DBB40` | variable |\n| Arrays | `short[]`, `ushort[]`, `int[]`, `uint[]`, `float[]`, `double[]` | first element address plus `arrayLength` | element size x length |\n\n## Core tag API\n\nRegister tags:\n\n```csharp\nplc.AddUpdateTagItem\u003cfloat\u003e(\"Temperature\", \"DB1.DBD0\");\nplc.AddUpdateTagItem\u003cbool\u003e(\"Running\", \"DB1.DBX4.0\");\nplc.AddUpdateTagItem\u003cbyte[]\u003e(\"RecipeBytes\", \"DB10.DBB0\", arrayLength: 64);\nplc.AddUpdateTagItem\u003cfloat[]\u003e(\"Curve\", \"DB20.DBD0\", arrayLength: 16);\n```\n\nFluent registration and polling control:\n\n```csharp\nplc.AddUpdateTagItem\u003cfloat\u003e(\"Temp1\", \"DB1.DBD0\")\n   .AddUpdateTagItem\u003cfloat\u003e(\"Temp2\", \"DB1.DBD4\")\n   .AddUpdateTagItem\u003cbool\u003e(\"Alarm\", \"DB1.DBX8.0\")\n   .SetTagPollIng(false); // disables polling on the last tag\n\nplc.GetTag(\"Temp1\").SetTagPollIng(false);\nplc.GetTag(\"Temp1\").SetTagPollIng(true);\nplc.RemoveTagItem(\"Temp1\");\n```\n\nTag stream projections:\n\n```csharp\nusing S7PlcRx;\n\nusing var dictionary = plc.ObserveAll\n    .TagToDictionary\u003cobject\u003e()\n    .Subscribe(values =\u003e Console.WriteLine(values.Count));\n\nusing var namedValue = plc.Observe\u003cfloat\u003e(\"Temperature\")\n    .ToTagValue(\"Temperature\")\n    .Subscribe(item =\u003e Console.WriteLine($\"{item.Tag}={item.Value}\"));\n```\n\n## Reactive reading\n\n```csharp\nusing System.Reactive.Linq;\n\nusing var highTemp = plc.Observe\u003cfloat\u003e(\"Temperature\")\n    .Where(x =\u003e x \u003e 80.0f)\n    .Subscribe(x =\u003e Console.WriteLine($\"High temperature: {x:F1}\"));\n\nusing var sampledPressure = plc.Observe\u003cfloat\u003e(\"Pressure\")\n    .Sample(TimeSpan.FromSeconds(5))\n    .Subscribe(x =\u003e Console.WriteLine($\"Pressure: {x:F2}\"));\n\nusing var averageFlow = plc.Observe\u003cfloat\u003e(\"FlowRate\")\n    .Buffer(TimeSpan.FromMinutes(1))\n    .Where(values =\u003e values.Count \u003e 0)\n    .Select(values =\u003e values.Average())\n    .Subscribe(avg =\u003e Console.WriteLine($\"Average flow: {avg:F2}\"));\n```\n\nConnection and diagnostic streams:\n\n```csharp\nplc.IsConnected.Subscribe(x =\u003e Console.WriteLine($\"Connected: {x}\"));\nplc.LastError.Subscribe(Console.WriteLine);\nplc.LastErrorCode.Subscribe(code =\u003e Console.WriteLine($\"Error code: {code}\"));\nplc.Status.Subscribe(Console.WriteLine);\nplc.IsPaused.Subscribe(paused =\u003e Console.WriteLine($\"Paused: {paused}\"));\nplc.ReadTime.Subscribe(ticks =\u003e Console.WriteLine($\"Read ticks: {ticks}\"));\n```\n\nCPU information:\n\n```csharp\nusing System.Reactive.Linq;\n\nvar cpuInfo = await plc.GetCpuInfo().FirstAsync();\nConsole.WriteLine($\"AS name: {cpuInfo[0]}, Module: {cpuInfo[1]}\");\n```\n\n## Manual reads and writes\n\n```csharp\nvar temperature = await plc.Value\u003cfloat\u003e(\"Temperature\");\n\nusing var cts = new CancellationTokenSource(TimeSpan.FromSeconds(2));\nvar pressure = await plc.ValueAsync\u003cfloat\u003e(\"Pressure\", cts.Token);\n\nplc.Value(\"SetPoint\", 72.5f);\nplc.Value(\"Enabled\", true);\nplc.Value(\"RecipeNumber\", (short)12);\nplc.Value(\"RecipeBytes\", new byte[] { 0x01, 0x02, 0x03 });\n```\n\nWatchdog:\n\n```csharp\nusing var plcWithWatchdog = new RxS7(\n    CpuType.S71500,\n    \"192.168.1.100\",\n    rack: 0,\n    slot: 1,\n    watchDogAddress: \"DB100.DBW0\",\n    interval: 100,\n    watchDogValueToWrite: 4500,\n    watchDogInterval: 10);\n\nplcWithWatchdog.ShowWatchDogWriting = true;\n```\n\n## Batch, async, and optimized APIs\n\nAdvanced batch helpers:\n\n```csharp\nusing S7PlcRx.Advanced;\n\nvar values = await plc.ValueBatch\u003cfloat\u003e(\"Temp1\", \"Temp2\", \"Temp3\");\n\nawait plc.ValueBatch(new Dictionary\u003cstring, float\u003e\n{\n    [\"SetPoint1\"] = 70.0f,\n    [\"SetPoint2\"] = 75.0f,\n});\n\nusing var batchSub = plc.ObserveBatch\u003cfloat\u003e(\"Temp1\", \"Temp2\")\n    .Subscribe(snapshot =\u003e Console.WriteLine(snapshot.Count));\n```\n\nDictionary batch helpers:\n\n```csharp\nvar tagMap = new Dictionary\u003cstring, string\u003e\n{\n    [\"Temperature\"] = \"DB1.DBD0\",\n    [\"Pressure\"] = \"DB1.DBD4\",\n};\n\nvar read = await plc.ReadBatchOptimized\u003cfloat\u003e(tagMap, timeoutMs: 5000);\nvar write = await plc.WriteBatchOptimized(\n    new Dictionary\u003cstring, float\u003e { [\"Temperature\"] = 22.5f },\n    verifyWrites: false,\n    enableRollback: false);\n```\n\nAsync-first `ValueTask` helpers:\n\n```csharp\nusing S7PlcRx.Advanced;\n\nvar current = await plc.ReadValueAsync\u003cfloat\u003e(\"Temperature\");\nvar many = await plc.ReadValuesAsync\u003cfloat\u003e(\"Temp1\", \"Temp2\");\n\nawait plc.WriteValuesAsync(new Dictionary\u003cstring, float\u003e\n{\n    [\"SetPoint1\"] = 72.5f,\n    [\"SetPoint2\"] = 73.0f,\n});\n```\n\n.NET 8+ async observable helpers:\n\n```csharp\nusing ReactiveUI.Extensions.Async;\nusing S7PlcRx.Advanced;\n\nawait using var sub = await plc.ObserveValueAsync\u003cfloat\u003e(\"Temperature\")\n    .SubscribeAsync(\n        async (value, cancellationToken) =\u003e\n        {\n            Console.WriteLine($\"Async temperature: {value}\" );\n            await Task.CompletedTask;\n        },\n        CancellationToken.None);\n```\n\n## Source generator property binding\n\n`S7PlcRx.SourceGenerators` generates PLC-bound properties from attributes. It removes repetitive tag registration, polling assignment, and setter write hooks.\n\n### Generated compile-time attributes\n\nThe generator injects this namespace into the consuming compilation:\n\n```csharp\nnamespace S7PlcRx.SourceGeneration;\n\n[AttributeUsage(AttributeTargets.Class)]\npublic sealed class S7PlcBindingAttribute : Attribute;\n\n[AttributeUsage(AttributeTargets.Property)]\npublic sealed class S7TagAttribute : Attribute\n{\n    public S7TagAttribute(string address);\n    public string Address { get; }\n    public int PollIntervalMs { get; set; } = 100;\n    public S7TagDirection Direction { get; set; } = S7TagDirection.ReadWrite;\n    public int ArrayLength { get; set; } = 1;\n}\n\npublic enum S7TagDirection\n{\n    ReadWrite,\n    ReadOnly,\n    WriteOnly\n}\n```\n\n### Generated binding example\n\n```csharp\nusing S7PlcRx;\nusing S7PlcRx.SourceGeneration;\n\n[S7PlcBinding]\npublic partial class MachineTags\n{\n    [S7Tag(\"DB1.DBD0\", PollIntervalMs = 100)]\n    public partial float Temperature { get; set; }\n\n    [S7Tag(\"DB1.DBX4.0\", PollIntervalMs = 100)]\n    public partial bool Running { get; set; }\n\n    [S7Tag(\"DB1.DBW6\", PollIntervalMs = 250, Direction = S7TagDirection.ReadOnly)]\n    public partial short ActualSpeed { get; set; }\n\n    [S7Tag(\"DB1.DBD8\", Direction = S7TagDirection.WriteOnly)]\n    public partial float SetPoint { get; set; }\n}\n\nusing var plc = S71500.Create(\"192.168.1.100\");\nvar tags = new MachineTags();\nusing var binding = tags.Bind(plc);\n\ntags.SetPoint = 72.5f; // queues a PLC write\n```\n\n### Generator rules and behavior\n\n- The class must be `partial` and marked with `[S7PlcBinding]`.\n- Each bound property must be `partial` and marked with `[S7Tag(\"...\")]`.\n- The generator emits backing fields, property implementations, a write hook, a read-apply hook, and `IDisposable Bind(IRxS7 plc)`.\n- Property setters call the runtime write hook when the value changes.\n- PLC reads update backing fields without creating write-back loops.\n- `PollIntervalMs \u003e 0` enables interval reads; `PollIntervalMs = 0` disables interval reads.\n- `ReadOnly` disables property writes; `WriteOnly` disables interval reads; `ReadWrite` enables both.\n- `ArrayLength` defines array/string/byte range length.\n- Generated byte-array batching supports DB addresses: `DBX`, `DBB`, `DBW`, `DBD`.\n\n### Efficient byte-array DB grouping\n\nFor same-DB nearby tags, the runtime reads one DB byte range and decodes properties locally:\n\n```text\nDB1.DBD0   Temperature  float  bytes 0..3\nDB1.DBD4   Pressure     float  bytes 4..7\nDB1.DBX8.0 Running      bool   byte 8 bit 0\n```\n\nRuntime range tag:\n\n```csharp\nplc.AddUpdateTagItem\u003cbyte[]\u003e(\"__s7_binding_db1_0_9\", \"DB1.DBB0\", arrayLength: 9);\n```\n\nWrites are coalesced on a short flush timer. The runtime performs read-modify-write byte-array writes so unrelated bytes/bits in the same range are preserved.\n\n### Manual runtime binding API\n\n```csharp\nusing S7PlcRx.Binding;\n\nvar definitions = new[]\n{\n    new S7TagDefinition(\"Temperature\", \"DB1.DBD0\", typeof(float), 100, S7TagDirection.ReadWrite),\n    new S7TagDefinition(\"Running\", \"DB1.DBX4.0\", typeof(bool), 100, S7TagDirection.ReadWrite),\n};\n\nusing var runtime = S7TagRuntimeBinding.Bind(\n    plc,\n    definitions,\n    (name, value) =\u003e Console.WriteLine($\"{name}={value}\"));\n\nruntime.Write(\"Temperature\", 25.5f);\n```\n\n## Performance and cache features\n\n```csharp\nusing S7PlcRx.Optimization;\nusing S7PlcRx.Performance;\n\nvar cached = await plc.ValueCached\u003cfloat\u003e(\"Temperature\", TimeSpan.FromSeconds(1));\nvar cacheStats = plc.GetCacheStatistics();\nplc.ClearCache(\"Temperature\");\n\nusing var smart = plc.MonitorTagSmart\u003cfloat\u003e(\"Temperature\", changeThreshold: 0.5, debounceMs: 100)\n    .Subscribe(change =\u003e Console.WriteLine($\"{change.TagName}: {change.PreviousValue} -\u003e {change.CurrentValue}\"));\n\nusing var perf = plc.MonitorPerformance(TimeSpan.FromSeconds(30))\n    .Subscribe(metrics =\u003e Console.WriteLine($\"{metrics.OperationsPerSecond:F1} ops/sec\"));\n\nvar optimizedReads = await plc.ReadOptimized\u003cfloat\u003e(new[] { \"Temp1\", \"Temp2\" });\nvar optimizedWrite = await plc.WriteOptimized(new Dictionary\u003cstring, float\u003e\n{\n    [\"SetPoint1\"] = 72.5f,\n    [\"SetPoint2\"] = 73.0f,\n});\n\nvar benchmark = await plc.RunBenchmark(new BenchmarkConfig\n{\n    LatencyTestCount = 10,\n    ThroughputTestDuration = TimeSpan.FromSeconds(10),\n    ReliabilityTestCount = 20,\n});\n\nvar stats = plc.GetPerformanceStatistics();\n```\n\nHigh-performance tag groups:\n\n```csharp\nusing S7PlcRx.Advanced;\nusing S7PlcRx.Performance;\n\nusing var group = plc.CreateTagGroup\u003cfloat\u003e(\"Process\", \"Temperature\", \"Pressure\", \"Flow\");\nusing var groupSub = group.ObserveGroup().Subscribe(snapshot =\u003e Console.WriteLine(snapshot.Count));\n\nvar allValues = await group.ReadAll();\nawait group.WriteAll(new Dictionary\u003cstring, float\u003e\n{\n    [\"Temperature\"] = 21.0f,\n    [\"Pressure\"] = 1.2f,\n});\n```\n\n## Enterprise features\n\nSymbol tables:\n\n```csharp\nusing S7PlcRx.Enterprise;\n\nvar csv = \"\"\"\nName,Address,DataType,Length,Description\nTemperature,DB1.DBD0,REAL,1,Process temperature\nRunning,DB1.DBX4.0,BOOL,1,Machine running\nRecipe,DB10.DBB0,ARRAY,64,Recipe bytes\n\"\"\";\n\nvar table = await plc.LoadSymbolTable(csv, SymbolTableFormat.Csv);\nvar temperature = await plc.ReadSymbol\u003cfloat\u003e(\"Temperature\");\nplc.WriteSymbol(\"Running\", true);\n```\n\nHigh availability:\n\n```csharp\nusing S7PlcRx.Enterprise;\n\nusing var primary = S71500.Create(\"192.168.1.100\");\nvar backups = new List\u003cIRxS7\u003e\n{\n    S71500.Create(\"192.168.1.101\"),\n    S71500.Create(\"192.168.1.102\"),\n};\n\nusing var ha = EnterpriseExtensions.CreateHighAvailabilityConnection(primary, backups, TimeSpan.FromSeconds(10));\nusing var failoverSub = ha.FailoverEvents.Subscribe(evt =\u003e Console.WriteLine(evt.Reason));\nIRxS7 active = ha.ActivePLC;\nawait ha.TriggerFailover();\n```\n\nConnection pool:\n\n```csharp\nusing S7PlcRx.Core;\nusing S7PlcRx.Enterprise;\nusing S7PlcRx.Enums;\n\nusing var pool = EnterpriseExtensions.CreateConnectionPool(\n    new[]\n    {\n        new PlcConnectionConfig\n        {\n            PLCType = CpuType.S71500,\n            IPAddress = \"192.168.1.100\",\n            Rack = 0,\n            Slot = 1,\n            ConnectionName = \"Line1Primary\",\n        },\n    },\n    new ConnectionPoolConfig\n    {\n        MaxConnections = 10,\n        EnableConnectionReuse = true,\n        HealthCheckInterval = TimeSpan.FromMinutes(1),\n    });\n\nIRxS7 connection = pool.GetConnection;\n```\n\n## Production reliability and diagnostics\n\n```csharp\nusing S7PlcRx.Advanced;\nusing S7PlcRx.Production;\n\nvar diagnostics = await plc.GetDiagnostics();\nConsole.WriteLine($\"Connected: {diagnostics.IsConnected}, latency: {diagnostics.ConnectionLatencyMs:F0} ms\");\n\nvar analysis = await plc.AnalyzePerformance(TimeSpan.FromMinutes(5));\nConsole.WriteLine($\"Total changes: {analysis.TotalTagChanges}\" );\n\nvar validation = await plc.ValidateProductionReadiness(new ProductionValidationConfig\n{\n    MaxAcceptableResponseTime = TimeSpan.FromMilliseconds(500),\n    MinimumReliabilityRate = 0.95,\n    ReliabilityTestCount = 10,\n    MinimumProductionScore = 80.0,\n});\n\nvar result = await plc.ExecuteWithErrorHandling(\n    () =\u003e plc.Value\u003cfloat\u003e(\"CriticalSensor\"),\n    new ProductionErrorConfig\n    {\n        MaxRetryAttempts = 3,\n        BaseRetryDelayMs = 1000,\n        UseExponentialBackoff = true,\n        CircuitBreakerThreshold = 5,\n        CircuitBreakerTimeout = TimeSpan.FromMinutes(1),\n    });\n\nvar handler = plc.EnableProductionErrorHandling(new ProductionErrorConfig());\nvar guarded = await handler.ExecuteAsync(() =\u003e plc.Value\u003cfloat\u003e(\"Temperature\"));\n```\n\n## PLC type conversion helpers\n\nAll conversion helpers use Siemens S7 byte ordering and are useful for codecs, tests, and generated/runtime byte-array binding logic.\n\n| Type/helper | Purpose | Common functions |\n|---|---|---|\n| `Bit` | Bit extraction/mutation from byte spans | `FromByte`, `FromSpan`, `ToBitArray`, `SetBit`, `GetBits`, `SetBits` |\n| `Boolean` | Single byte bit helpers | `GetValue`, `SetBit`, `ClearBit` |\n| `Byte` | Byte conversion | `ToByteArray`, `ToSpan`, `FromByteArray`, `FromSpan` |\n| `ByteArray` | Growable pooled byte buffer | `Add`, `Clear`, `TryCopyTo`, `Span`, `Memory`, `Array`, `Length` |\n| `Int` / `Word` | 16-bit signed/unsigned | `FromByteArray`, `FromSpan`, `ToArray`, `ToByteArray`, `ToSpan` |\n| `DInt` / `DWord` | 32-bit signed/unsigned | `FromByteArray`, `FromSpan`, `ToArray`, `ToByteArray`, `ToSpan` |\n| `Real` / `LReal` | 32/64-bit floating point | `FromByteArray`, `FromSpan`, `ToArray`, `ToByteArray`, `ToSpan` |\n| `Counter` / `Timer` | S7 counter/timer formats | `FromByteArray`, `FromSpan`, `ToArray`, `ToByteArray`, `ToSpan` |\n| `DateTime` / `DateTimeLong` | S7 date/time formats | `FromByteArray`, `FromSpan`, `ToArray`, `ToByteArray`, `ToSpan` |\n| `TimeSpan` | S7 time span conversion | `FromByteArray`, `FromSpan`, `ToArray`, `ToByteArray`, `ToSpan` |\n| `String`, `S7String`, `S7WString` | String encodings | `FromByteArray`, `FromSpan`, `ToByteArray`, `ToSpan`, `TryToSpan` |\n| `Struct`, `Class` | Reflection-based complex type conversion | `GetStructSize`/`GetClassSize`, `FromBytes`, `ToBytes` |\n\n```csharp\nusing S7PlcRx.PlcTypes;\n\nSpan\u003cbyte\u003e bytes = stackalloc byte[4];\nReal.ToSpan(12.5f, bytes);\nfloat roundTrip = Real.FromSpan(bytes);\n```\n\n## Error handling\n\n```csharp\nplc.LastError.Subscribe(message =\u003e Console.WriteLine(message));\nplc.LastErrorCode.Subscribe(code =\u003e Console.WriteLine(code));\n```\n\n- `PlcException` carries an `ErrorCode` for PLC communication failures.\n- `S7Exception` is a general S7 exception type.\n- `TagAddressOutOfRangeException` is thrown for invalid tag addresses.\n- Production error handling adds retries and circuit-breaker behavior.\n\n## Public API reference\n\nThis section is generated from the public C# surface in `src/S7PlcRx` and `src/S7PlcRx.SourceGenerators`, then paired with the usage guidance above. It is intended to make this README a single documentation source for the repository.\n\n### Source generator generated API\n\nThe source generator emits these compile-time-only types into consumer projects:\n\n- `S7PlcRx.SourceGeneration.S7PlcBindingAttribute` — marks a `partial class` for PLC property binding generation.\n- `S7PlcRx.SourceGeneration.S7TagAttribute` — marks a `partial` property with an S7 address; properties: `Address`, `PollIntervalMs`, `Direction`, `ArrayLength`.\n- `S7PlcRx.SourceGeneration.S7TagDirection` — `ReadWrite`, `ReadOnly`, `WriteOnly`.\n- Generated instance method: `public IDisposable Bind(IRxS7 plc)` on each `[S7PlcBinding]` class.\n\n### Runtime public surface\n\n\u003cdetails open\u003e\n\u003csummary\u003eAll public types and members\u003c/summary\u003e\n\n#### Namespace `S7PlcRx`\n\n##### `S7PlcRx.IRxS7`\nSource: `IRxS7.cs:19`\n\nDefines an interface for reactive communication with a Siemens S7 PLC, providing observable access to connection status, errors, tag values, and PLC information, as well as methods for reading and writing variables asynchronously. \u003cremarks\u003eThe IRxS7 interface exposes members for monitoring and interacting with a PLC in a reactive manner using observables. It supports observing connection state, errors, and tag values, as well as reading and writing variables with optional cancellation support. Implementations are expected to handle connection management and provide up-to-date PLC information. Thread safety and subscription management depend on the specific implementation.\u003c/remarks\u003e\n\n| Member | Summary |\n|---|---|\n| `public string IP get; }` | Gets the IP address associated with the current instance. |\n| `public IObservable\u003cbool\u003e IsConnected get; }` | Gets an observable sequence that indicates whether the connection is currently established. \u003cremarks\u003eSubscribers receive updates whenever the connection state changes. The sequence emits \u003csee langword=\"true\"/\u003e when connected and \u003csee langword=\"false\"/\u003e when disconnected.\u003c/remarks\u003e |\n| `public bool IsConnectedValue get; }` | Gets a value indicating whether the connection is currently established. |\n| `public IObservable\u003cstring\u003e LastError get; }` | Gets an observable sequence that provides error messages encountered during operation. \u003cremarks\u003eSubscribers receive error messages as they occur. The sequence completes when the underlying process completes or is disposed. No errors are pushed after completion.\u003c/remarks\u003e |\n| `public IObservable\u003cErrorCode\u003e LastErrorCode get; }` | Gets an observable sequence that provides notifications of the most recent error code encountered by the component. \u003cremarks\u003eSubscribers receive updates whenever a new error occurs. The sequence completes when the component is disposed or no longer reports errors. Thread safety and emission timing depend on the implementation of the observable.\u003c/remarks\u003e |\n| `public IObservable\u003cTag?\u003e ObserveAll get; }` | Gets an observable sequence that emits all tag updates as they occur. \u003cremarks\u003eSubscribers receive notifications for every tag, including additions, updates, and removals. The sequence emits a value of \u003csee langword=\"null\"/\u003e when a tag is removed.\u003c/remarks\u003e |\n| `public CpuType PLCType get; }` | Gets the type of programmable logic controller (PLC) associated with this instance. |\n| `public short Rack get; }` | Gets the rack number associated with the device or connection. |\n| `public short Slot get; }` | Gets the slot number associated with the current instance. |\n| `public IObservable\u003cbool\u003e IsPaused get; }` | Gets an observable sequence that indicates whether the operation is currently paused. \u003cremarks\u003eSubscribers receive a value of \u003csee langword=\"true\"/\u003e when the operation is paused and \u003csee langword=\"false\"/\u003e when it is active. The sequence emits updates whenever the paused state changes.\u003c/remarks\u003e |\n| `public IObservable\u003cstring\u003e Status get; }` | Gets an observable sequence that provides status updates as strings. \u003cremarks\u003eSubscribers receive status notifications as they occur. The sequence may complete or error depending on the underlying implementation.\u003c/remarks\u003e |\n| `public Tags TagList get; }` | Gets the collection of tags associated with the current instance. |\n| `public bool ShowWatchDogWriting get; set; }` | Gets or sets a value indicating whether WatchDog writing operations are displayed. |\n| `public string? WatchDogAddress get; }` | Gets the network address of the WatchDog service, if configured. |\n| `public ushort WatchDogValueToWrite get; set; }` | Gets or sets the value to be written to the watchdog register. |\n| `public int WatchDogWritingTime get; }` | Gets the time interval, in milliseconds, used by the watchdog for writing operations. |\n| `public IObservable\u003clong\u003e ReadTime get; }` | Gets an observable sequence that provides the current read time in ticks. \u003cremarks\u003eSubscribers receive updates whenever the read time changes. The value represents the number of ticks elapsed, where one tick equals 100 nanoseconds.\u003c/remarks\u003e |\n| `public IObservable\u003cT?\u003e Observe\u003cT\u003e(string? variable);` | Observes changes to the specified variable and returns a sequence of its values as they are updated. \u003ctypeparam name=\"T\"\u003eThe type of the variable to observe.\u003c/typeparam\u003e \u003cparam name=\"variable\"\u003eThe name of the variable to observe. Can be null to observe the default variable, if applicable.\u003c/param\u003e \u003creturns\u003eAn observable sequence that emits the current and subsequent values of the specified variable. The value is null if the variable does not exist or has not been set.\u003c/returns\u003e |\n| `public Task\u003cT?\u003e Value\u003cT\u003e(string? variable);` | Asynchronously retrieves the value of the specified variable, if it exists. \u003ctypeparam name=\"T\"\u003eThe type of the value to retrieve.\u003c/typeparam\u003e \u003cparam name=\"variable\"\u003eThe name of the variable whose value is to be retrieved. Can be null to indicate the default variable.\u003c/param\u003e \u003creturns\u003eA task that represents the asynchronous operation. The task result contains the value of the variable if found; otherwise, null.\u003c/returns\u003e |\n| `public Task\u003cT?\u003e ValueAsync\u003cT\u003e(string? variable, CancellationToken cancellationToken);` | Asynchronously retrieves the value of the specified variable, if it exists. \u003ctypeparam name=\"T\"\u003eThe type of the value to retrieve.\u003c/typeparam\u003e \u003cparam name=\"variable\"\u003eThe name of the variable to retrieve. Can be null to indicate the default variable, if supported.\u003c/param\u003e \u003cparam name=\"cancellationToken\"\u003eA cancellation token that can be used to cancel the asynchronous operation.\u003c/param\u003e \u003creturns\u003eA task that represents the asynchronous operation. The task result contains the value of the variable if found; otherwise, null.\u003c/returns\u003e |\n| `public void Value\u003cT\u003e(string? variable, T? value);` | Sets the value of a variable with the specified name and value. \u003ctypeparam name=\"T\"\u003eThe type of the value to assign to the variable.\u003c/typeparam\u003e \u003cparam name=\"variable\"\u003eThe name of the variable to set. Can be null to indicate an unnamed or default variable.\u003c/param\u003e \u003cparam name=\"value\"\u003eThe value to assign to the variable. Can be null if the variable type allows null values.\u003c/param\u003e |\n| `public IObservable\u003cstring[]\u003e GetCpuInfo();` | Retrieves an observable sequence containing information about the system's CPU. \u003cremarks\u003eSubscribers receive updates as the CPU information changes. The format and content of each string array may vary depending on the platform or implementation.\u003c/remarks\u003e \u003creturns\u003eAn observable sequence of string arrays, where each array contains details about the CPU. The sequence emits new arrays when CPU information is updated.\u003c/returns\u003e |\n\n##### `S7PlcRx.ITag`\nSource: `Tags/ITag.cs:9`\n\nRepresents a tag that can be configured to control polling behavior.\n\n| Member | Summary |\n|---|---|\n| `public void SetDoNotPoll(bool value);` | Sets whether the object should be excluded from polling operations. \u003cparam name=\"value\"\u003etrue to prevent the object from being polled; otherwise, false.\u003c/param\u003e |\n\n##### `S7PlcRx.PlcException`\nSource: `PlcException.cs:16`\n\n| Member | Summary |\n|---|---|\n| `public PlcException(ErrorCode errorCode) : this(errorCode, $\"PLC communication failed with error '{errorCode}'.\")` | Initializes a new instance of the \u003csee cref=\"PlcException\"/\u003e class. \u003cparam name=\"errorCode\"\u003eThe error code.\u003c/param\u003e |\n| `public PlcException(ErrorCode errorCode, Exception? innerException) : this(errorCode, innerException?.Message, innerException)` | Initializes a new instance of the \u003csee cref=\"PlcException\"/\u003e class. \u003cparam name=\"errorCode\"\u003eThe error code.\u003c/param\u003e \u003cparam name=\"innerException\"\u003eThe inner exception.\u003c/param\u003e |\n| `public PlcException(ErrorCode errorCode, string? message) : base(message) =\u003e ...` | Initializes a new instance of the \u003csee cref=\"PlcException\"/\u003e class. \u003cparam name=\"errorCode\"\u003eThe error code.\u003c/param\u003e \u003cparam name=\"message\"\u003eThe message.\u003c/param\u003e |\n| `public PlcException(ErrorCode errorCode, string? message, Exception? inner) : base(message, inner) =\u003e ...` | Initializes a new instance of the \u003csee cref=\"PlcException\"/\u003e class. \u003cparam name=\"errorCode\"\u003eThe error code.\u003c/param\u003e \u003cparam name=\"message\"\u003eThe message.\u003c/param\u003e \u003cparam name=\"inner\"\u003eThe inner.\u003c/param\u003e |\n| `public ErrorCode ErrorCode get; }` | Gets the error code. \u003cvalue\u003e The error code. \u003c/value\u003e |\n\n##### `S7PlcRx.RxS7`\nSource: `RxS7.cs:32`\n\nProvides an observable, reactive interface for reading from and writing to Siemens S7 PLCs, supporting tag-based access, status monitoring, and asynchronous operations. \u003cremarks\u003eThe RxS7 class enables integration with Siemens S7 programmable logic controllers (PLCs) using a tag-based model and reactive programming patterns. It exposes observables for PLC data, connection status, errors, and operational metrics, allowing clients to subscribe to real-time updates. The class supports both synchronous and asynchronous read/write operations, as well as advanced features such as watchdog monitoring and batch variable access. Thread safety is maintained for concurrent operations. Dispose the instance when no longer needed to release resources and terminate background operations.\u003c/remarks\u003e\n\n| Member | Summary |\n|---|---|\n| `public RxS7(CpuType type, string ip, short rack, short slot, string? watchDogAddress = null, double interval = 100, ushort watchDogValueToWrite = 4500, int watchDogInterval = 10)` | Initializes a new instance of the \u003csee cref=\"RxS7\"/\u003e class and establishes a connection to a Siemens S7 PLC with optional. watchdog monitoring and periodic tag reading. \u003cremarks\u003eIf a valid watchdog address is provided, the constructor enables periodic writing to the specified address to support external watchdog monitoring. Tag reading and connection status monitoring are started automatically upon construction.\u003c/remarks\u003e \u003cparam name=\"type\"\u003eThe type of the PLC CPU to connect to.\u003c/param\u003e \u003cparam name=\"ip\"\u003eThe IP address of the PLC to connect to.\u003c/param\u003e \u003cparam name=\"rack\"\u003eThe rack number of the PLC hardware configuration.\u003c/param\u003e \u003cparam name=\"slot\"\u003eThe slot number of the PLC CPU module.\u003c/param\u003e \u003cparam name=\"watchDogAddress\"\u003eThe address of the watchdog tag in the PLC memory. Must be a DBW address or null to disable watchdog monitoring.\u003c/param\u003e \u003cparam name=\"interval\"\u003eThe interval, in milliseconds, at which tag values are read from the PLC. Must be greater than 0.\u003c/param\u003e \u003cparam name=\"watchDogValueToWrite\"\u003eThe value to write to the watchdog address during each watchdog cycle.\u003c/param\u003e \u003cparam name=\"watchDogInterval\"\u003eThe interval, in seconds, at which the watchdog value is written. Must be greater than 0 if watchdog monitoring is enabled.\u003c/param\u003e \u003cexception cref=\"ArgumentException\"\u003eThrown if watchDogAddress is provided and is not a valid DBW address.\u003c/exception\u003e \u003cexception cref=\"ArgumentOutOfRangeException\"\u003eThrown if watchDogInterval is less than 1 when watchdog monitoring is enabled.\u003c/exception\u003e |\n| `public IObservable\u003cTag?\u003e ObserveAll =\u003e ...` | Gets an observable sequence that emits all tag updates as they occur. \u003cremarks\u003eEach observer receives tag updates in real time as they are published. The sequence is shared among all subscribers, and subscriptions are managed automatically. Observers may receive null values if a tag is removed or unavailable.\u003c/remarks\u003e |\n| `public IObservable\u003cbool\u003e IsPaused =\u003e ...` | Gets an observable sequence that indicates whether the operation is currently paused. \u003cremarks\u003eThe returned observable emits a value of \u003csee langword=\"true\"/\u003e when the operation enters a paused state, and \u003csee langword=\"false\"/\u003e when it resumes. Subscribers receive updates only when the paused state changes. The sequence is shared among all subscribers.\u003c/remarks\u003e |\n| `public string IP get; }` | Gets the IP address associated with the current instance. |\n| `public IObservable\u003cbool\u003e IsConnected get; }` | Gets an observable sequence that indicates whether the connection is currently established. \u003cremarks\u003eSubscribers receive updates whenever the connection state changes. The sequence emits \u003csee langword=\"true\"/\u003e when connected and \u003csee langword=\"false\"/\u003e when disconnected.\u003c/remarks\u003e |\n| `public bool IsConnectedValue get; private set; }` | Gets a value indicating whether the connection is currently established. |\n| `public IObservable\u003cstring\u003e LastError =\u003e ...` | Gets an observable sequence that provides the most recent error messages encountered by the component. \u003cremarks\u003eSubscribers receive error messages as they occur. The sequence is shared among all subscribers, and each subscriber receives messages from the point of subscription onward.\u003c/remarks\u003e |\n| `public IObservable\u003cErrorCode\u003e LastErrorCode =\u003e ...` | Gets an observable sequence that emits the most recent error code reported by the system. \u003cremarks\u003eSubscribers receive updates whenever a new error code is reported. The sequence is shared among all subscribers and only remains active while there is at least one active subscription.\u003c/remarks\u003e |\n| `public CpuType PLCType get; }` | Gets the type of PLC (Programmable Logic Controller) associated with this instance. |\n| `public short Rack get; }` | Gets the rack number associated with the device or component. |\n| `public bool ShowWatchDogWriting get; set; }` | Gets or sets a value indicating whether WatchDog writing output is displayed. |\n| `public short Slot get; }` | Gets the slot number associated with this instance. |\n| `public IObservable\u003cstring\u003e Status =\u003e ...` | Gets an observable sequence that provides status updates as strings. \u003cremarks\u003eSubscribers receive status updates as they occur. The observable sequence is shared among all subscribers, and subscriptions are managed automatically. Status updates are pushed to observers in real time.\u003c/remarks\u003e |\n| `public Tags TagList get; } = [];` | Gets the collection of tags associated with the current instance. |\n| `public string? WatchDogAddress get; }` | Gets the network address of the WatchDog service, if configured. |\n| `public ushort WatchDogValueToWrite get; set; } = 4500;` | Gets or sets the value to be written to the watchdog timer. |\n| `public int WatchDogWritingTime get; } = 10;` | Gets the interval, in seconds, that the watchdog uses when writing status updates. |\n| `public bool IsDisposed get; private set; }` | Gets a value indicating whether gets a value that indicates whether the object is disposed. |\n| `public IObservable\u003clong\u003e ReadTime =\u003e ...` | Gets an observable sequence that emits the current read time in ticks whenever a read operation occurs. \u003cremarks\u003eThe observable sequence is shared among all subscribers. Each subscriber receives notifications when a read operation is performed, with the value representing the read time in ticks. The sequence completes when the underlying source completes.\u003c/remarks\u003e |\n| `public IObservable\u003cT?\u003e Observe\u003cT\u003e(string? variable) =\u003e ...` | Returns an observable sequence that emits the current and future values of the specified variable, cast to the specified type. \u003cremarks\u003eThe returned observable is hot and shared among all subscribers. Subscribers receive the most recent value and all subsequent updates. If the variable does not exist or its value cannot be cast to the specified type, the observable emits null.\u003c/remarks\u003e \u003ctypeparam name=\"T\"\u003eThe type to which the variable's value is cast in the observable sequence.\u003c/typeparam\u003e \u003cparam name=\"variable\"\u003eThe name of the variable to observe. If null, all variables are observed.\u003c/param\u003e \u003creturns\u003eAn observable sequence of values of type T, or null if the variable's value is not present or cannot be cast to T. The sequence emits a new value each time the variable changes.\u003c/returns\u003e |\n| `public async Task\u003cT?\u003e Value\u003cT\u003e(string? variable)` | Asynchronously retrieves the value of the specified variable, cast to the specified type, if available. \u003cremarks\u003eIf the variable's type is not known, it is set to the requested type \u003ctypeparamref name=\"T\"/\u003e before retrieving the value. The method waits for an internal pause condition to be met before proceeding.\u003c/remarks\u003e \u003ctypeparam name=\"T\"\u003eThe type to which the variable's value is cast and returned.\u003c/typeparam\u003e \u003cparam name=\"variable\"\u003eThe name of the variable whose value is to be retrieved. Can be null.\u003c/param\u003e \u003creturns\u003eA value of type \u003ctypeparamref name=\"T\"/\u003e if the variable exists and can be cast to the specified type; otherwise, \u003csee langword=\"default\"/\u003e.\u003c/returns\u003e |\n| `public async Task\u003cT?\u003e ValueAsync\u003cT\u003e(string? variable, CancellationToken cancellationToken)` | Asynchronously retrieves the value of the specified variable, pausing polling operations during the read. \u003cremarks\u003ePolling is temporarily paused while the value is being read to ensure consistency. If no polling is active, the method may wait briefly before proceeding. The method is cancellation-friendly and will respond promptly to cancellation requests.\u003c/remarks\u003e \u003ctypeparam name=\"T\"\u003eThe expected type of the variable's value to retrieve.\u003c/typeparam\u003e \u003cparam name=\"variable\"\u003eThe name of the variable whose value is to be retrieved. Cannot be null, empty, or consist only of white-space characters.\u003c/param\u003e \u003cparam name=\"cancellationToken\"\u003eA cancellation token that can be used to cancel the operation.\u003c/param\u003e \u003creturns\u003eA task that represents the asynchronous operation. The task result contains the value of the specified variable cast to type T, or the default value of T if the variable is not found or cannot be cast.\u003c/returns\u003e \u003cexception cref=\"ArgumentNullException\"\u003eThrown if variable is null, empty, or consists only of white-space characters.\u003c/exception\u003e \u003cexception cref=\"OperationCanceledException\"\u003eThrown if the operation is canceled via the cancellation token.\u003c/exception\u003e |\n| `public void Value\u003cT\u003e(string? variable, T? value)` | Sets the value of the specified variable if it exists and the value is compatible with the variable's type. \u003cremarks\u003eIf the variable does not exist or the value is null, this method does nothing. The value is only set if its type matches the variable's expected type or if the type parameter is object.\u003c/remarks\u003e \u003ctypeparam name=\"T\"\u003eThe type of the value to assign to the variable.\u003c/typeparam\u003e \u003cparam name=\"variable\"\u003eThe name of the variable whose value is to be set. Cannot be null.\u003c/param\u003e \u003cparam name=\"value\"\u003eThe value to assign to the variable. Must be compatible with the variable's type.\u003c/param\u003e |\n| `public void Dispose()` | Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. |\n| `public IObservable\u003cstring[]\u003e GetCpuInfo() =\u003e ...` | Retrieves detailed information about the connected CPU as an observable sequence. \u003cremarks\u003eThe method waits until a connection is established before retrieving CPU information. If the required data is not immediately available, the method will retry until successful or until the subscription is disposed. The order and content of the returned string array correspond to specific CPU information fields. This method is intended for use in reactive programming scenarios where CPU information is needed asynchronously.\u003c/remarks\u003e \u003creturns\u003eAn observable sequence that emits a string array containing CPU information fields, such as the AS name, module name, copyright, serial number, module type name, order code, and version numbers. The sequence completes after emitting the data.\u003c/returns\u003e |\n\n##### `S7PlcRx.S71200`\nSource: `Create/S71200.cs:9`\n\nProvides factory methods for creating connections to Siemens S7-1200 PLC devices.\n\n| Member | Summary |\n|---|---|\n| `public static IRxS7 Create(string ip, short rack = 0, string? watchDogAddress = null, double interval = 100, ushort watchDogValueToWrite = 4500, int watchDogInterval = 100)` | Creates a new instance of an S7 PLC connection with the specified configuration parameters. \u003cparam name=\"ip\"\u003eThe IP address of the S7 PLC to connect to.\u003c/param\u003e \u003cparam name=\"rack\"\u003eThe rack number of the PLC. Must be between 0 and 7. The default is 0.\u003c/param\u003e \u003cparam name=\"watchDogAddress\"\u003eThe address of the watchdog variable in the PLC memory. If null, the watchdog feature is disabled.\u003c/param\u003e \u003cparam name=\"interval\"\u003eThe polling interval, in milliseconds, for reading data from the PLC. The default is 100 milliseconds.\u003c/param\u003e \u003cparam name=\"watchDogValueToWrite\"\u003eThe value to write to the watchdog variable, if specified. The default is 4500.\u003c/param\u003e \u003cparam name=\"watchDogInterval\"\u003eThe interval, in milliseconds, at which the watchdog value is written. The default is 100 milliseconds.\u003c/param\u003e \u003creturns\u003eAn object implementing the IRxS7 interface that represents the configured PLC connection.\u003c/returns\u003e \u003cexception cref=\"ArgumentOutOfRangeException\"\u003eThrown if the value of rack is less than 0 or greater than 7.\u003c/exception\u003e |\n\n##### `S7PlcRx.S71500`\nSource: `Create/S71500.cs:9`\n\nProvides factory methods for creating connections to Siemens S7-1500 PLC devices.\n\n| Member | Summary |\n|---|---|\n| `public static IRxS7 Create(string ip, short rack = 0, short slot = 1, string? watchDogAddress = null, double interval = 100, ushort watchDogValueToWrite = 4500, int watchDogInterval = 10)` | Creates a new instance of an S7 PLC client configured for the specified IP address, rack, slot, and optional watchdog monitoring. \u003cremarks\u003eIf \u003cparamref name=\"watchDogAddress\"/\u003e is specified, the client will periodically write \u003cparamref name=\"watchDogValueToWrite\"/\u003e to the given address at the specified \u003cparamref name=\"watchDogInterval\"/\u003e. This can be used to implement a heartbeat or keep-alive mechanism with the PLC.\u003c/remarks\u003e \u003cparam name=\"ip\"\u003eThe IP address of the S7 PLC to connect to.\u003c/param\u003e \u003cparam name=\"rack\"\u003eThe rack number of the PLC. Must be between 0 and 7.\u003c/param\u003e \u003cparam name=\"slot\"\u003eThe slot number of the PLC CPU module. Must be between 1 and 31.\u003c/param\u003e \u003cparam name=\"watchDogAddress\"\u003eThe address of the watchdog variable in the PLC memory to monitor. If null, watchdog monitoring is disabled.\u003c/param\u003e \u003cparam name=\"interval\"\u003eThe polling interval, in milliseconds, for communication with the PLC. Must be positive.\u003c/param\u003e \u003cparam name=\"watchDogValueToWrite\"\u003eThe value to write to the watchdog variable when monitoring is enabled.\u003c/param\u003e \u003cparam name=\"watchDogInterval\"\u003eThe interval, in seconds, at which the watchdog value is written. Must be positive.\u003c/param\u003e \u003creturns\u003eAn IRxS7 instance configured to communicate with the specified S7 PLC and optional watchdog monitoring.\u003c/returns\u003e \u003cexception cref=\"ArgumentOutOfRangeException\"\u003eThrown when the value of \u003cparamref name=\"rack\"/\u003e is not between 0 and 7, or \u003cparamref name=\"slot\"/\u003e is not between 1 and 31.\u003c/exception\u003e |\n\n##### `S7PlcRx.S7200`\nSource: `Create/S7200.cs:9`\n\nProvides factory methods for creating connections to Siemens S7-200 PLC devices.\n\n| Member | Summary |\n|---|---|\n| `public static IRxS7 Create(string ip, short rack, short slot, string? watchDogAddress = null, double interval = 100, ushort watchDogValueToWrite = 4500, int watchDogInterval = 100) =\u003e ...` | Creates a new instance of an S7-200 PLC client for communication over TCP/IP with optional watchdog monitoring. \u003cremarks\u003eIf a watchdog address is specified, the client will periodically write the specified value to the PLC at the given interval to support connection monitoring or fail-safe logic. Ensure that the PLC is configured to handle the watchdog mechanism as expected.\u003c/remarks\u003e \u003cparam name=\"ip\"\u003eThe IP address of the S7-200 PLC to connect to. Cannot be null or empty.\u003c/param\u003e \u003cparam name=\"rack\"\u003eThe rack number of the PLC CPU module. Typically 0 for S7-200 devices.\u003c/param\u003e \u003cparam name=\"slot\"\u003eThe slot number of the PLC CPU module. Typically 0 or 1 for S7-200 devices.\u003c/param\u003e \u003cparam name=\"watchDogAddress\"\u003eThe address in the PLC memory to use for the watchdog mechanism, or null to disable watchdog monitoring.\u003c/param\u003e \u003cparam name=\"interval\"\u003eThe polling interval, in milliseconds, for regular communication with the PLC. Must be greater than 0.\u003c/param\u003e \u003cparam name=\"watchDogValueToWrite\"\u003eThe value to write to the watchdog address during each watchdog cycle.\u003c/param\u003e \u003cparam name=\"watchDogInterval\"\u003eThe interval, in milliseconds, at which the watchdog value is written. Must be greater than 0.\u003c/param\u003e \u003creturns\u003eAn IRxS7 instance configured to communicate with the specified S7-200 PLC, with optional watchdog monitoring enabled.\u003c/returns\u003e |\n\n##### `S7PlcRx.S7300`\nSource: `Create/S7300.cs:9`\n\nProvides factory methods for creating connections to Siemens S7-300 PLC devices.\n\n| Member | Summary |\n|---|---|\n| `public static IRxS7 Create(string ip, short rack, short slot, string? watchDogAddress = null, double interval = 100, ushort watchDogValueToWrite = 4500, int watchDogInterval = 100)` | Creates a new instance of an S7 PLC connection with the specified configuration parameters. \u003cparam name=\"ip\"\u003eThe IP address of the S7 PLC to connect to.\u003c/param\u003e \u003cparam name=\"rack\"\u003eThe rack number of the PLC. Must be between 0 and 7, inclusive.\u003c/param\u003e \u003cparam name=\"slot\"\u003eThe slot number of the PLC. Must be between 1 and 31, inclusive.\u003c/param\u003e \u003cparam name=\"watchDogAddress\"\u003eThe address in the PLC memory to use for the watchdog mechanism, or null to disable the watchdog.\u003c/param\u003e \u003cparam name=\"interval\"\u003eThe polling interval, in milliseconds, for communication with the PLC. Must be greater than 0.\u003c/param\u003e \u003cparam name=\"watchDogValueToWrite\"\u003eThe value to write to the watchdog address during each interval.\u003c/param\u003e \u003cparam name=\"watchDogInterval\"\u003eThe interval, in milliseconds, at which the watchdog value is written. Must be greater than 0.\u003c/param\u003e \u003creturns\u003eAn object implementing the IRxS7 interface that represents the configured PLC connection.\u003c/returns\u003e \u003cexception cref=\"ArgumentOutOfRangeException\"\u003eThrown when the value of \u003cparamref name=\"rack\"/\u003e is not between 0 and 7, or when the value of \u003cparamref name=\"slot\"/\u003e is not between 1 and 31.\u003c/exception\u003e |\n\n##### `S7PlcRx.S7400`\nSource: `Create/S7400.cs:9`\n\nProvides factory methods for creating S7-400 PLC connections.\n\n| Member | Summary |\n|---|---|\n| `public static IRxS7 Create(string ip, short rack, short slot, string? watchDogAddress = null, double interval = 100, ushort watchDogValueToWrite = 4500, int watchDogInterval = 100)` | Creates a new instance of an S7 PLC client configured for the specified IP address, rack, slot, and optional watchdog monitoring parameters. \u003cremarks\u003eIf watchdog monitoring is enabled by specifying a non-null watchDogAddress, the client will periodically write the specified value to the given address at the defined interval. This can be used to implement a heartbeat or keep-alive mechanism with the PLC.\u003c/remarks\u003e \u003cparam name=\"ip\"\u003eThe IP address of the S7 PLC to connect to.\u003c/param\u003e \u003cparam name=\"rack\"\u003eThe rack number of the PLC. Must be between 0 and 7.\u003c/param\u003e \u003cparam name=\"slot\"\u003eThe slot number of the PLC CPU. Must be between 1 and 31.\u003c/param\u003e \u003cparam name=\"watchDogAddress\"\u003eThe address in the PLC memory to use for the watchdog mechanism, or null to disable watchdog monitoring.\u003c/param\u003e \u003cparam name=\"interval\"\u003eThe polling interval, in milliseconds, for reading data from the PLC. Must be greater than 0.\u003c/param\u003e \u003cparam name=\"watchDogValueToWrite\"\u003eThe value to write to the watchdog address during each interval if watchdog monitoring is enabled.\u003c/param\u003e \u003cparam name=\"watchDogInterval\"\u003eThe interval, in milliseconds, at which the watchdog value is written if watchdog monitoring is enabled. Must be greater than 0.\u003c/param\u003e \u003creturns\u003eAn IRxS7 instance configured to communicate with the specified S7 PLC and optional watchdog monitoring.\u003c/returns\u003e \u003cexception cref=\"ArgumentOutOfRangeException\"\u003eThrown if rack is not between 0 and 7, or if slot is not between 1 and 31.\u003c/exception\u003e |\n\n##### `S7PlcRx.S7Exception`\nSource: `S7Exception.cs:13`\n\n| Member | Summary |\n|---|---|\n| `public S7Exception()` | Initializes a new instance of the \u003csee cref=\"S7Exception\"/\u003e class. |\n| `public S7Exception(string message) : base(message)` | Initializes a new instance of the \u003csee cref=\"S7Exception\"/\u003e class. \u003cparam name=\"message\"\u003eThe message that describes the error.\u003c/param\u003e |\n| `public S7Exception(string message, Exception innerException) : base(message, innerException)` | Initializes a new instance of the \u003csee cref=\"S7Exception\"/\u003e class. \u003cparam name=\"message\"\u003eThe error message that explains the reason for the exception.\u003c/param\u003e \u003cparam name=\"innerException\"\u003eThe exception that is the cause of the current exception, or a null reference (\u003csee langword=\"Nothing\" /\u003e in Visual Basic) if no inner exception is specified.\u003c/param\u003e |\n\n##### `S7PlcRx.Tag`\nSource: `Tags/Tag.cs:15`\n\n| Member | Summary |\n|---|---|\n| `public Tag()` | Initializes a new instance of the \u003csee cref=\"Tag\"/\u003e class. |\n| `public Tag(string address, Type type)` | Initializes a new instance of the \u003csee cref=\"Tag\" /\u003e class. \u003cparam name=\"address\"\u003eThe address.\u003c/param\u003e \u003cparam name=\"type\"\u003eThe type.\u003c/param\u003e |\n| `public Tag(string address, Type type, int arrayLength)` | Initializes a new instance of the \u003csee cref=\"Tag\"/\u003e class. \u003cparam name=\"address\"\u003eThe address.\u003c/param\u003e \u003cparam name=\"type\"\u003eThe type.\u003c/param\u003e \u003cparam name=\"arrayLength\"\u003eLength of the array.\u003c/param\u003e |\n| `public Tag(string name, string address, Type type)` | Initializes a new instance of the \u003csee cref=\"Tag\" /\u003e class. \u003cparam name=\"name\"\u003eThe name.\u003c/param\u003e \u003cparam name=\"address\"\u003eThe address.\u003c/param\u003e \u003cparam name=\"type\"\u003eThe type.\u003c/param\u003e |\n| `public Tag(string name, string address, Type type, int arrayLength)` | Initializes a new instance of the \u003csee cref=\"Tag\"/\u003e class. \u003cparam name=\"name\"\u003eThe name.\u003c/param\u003e \u003cparam name=\"address\"\u003eThe address.\u003c/param\u003e \u003cparam name=\"type\"\u003eThe type.\u003c/param\u003e \u003cparam name=\"arrayLength\"\u003eLength of the array.\u003c/param\u003e |\n| `public Tag(string name, string address, object value, Type type)` | Initializes a new instance of the \u003csee cref=\"Tag\" /\u003e class. \u003cparam name=\"name\"\u003eThe name.\u003c/param\u003e \u003cparam name=\"address\"\u003eThe address.\u003c/param\u003e \u003cparam name=\"value\"\u003eThe value.\u003c/param\u003e \u003cparam name=\"type\"\u003eThe type.\u003c/param\u003e |\n| `public string? Address get; set; }` | Gets or sets the address associated with the entity. |\n| `public string? Name get; set; }` | Gets or sets the name associated with the object. |\n| `public object? Value get; set; }` | Gets or sets the value associated with this instance. |\n| `public object? NewValue get; internal set; }` | Gets the new value associated with the change event. |\n| `public Type Type get; internal set; }` | Gets the runtime type information associated with the current instance. |\n| `public int? ArrayLength get; internal set; }` | Gets the length of the array, if known. |\n| `public bool DoNotPoll get; internal set; }` | Gets a value indicating whether polling operations should be suppressed for this instance. |\n| `public void SetDoNotPoll(bool value) =\u003e ...` | Sets a value indicating whether polling operations should be disabled. \u003cparam name=\"value\"\u003etrue to disable polling; otherwise, false.\u003c/param\u003e |\n\n##### `S7PlcRx.TagAddressOutOfRangeException`\nSource: `Tags/TagAddressOutOfRangeException.cs:15`\n\nNo public instance/static members declared directly on this type.\n\n##### `S7PlcRx.TagExtensions`\nSource: `Tags/TagExtensions.cs:18`\n\nProvides extension methods for managing and interacting with tag items in IRxS7-based PLC systems. These methods enable adding, updating, retrieving, and removing tags, as well as converting tag streams to dictionary representations. \u003cremarks\u003eThe TagExtensions class offers a fluent API for working with tags in Siemens S7 PLC communication scenarios. It includes methods for adding tags with type and array support, controlling polling behavior, and transforming tag data streams. All methods are implemented as extension methods to enhance usability and integration with IRxS7 and related types. Thread safety and error handling depend on the underlying IRxS7 and Tag implementations.\u003c/remarks\u003e\n\n| Member | Summary |\n|---|---|\n| `public static (ITag? tag, IRxS7? plc) AddUpdateTagItem\u003cT\u003e(this IRxS7 @this, string tagName, string address, int? arrayLength = null)` | Adds or updates a tag item of the specified type in the PLC and returns the created tag and the PLC instance. \u003cremarks\u003eIf the specified type parameter is a string or an array type and an array length is provided, the tag will be created as an array with the given length. Otherwise, the tag will be created as a single value. This method is an extension method for IRxS7 implementations that support tag management.\u003c/remarks\u003e \u003ctypeparam name=\"T\"\u003eThe data type of the tag to add or update. This determines the type of value the tag will hold.\u003c/typeparam\u003e \u003cparam name=\"this\"\u003eThe PLC instance to which the tag item will be added or updated.\u003c/param\u003e \u003cparam name=\"tagName\"\u003eThe name to assign to the tag item.\u003c/param\u003e \u003cparam name=\"address\"\u003eThe address in the PLC where the tag item is located.\u003c/param\u003e \u003cparam name=\"arrayLength\"\u003eThe length of the array for the tag item, if the tag represents an array type. Specify null for non-array types.\u003c/param\u003e \u003creturns\u003eA tuple containing the created or updated tag as the first element and the PLC instance as the second element. The tag will be null if the PLC instance does not support tag operations.\u003c/returns\u003e |\n| `public static (ITag? tag, IRxS7? plc) AddUpdateTagItem(this IRxS7 @this, Type type, string tagName, string address, int? arrayLength = null)` | Adds or updates a tag item in the specified PLC instance and returns the created tag and the PLC reference. \u003cremarks\u003eIf the specified type is an array or a string, the array length must be provided. If the PLC instance does not support adding or updating tags, the returned tag will be null.\u003c/remarks\u003e \u003cparam name=\"this\"\u003eThe PLC instance to which the tag item will be added or updated.\u003c/param\u003e \u003cparam name=\"type\"\u003eThe data type of the tag to add or update. Must not be null.\u003c/param\u003e \u003cparam name=\"tagName\"\u003eThe name of the tag to add or update.\u003c/param\u003e \u003cparam name=\"address\"\u003eThe address in the PLC where the tag is located.\u003c/param\u003e \u003cparam name=\"arrayLength\"\u003eThe length of the array if the tag type is an array or a string; otherwise, null.\u003c/param\u003e \u003creturns\u003eA tuple containing the created tag (or null if the operation was not successful) and the PLC reference.\u003c/returns\u003e |\n| `public static (ITag? tag, IRxS7? plc) AddUpdateTagItem\u003cT\u003e(this (ITag? _, IRxS7? plc) @this, string tagName, string address, int? arrayLength = null)` | Adds or updates a tag item of the specified type in the PLC and returns the created tag along with the PLC instance. \u003cremarks\u003eIf the PLC instance is not of type \u003csee cref=\"RxS7\"/\u003e, no tag is added or updated and the returned tag will be null. When adding a string or array tag, \u003cparamref name=\"arrayLength\"/\u003e must be specified to define the size of the tag.\u003c/remarks\u003e \u003ctypeparam name=\"T\"\u003eThe data type of the tag to add or update. This can be a primitive type, string, or array type.\u003c/typeparam\u003e \u003cparam name=\"this\"\u003eA tuple containing the current tag (which may be null) and the PLC instance in which to add or update the tag.\u003c/param\u003e \u003cparam name=\"tagName\"\u003eThe name to assign to the tag item.\u003c/param\u003e \u003cparam name=\"address\"\u003eThe address in the PLC where the tag is located.\u003c/param\u003e \u003cparam name=\"arrayLength\"\u003eThe length of the array if the tag represents an array type. This parameter is required when \u003ctypeparamref name=\"T\"/\u003e is a string or an array type; otherwise, it is ignored.\u003c/param\u003e \u003creturns\u003eA tuple containing the created or updated tag as \u003csee cref=\"ITag\"/\u003e and the PLC instance as \u003csee cref=\"IRxS7\"/\u003e. If the PLC instance is null, the tag will also be null.\u003c/returns\u003e |\n| `public static (ITag? tag, IRxS7? plc) AddUpdateTagItem(this (ITag? _, IRxS7? plc) @this, Type type, string tagName, string address, int? arrayLength = null)` | Adds or updates a tag item in the specified PLC instance using the provided type, tag name, and address. \u003cremarks\u003eIf the tag type is a string or an array, the array length must be specified. The method does not modify the PLC instance if it is null or if the type is null.\u003c/remarks\u003e \u003cparam name=\"this\"\u003eA tuple containing the current tag (ignored) and the PLC instance in which to add or update the tag item.\u003c/param\u003e \u003cparam name=\"type\"\u003eThe type of the tag to add or update. Must not be null.\u003c/param\u003e \u003cparam name=\"tagName\"\u003eThe name of the tag to add or update in the PLC.\u003c/param\u003e \u003cparam name=\"address\"\u003eThe address in the PLC where the tag is located.\u003c/param\u003e \u003cparam name=\"arrayLength\"\u003eThe length of the array if the tag type is an array or a string. Optional.\u003c/param\u003e \u003creturns\u003eA tuple containing the created or updated tag and the PLC instance. The tag is null if the PLC instance or type is null.\u003c/returns\u003e |\n| `public static (ITag? tag, IRxS7? plc) SetTagPollIng(this (ITag? tag, IRxS7? plc) @this, bool polling = true)` | Enables or disables polling for the specified tag by setting its polling state. \u003cremarks\u003eIf the tag is null, this method has no effect. This method does not modify the PLC instance.\u003c/remarks\u003e \u003cparam name=\"this\"\u003eA tuple containing the tag and PLC instance to update.\u003c/param\u003e \u003cparam name=\"polling\"\u003etrue to enable polling for the tag; false to disable polling. The default is true.\u003c/param\u003e \u003creturns\u003eA tuple containing the original tag and PLC instance.\u003c/returns\u003e |\n| `public static (ITag? tag, IRxS7? plc) GetTag(this IRxS7 @this, string tagName) =\u003e ...` | Retrieves the tag with the specified name from the PLC's tag list, along with the associated PLC instance. \u003cremarks\u003eIf the specified tag name does not exist in the PLC's tag list, the method returns a tuple with a null tag and the original PLC instance. This method is an extension method for the IRxS7 interface.\u003c/remarks\u003e \u003cparam name=\"this\"\u003eThe PLC instance from which to retrieve the tag.\u003c/param\u003e \u003cparam name=\"tagName\"\u003eThe name of the tag to retrieve. Cannot be null.\u003c/param\u003e \u003creturns\u003eA tuple containing the tag that matches the specified name and the PLC instance. If the tag is not found, the tag element of the tuple is null.\u003c/returns\u003e |\n| `public static void RemoveTagItem(this IRxS7 @this, string tagName)` | Removes the tag item with the specified name from the underlying RxS7 instance, if applicable. \u003cremarks\u003eIf the underlying object is not an RxS7 instance, this method has no effect.\u003c/remarks\u003e \u003cparam name=\"this\"\u003eThe IRxS7 instance from which to remove the tag item.\u003c/param\u003e \u003cparam name=\"tagName\"\u003eThe name of the tag item to remove. Cannot be null.\u003c/param\u003e |\n| `public static IObservable\u003cIDictionary\u003cstring, TValue\u003e\u003e TagToDictionary\u003cTValue\u003e(this IObservable\u003cTag?\u003e source)` | Projects a sequence of nullable Tag objects into a stream of dictionaries containing the most recent values for each tag name, filtered by the specified value type. \u003cremarks\u003eEach emitted dictionary contains all tag names encountered so far whose values are of type TValue and are not null. The dictionary is updated with each new matching tag in the source sequence. The same dictionary instance is reused and updated for each emission; callers should not modify the returned dictionary.\u003c/remarks\u003e \u003ctypeparam name=\"TValue\"\u003eThe type to which tag values are filtered and cast. Only tags whose values are of this type are included in the resulting dictionaries.\u003c/typeparam\u003e \u003cparam name=\"source\"\u003eThe observable sequence of nullable Tag objects to process.\u003c/param\u003e \u003creturns\u003eAn observable sequence of dictionaries mapping tag names to their most recent non-null values of type TValue. Each dictionary reflects the accumulated state up to that point in the source sequence.\u003c/returns\u003e |\n| `public static IObservable\u003c(string Tag, TValue Value)\u003e ToTagValue\u003cTValue\u003e(this IObservable\u003cTValue?\u003e source, string tag) =\u003e ...` | Projects each non-null value in the source sequence into a tuple containing the specified tag and the value. \u003cremarks\u003eNull values in the source sequence are filtered out. The resulting sequence will not contain any tuples with a null value.\u003c/remarks\u003e \u003ctypeparam name=\"TValue\"\u003eThe type of the values in the source sequence.\u003c/typeparam\u003e \u003cparam name=\"source\"\u003eThe observable sequence of nullable values to process. Only non-null values are included in the result.\u003c/param\u003e \u003cparam name=\"tag\"\u003eThe tag to associate with each value in the resulting sequence. This value is included in each emitted tuple.\u003c/param\u003e \u003creturns\u003eAn observable sequence of tuples, where each tuple contains the specified tag and a non-null value from the source sequence.\u003c/returns\u003e |\n\n##### `S7PlcRx.Tags`\nSource: `Tags/Tags.cs:18`\n\n| Member | Summary |\n|---|---|\n| `public Tags()` | Initializes a new instance of the \u003csee cref=\"Tags\"/\u003e class. |\n| `public object? this[object key, bool isEnd = false] #pragma warning restore RCS1163 // Unused parameter.` |  |\n| `public Tag? this[string name] =\u003e ...` | Gets the tag with the specified name, if it exists. \u003cparam name=\"name\"\u003eThe name of the tag to retrieve. The comparison may be case-sensitive depending on the implementation.\u003c/param\u003e \u003creturns\u003eThe tag associated with the specified name, or null if no tag with that name exists.\u003c/returns\u003e |\n| `public Tag? this[Tag? tag] =\u003e ...` | Gets the tag from the collection that matches the specified tag's name, if present. \u003cremarks\u003eThis indexer performs a lookup based on the name of the provided tag. If the specified tag is null, the result is null.\u003c/remarks\u003e \u003cparam name=\"tag\"\u003eThe tag whose name is used to locate the corresponding tag in the collection. Can be null.\u003c/param\u003e \u003creturns\u003eThe tag from the collection that has the same name as the specified tag, or null if no such tag exists.\u003c/returns\u003e |\n| `public new void Add(object key, object value)` | Adds an element with the specified key and value to the collection in a thread-safe manner. \u003cremarks\u003eThis method ensures that the add operation is thread-safe. If an element with the same key already exists, an exception is thrown.\u003c/remarks\u003e \u003cparam name=\"key\"\u003eThe key of the element to add. Cannot be null.\u003c/param\u003e \u003cparam name=\"value\"\u003eThe value of the element to add. Can be null.\u003c/param\u003e |\n| `public void Add(object key, Tag tag)` | Adds the specified tag to the collection with the associated key. \u003cremarks\u003eIf the collection already contains an element with the same key, an exception may be thrown depending on the underlying implementation. This method is thread-safe.\u003c/remarks\u003e \u003cparam name=\"key\"\u003eThe key with which the specified tag is to be associated. Cannot be null.\u003c/param\u003e \u003cparam name=\"tag\"\u003eThe tag to add to the collection. Cannot be null.\u003c/param\u003e |\n| `public void Add(Tag tag)` | Adds the specified tag to the collection. \u003cparam name=\"tag\"\u003eThe tag to add to the collection. Cannot be null.\u003c/param\u003e |\n| `public void Add(object key, Tags tags)` | Adds the specified key and associated tags to the collection. \u003cparam name=\"key\"\u003eThe key with which the specified tags are to be associated. Cannot be null.\u003c/param\u003e \u003cparam name=\"tags\"\u003eThe tags to associate with the specified key. Cannot be null.\u003c/param\u003e |\n| `public void AddRange(IEnumerable\u003cTag\u003e tags)` | Adds a collection of tags to the current instance, including only those tags whose values are not null. \u003cparam name=\"tags\"\u003eThe collection of \u003csee cref=\"Tag\"/\u003e objects to add. Only tags with non-null values are added.\u003c/param\u003e \u003cexception cref=\"ArgumentNullException\"\u003eThrown if \u003cparamref name=\"tags\"/\u003e is null.\u003c/exception\u003e |\n| `public Tags GetTags()` | Retrieves a collection of tags that have non-null values. \u003creturns\u003eA \u003csee cref=\"Tags\"/\u003e collection containing all tags with non-null values. The collection will be empty if no such tags exist.\u003c/returns\u003e |\n| `public List\u003cTag\u003e ToList()` | Returns a list containing all tags in the collection. \u003cremarks\u003eThe returned list is a snapshot of the collection at the time of the call. Subsequent modifications to the collection are not reflected in the returned list. This method is thread-safe.\u003c/remarks\u003e \u003creturns\u003eA list of \u003csee cref=\"Tag\"/\u003e objects representing the tags in the collection. The list is empty if the collection contains no tags or if an error occurs while retrieving the tags.\u003c/returns\u003e |\n\n#### Namespace `S7PlcRx.Advanced`\n\n##### `S7PlcRx.Advanced.AdvancedExtensions`\nSource: `Advanced/AdvancedExtensions.cs:22`\n\nProvides advanced extension methods for efficient batch operations, diagnostics, and performance analysis on PLC (Programmable Logic Controller) instances using the IRxS7 interface. \u003cremarks\u003eThese extension methods enable high-performance reading, writing, monitoring, and analysis of PLC variables, supporting scenarios such as batch updates, optimized data access, and system diagnostics. Methods are designed to simplify complex PLC interactions and provide recommendations for optimization. All methods require a valid IRxS7 instance and may throw exceptions if invalid arguments are supplied. Thread safety and performance considerations are addressed where relevant in individual method documentation.\u003c/remarks\u003e\n\n| Member | Summary |\n|---|---|\n| `public static IObservable\u003cDictionary\u003cstring, T?\u003e\u003e ObserveBatch\u003cT\u003e(this IRxS7 plc, params string[] variables)` | Observes the values of multiple PLC variables as a batch and emits updates as a dictionary when any of the specified variables change. \u003cremarks\u003eThe returned observable is hot and shared among all subscribers. If a specified variable is not already being polled, polling is automatically enabled for that variable. The sequence emits a new dictionary only when the set of variable values changes.\u003c/remarks\u003e \u003ctypeparam name=\"T\"\u003eThe type of the variable values to observe. Must be compatible with the PLC variable types.\u003c/typeparam\u003e \u003cparam name=\"plc\"\u003eThe PLC instance that provides access to the variables to observe. Cannot be null.\u003c/param\u003e \u003cparam name=\"variables\"\u003eThe names of the PLC variables to observe. If empty, an empty dictionary is emitted.\u003c/param\u003e \u003creturns\u003eAn observable sequence that emits a dictionary mapping each specified variable name to its most recent value. The dictionary is updated and emitted whenever any of the observed variables change.\u003c/returns\u003e \u003cexception cref=\"ArgumentNullException\"\u003eThrown if the \u003cparamref name=\"plc\"/\u003e parameter is null.\u003c/exception\u003e |\n| `public static async Task\u003cDictionary\u003cstring, T?\u003e\u003e ValueBatch\u003cT\u003e(this IRxS7 plc, params string[] variables)` | Asynchronously reads the values of multiple variables from the PLC and returns a dictionary mapping variable names to their values. \u003cremarks\u003eIf a variable name does not exist in the PLC or cannot be read, its value in the returned dictionary will be the default value for type T. The order of the returned dictionary corresponds to the order of the requested variable names. This method may perform the reads in parallel for efficiency.\u003c/remarks\u003e \u003ctypeparam name=\"T\"\u003eThe type of the variable values to read from the PLC.\u003c/typeparam\u003e \u003cparam name=\"plc\"\u003eThe PLC instance from which to read the variable values. Cannot be null.\u003c/param\u003e \u003cparam name=\"variables\"\u003eAn array of variable names to read from the PLC. Each name must correspond to a valid variable in the PLC.\u003c/param\u003e \u003creturns\u003eA task that represents the asynchronous operation. The task result contains a dictionary mapping each requested variable name to its value of type T, or to the default value of T if the variable could not be read or does not exist. If no variables are specified, returns an empty dictionary.\u003c/returns\u003e \u003cexception cref=\"ArgumentNullException\"\u003eThrown if the plc parameter is null.\u003c/exception\u003e |\n| `public static async Task ValueBatch\u003cT\u003e(this IRxS7 plc, Dictionary\u003cstring, T\u003e values)` | Writes a batch of values to the PLC asynchronously using the specified tag-value pairs. \u003cremarks\u003eIf the underlying PLC implementation supports batch writing, the method attempts to write all values in a single operation for improved performance. Otherwise, values are written individually in parallel. No action is taken if the dictionary is null or empty.\u003c/remarks\u003e \u003ctypeparam name=\"T\"\u003eThe type of the values to write to the PLC.\u003c/typeparam\u003e \u003cparam name=\"plc\"\u003eThe PLC interface to which the values will be written.\u003c/param\u003e \u003cparam name=\"values\"\u003eA dictionary containing tag names as keys and the corresponding values to write. Cannot be null or empty.\u003c/param\u003e \u003creturns\u003eA task that represents the asynchronous write operation.\u003c/returns\u003e |\n| `public static async Task\u003cBatchReadResult\u003cT\u003e\u003e ReadBatchOptimized\u003cT\u003e( this IRxS7 plc, Dictionary\u003cstring, string\u003e tagMapping, int timeoutMs = 5000)` | Reads a batch of tags from the PLC in an optimized manner, grouping reads by data block to improve performance. \u003cremarks\u003eIf the tagMapping dictionary is null or empty, the method returns a successful result with no values. Tags are grouped by data block to minimize communication overhead. If a tag does not exist in the PLC's tag list, it is added before reading. Each tag read is subject to the specified timeout.\u003c/remarks\u003e \u003ctypeparam name=\"T\"\u003eThe type of the values to read from the PLC tags.\u003c/typeparam\u003e \u003cparam name=\"plc\"\u003eThe PLC instance that provides access to the tags to be read. Cannot be null.\u003c/param\u003e \u003cparam name=\"tagMapping\"\u003eA dictionary mapping logical tag names to their corresponding PLC addresses. Each key is the tag name, and each value is the PLC address to read.\u003c/param\u003e \u003cparam name=\"timeoutMs\"\u003eThe maximum time, in milliseconds, to wait for each tag read operation before timing out. The default is 5000 milliseconds.\u003c/param\u003e \u003creturns\u003eA task that represents the asynchronous operation. The task result contains a BatchReadResult{T} with the values read, per-tag success status, and any errors encountered.\u003c/returns\u003e \u003cexception cref=\"ArgumentNullException\"\u003eThrown if the plc parameter is null.\u003c/exception\u003e |\n| `public static async Task\u003cBatchWriteResult\u003e WriteBatchOptimized\u003cT\u003e( this IRxS7 plc, Dictionary\u003cstring, T\u003e values, bool verifyWrites = false, bool enableRollback = false)` | Writes a batch of values to the specified PLC in an optimized manner, with optional write verification and rollback support. \u003cremarks\u003eIf enableRollback is set to true and any write fails, the method attempts to restore all affected PLC addresses to their original values. Write verification, if enabled, reads back each value after writing to ensure correctness. This method is asynchronous and should be awaited to ensure completion of all write and verification operations.\u003c/remarks\u003e \u003ctypeparam name=\"T\"\u003eThe type of the values to write to the PLC.\u003c/typeparam\u003e \u003cparam name=\"plc\"\u003eThe PLC instance to which the values will be written. Cannot be null.\u003c/param\u003e \u003cparam name=\"values\"\u003eA dictionary mapping PLC variable addresses to the values to write. Each key represents a PLC address, and each value is the data to write to that address. If the dictionary is null or empty, no write operations are performed.\u003c/param\u003e \u003cparam name=\"verifyWrites\"\u003etrue to verify each write by reading back the value after writing; otherwise, false. Verification may increase operation time.\u003c/param\u003e \u003cparam name=\"enableRollback\"\u003etrue to enable rollback of all written values to their original state if any write fails; otherwise, false. Rollback is attempted only if an error occurs during the batch write.\u003c/param\u003e \u003creturns\u003eA BatchWriteResult object containing the outcome of the batch write operation, including per-address success and error information. If no values are provided, the result indicates overall success.\u003c/returns\u003e \u003cexception cref=\"ArgumentNullException\"\u003eThrown if plc is null.\u003c/exception\u003e |\n| `public static async Task\u003cProductionDiagnostics\u003e GetDiagnostics(this IRxS7 plc)` | Asynchronously collects diagnostic information and performance metrics from the specified PLC instance. \u003cremarks\u003eIf the PLC is not connected, only basic information is included in the diagnostics. For S7-1500 PLCs, additional CPU information and connection latency are measured. Any errors encountered during diagnostic collection are recorded in the Errors property of the returned ProductionDiagnostics object.\u003c/remarks\u003e \u003cparam name=\"plc\"\u003eThe PLC instance from which to retrieve diagnostics. Must implement the IRxS7 interface.\u003c/param\u003e \u003creturns\u003eA task that represents the asynchronous operation. The task result contains a ProductionDiagnostics object with collected diagnostic data, tag metrics, and optimization recommendations.\u003c/returns\u003e \u003cexception cref=\"ArgumentNullException\"\u003eThrown if the plc parameter is null.\u003c/exception\u003e |\n| `public static async Task\u003cPerformanceAnalysis\u003e AnalyzePerformance(this IRxS7 plc, TimeSpan monitoringDuration)` | Analyzes tag change performance on the specified PLC over a given monitoring duration and returns a summary of tag change frequencies and recommendations. \u003cremarks\u003eThis method subscribes to all tag changes on the PLC and tracks the frequency of changes for each tag during the specified monitoring period. The analysis includes total tag changes, per-tag change counts, and suggestions for optimizing performance based on observed activity. The method is thread-safe and does not block the calling thread.\u003c/remarks\u003e \u003cparam name=\"plc\"\u003eThe PLC instance to monitor for tag changes. Cannot be null.\u003c/param\u003e \u003cparam name=\"monitoringDuration\"\u003eThe length of time to observe tag changes. Must be a positive time span.\u003c/param\u003e \u003creturns\u003eA task that represents the asynchronous operation. The task result contains a PerformanceAnalysis object with tag change statistics and performance recommendations for the monitored period.\u003c/returns\u003e \u003cexception cref=\"ArgumentNullException\"\u003eThrown if the plc parameter is null.\u003c/exception\u003e |\n| `public static HighPerformanceTagGroup\u003cT\u003e CreateTagGroup\u003cT\u003e(this IRxS7 plc, string groupName, params string[] tagNames) =\u003e ...` | Creates a new high-performance tag group for batch reading or writing of multiple tags from the specified PLC connection. \u003cremarks\u003eUse this method to efficiently manage and operate on multiple tags as a single group, which can improve performance for batch operations.\u003c/remarks\u003e \u003ctypeparam name=\"T\"\u003eThe data type of the tag values in the group.\u003c/typeparam\u003e \u003cparam name=\"plc\"\u003eThe PLC connection used to access the tags. Cannot be null.\u003c/param\u003e \u003cparam name=\"groupName\"\u003eThe name assigned to the tag group. Used to identify the group within the PLC context.\u003c/param\u003e \u003cparam name=\"tagNames\"\u003eAn array of tag names to include in the group. Each name must correspond to a valid tag in the PLC.\u003c/param\u003e \u003creturns\u003eA new instance of HighPerformanceTagGroup{T} containing the specified tags, associated with the given PLC connection.\u003c/returns\u003e |\n\n##### `S7PlcRx.Advanced.AsyncExtensions`\nSource: `Advanced/AsyncExtensions.cs:18`\n\nProvides additional async-first helpers for reading, writing, and observing PLC values without changing the base \u003csee cref=\"IRxS7\"/\u003e API surface. \u003cremarks\u003eThese helpers layer \u003csee cref=\"ValueTask\"/\u003e and async-observable patterns over the existing PLC API. Where possible, they complete synchronously from cached tag values or the existing multi-variable read/write paths to reduce avoidable allocations.\u003c/remarks\u003e\n\n| Member | Summary |\n|---|---|\n| `public static ValueTask\u003cT?\u003e ReadValueAsync\u003cT\u003e(this IRxS7 plc, string? variable, CancellationToken cancellationToken = default)` | Reads a PLC value using a \u003csee cref=\"ValueTask{TResult}\"/\u003e, returning synchronously when a compatible cached tag value is already available. \u003ctypeparam name=\"T\"\u003eThe expected PLC value type.\u003c/typeparam\u003e \u003cparam name=\"plc\"\u003eThe PLC instance.\u003c/param\u003e \u003cparam name=\"variable\"\u003eThe tag name to read.\u003c/param\u003e \u003cparam name=\"cancellationToken\"\u003eThe cancellation token for the read operation.\u003c/param\u003e \u003creturns\u003eA \u003csee cref=\"ValueTask{TResult}\"/\u003e that resolves to the current PLC value.\u003c/returns\u003e |\n| `public static ValueTask\u003cDictionary\u003cstring, T?\u003e\u003e ReadValuesAsync\u003cT\u003e(this IRxS7 plc, params string[] variables) =\u003e ...` | Reads multiple PLC values using a \u003csee cref=\"ValueTask{TResult}\"/\u003e, preferring cached values and the optimized multi-variable read path where available. \u003ctypeparam name=\"T\"\u003eThe expected PLC value type.\u003c/typeparam\u003e \u003cparam name=\"plc\"\u003eThe PLC instance.\u003c/param\u003e \u003cparam name=\"variables\"\u003eThe tag names to read.\u003c/param\u003e \u003creturns\u003eA \u003csee cref=\"ValueTask{TResult}\"/\u003e that resolves to a dictionary of tag values keyed by tag name.\u003c/returns\u003e |\n| `public static ValueTask\u003cDictionary\u003cstring, T?\u003e\u003e ReadValuesAsync\u003cT\u003e(this IRxS7 plc, IReadOnlyList\u003cstring\u003e variables, CancellationToken cancellationToken = default)` | Reads multiple PLC values using a \u003csee cref=\"ValueTask{TResult}\"/\u003e, honoring cancellation for deferred reads. \u003ctypeparam name=\"T\"\u003eThe expected PLC value type.\u003c/typeparam\u003e \u003cparam name=\"plc\"\u003eThe PLC instance.\u003c/param\u003e \u003cparam name=\"variables\"\u003eThe tag names to read.\u003c/param\u003e \u003cparam name=\"cancellationToken\"\u003eThe cancellation token for deferred reads.\u003c/param\u003e \u003creturns\u003eA \u003csee cref=\"ValueTask{TResult}\"/\u003e that resolves to a dictionary of tag values keyed by tag name.\u003c/returns\u003e |\n| `public static ValueTask WriteValuesAsync\u003cT\u003e(this IRxS7 plc, IReadOnlyDictionary\u003cstring, T\u003e values, CancellationToken cancellationToken = default)` | Writes multiple PLC values using a \u003csee cref=\"ValueTask\"/\u003e, preferring the optimized multi-variable write path where available. \u003ctypeparam name=\"T\"\u003eThe PLC value type.\u003c/typeparam\u003e \u003cparam name=\"plc\"\u003eThe PLC instance.\u003c/param\u003e \u003cparam name=\"values\"\u003eThe tag values to write.\u003c/param\u003e \u003cparam name=\"cancellationToken\"\u003eThe cancellation token.\u003c/param\u003e \u003creturns\u003eA completed \u003csee cref=\"ValueTask\"/\u003e when the writes have been issued.\u003c/returns\u003e |\n| `public static IObservableAsync\u003cT?\u003e ObserveValueAsync\u003cT\u003e(this IRxS7 plc, string? variable)` | Exposes a PLC variable as an async observable sequence. \u003ctypeparam name=\"T\"\u003eThe expected PLC value type.\u003c/typeparam\u003e \u003cparam name=\"plc\"\u003eThe PLC instance.\u003c/param\u003e \u003cparam name=\"variable\"\u003eThe tag name to observe.\u003c/param\u003e \u003creturns\u003eAn \u003csee cref=\"IObservableAsync{T}\"/\u003e that emits tag value updates asynchronously.\u003c/returns\u003e |\n| `public static IObservableAsync\u003cDictionary\u003cstring, T?\u003e\u003e ObserveValuesAsync\u003cT\u003e(this IRxS7 plc, params string[] variables)` | Exposes a batch PLC observation as an async observable sequence. \u003ctypeparam name=\"T\"\u003eThe expected PLC value type.\u003c/typeparam\u003e \u003cparam name=\"plc\"\u003eThe PLC instance.\u003c/param\u003e \u003cparam name=\"variables\"\u003eThe tag names to observe.\u003c/param\u003e \u003creturns\u003eAn \u003csee cref=\"IObservableAsync{T}\"/\u003e that emits dictionaries of tag values asynchronously.\u003c/returns\u003e |\n\n##### `S7PlcRx.Advanced.DictionaryEqualityComparer\u003cTKey, TValue\u003e`\nSource: `Advanced/DictionaryEqualityComparer.cs:15`\n\nProvides an equality comparer for dictionaries that determines equality based on their key-value pairs. \u003cremarks\u003eThis comparer considers two dictionaries equal if they contain the same number of key-value pairs and each key in one dictionary exists in the other with an equal value, as determined by the default equality comparer for the value type. The order of key-value pairs does not affect equality. This comparer can be used to compare dictionaries in collections such as hash sets or as keys in other dictionaries.\u003c/remarks\u003e \u003ctypeparam name=\"TKey\"\u003eThe type of keys in the dictionaries. Must be non-nullable.\u003c/typeparam\u003e \u003ctypeparam name=\"TValue\"\u003eThe type of values in the dictionaries.\u003c/typeparam\u003e\n\nNo public instance/static members declared directly on this type.\n\n#### Namespace `S7PlcRx.BatchOperations`\n\n##### `S7PlcRx.BatchOperations.BatchOperationResult`\nSource: `BatchOperations/BatchOperationResult.cs:14`\n\nRepresents the result of executing a batch operation, including summary statistics and details for each operation. \u003cremarks\u003eUse this class to access aggregate information such as the number of successful and failed operations, processing times, and detailed results for each operation in the batch. The class provides both summary properties and collections for per-operation and error details.\u003c/remarks\u003e\n\n| Member | Summary |\n|---|---|\n| `public DateTime StartTime get; set; }` | Gets or sets the operation start time. |\n| `public DateTime EndTime get; set; }` | Gets or sets the operation end time. |\n| `public int OperationCount get; set; }` | Gets or sets the number of operations in the batch. |\n| `public int SuccessfulOperations get; set; }` | Gets or sets the number of successful operations. |\n| `public int FailedOperations get; set; }` | Gets or sets the number of failed operations. |\n| `public TimeSpan ProcessingTime =\u003e ...` | Gets the total processing time. |\n| `public double AverageTimePerOperation =\u003e ...` | Gets the average time per operation. |\n| `public List\u003cOperationDetail\u003e OperationDetails get; } = [];` | Gets operation details. |\n| `public List\u003cstring\u003e ErrorDetails get; } = [];` | Gets error details for failed operations. |\n\n##### `S7PlcRx.BatchOperations.BatchReadResult\u003cT\u003e`\nSource: `BatchOperations/BatchReadResult.cs:17`\n\nRepresents the result of a batch read operation, including the values read, per-tag success status, error messages, and overall success information. \u003cremarks\u003eUse this class to access the outcome of a batch read, including which tags succeeded, which failed, and any associated error messages. The dictionaries provide per-tag details, while the overall success and count properties offer summary information. This class is typically used in scenarios where multiple items are read in a single operation and individual results must be tracked.\u003c/remarks\u003e \u003ctypeparam name=\"T\"\u003eThe type of the values returned for each tag in the batch read operation.\u003c/typeparam\u003e\n\n| Member | Summary |\n|---|---|\n| `public Dictionary\u003cstring, T\u003e Values get; } = [];` | Gets the successfully read values. |\n| `public Dictionary\u003cstring, bool\u003e Success get; } = [];` | Gets the success status for each tag. |\n| `public Dictionary\u003cstring, string\u003e Errors get; } = [];` | Gets error messages for failed reads. |\n| `public bool OverallSuccess get; set; }` | Gets or sets a value indicating whether gets whether all reads were successful. |\n| `public int SuccessCount =\u003e ...` | Gets the count of successful reads. |\n| `public int ErrorCount =\u003e ...` | Gets the count of failed reads. |\n\n##### `S7PlcRx.BatchOperations.BatchWriteResult`\nSource: `BatchOperations/BatchWriteResult.cs:15`\n\nRepresents the result of a batch write operation, including per-item success status, error messages, and overall outcome. \u003cremarks\u003eUse this class to inspect which items in a batch write succeeded or failed, retrieve error details for failed items, and determine whether the entire batch was successful or if a rollback was performed. The dictionaries map item identifiers (such as tag names) to their respective statuses and error messages.\u003c/remarks\u003e\n\n| Member | Summary |\n|---|---|\n| `public Dictionary\u003cstring, bool\u003e Success get; } = [];` | Gets the success status for each tag. |\n| `public Dictionary\u003cstring, string\u003e Errors get; } = [];` | Gets error messages for failed writes. |\n| `public bool OverallSuccess get; set; }` | Gets or sets a value indicating whether gets whether all writes were successful. |\n| `public bool RollbackPerformed get; set; }` | Gets or sets a value indicating whether gets whether rollback was performed. |\n| `public int SuccessCount =\u003e ...` | Gets the count of successful writes. |\n| `public int ErrorCount =\u003e ...` | Gets the count of failed writes. |\n\n#### Namespace `S7PlcRx.Binding`\n\n##### `S7PlcRx.Binding.S7TagDefinition`\nSource: `Binding/S7TagDefinition.cs:9`\n\nDescribes a generated PLC tag/property binding.\n\n| Member | Summary |\n|---|---|\n| `public S7TagDefinition(string name, string address, Type valueType, int pollIntervalMs, S7TagDirection direction, int arrayLength = 1)` | Initializes a new instance of the \u003csee cref=\"S7TagDefinition\"/\u003e class. \u003cparam name=\"name\"\u003eThe property and PLC tag name.\u003c/param\u003e \u003cparam name=\"address\"\u003eThe S7 DB address.\u003c/param\u003e \u003cparam name=\"valueType\"\u003eThe .NET value type.\u003c/param\u003e \u003cparam name=\"pollIntervalMs\"\u003eThe read polling interval in milliseconds.\u003c/param\u003e \u003cparam name=\"direction\"\u003eThe tag access direction.\u003c/param\u003e \u003cparam name=\"arrayLength\"\u003eThe array/string element length.\u003c/param\u003e |\n| `public string Name get; }` | Gets the property and PLC tag name. |\n| `public string Address get; }` | Gets the S7 DB address. |\n| `public Type ValueType get; }` | Gets the .NET value type. |\n| `public int PollIntervalMs get; }` | Gets the read polling interval in milliseconds. |\n| `public S7TagDirection Direction get; }` | Gets the tag access direction. |\n| `public int ArrayLength get; }` | Gets the array/string element length. |\n| `public bool CanRead =\u003e ...` | Gets a value indicating whether this tag should be read on polling intervals. |\n| `public bool CanWrite =\u003e ...` | Gets a value indicating whether this tag can write property changes to the PLC. |\n\n##### `S7PlcRx.Binding.S7TagDirection`\nSource: `Binding/S7TagDirection.cs:9`\n\nDefines the PLC access direction for a generated tag binding.\n\nEnum values: `ReadWrite`, `ReadOnly`, `WriteOnly`.\n\n##### `S7PlcRx.Binding.S7TagRuntimeBinding`\nSource: `Binding/S7TagRuntimeBinding.cs:15`\n\nRuntime engine used by generated tag bindings to poll and write PLC DB values in byte-array batches.\n\n| Member | Summary |\n|---|---|\n| `public static S7TagRuntimeBinding Bind(IRxS7 plc, IReadOnlyList\u003cS7TagDefinition\u003e definitions, Action\u003cstring, object?\u003e applyRead) =\u003e ...` | Creates and starts a runtime binding for generated PLC tag definitions. \u003cparam name=\"plc\"\u003eThe PLC instance.\u003c/param\u003e \u003cparam name=\"definitions\"\u003eThe tag definitions emitted by the source generator.\u003c/param\u003e \u003cparam name=\"applyRead\"\u003eA generated callback that assigns PLC values to backing fields without re-writing them.\u003c/param\u003e \u003creturns\u003eA disposable runtime binding.\u003c/returns\u003e |\n| `public void Write(string name, object? value)` | Queues a generated property change for a grouped byte-array write. \u003cparam name=\"name\"\u003eThe generated tag/property name.\u003c/param\u003e \u003cparam name=\"value\"\u003eThe new property value.\u003c/param\u003e |\n| `public void Dispose()` | Releases timers and pending write state. |\n\n#### Namespace `S7PlcRx.Cache`\n\n##### `S7PlcRx.Cache.CacheStatistics`\nSource: `Cache/CacheStatistics.cs:13`\n\nProvides statistical information about the state and performance of a cache, including entry counts, hit rates, and entry timestamps. \u003cremarks\u003eUse this class to monitor cache usage patterns and effectiveness. The statistics can help identify cache performance issues or guide tuning decisions. All values represent a snapshot at the time the object is created or updated; they do not update automatically.\u003c/remarks\u003e\n\n| Member | Summary |\n|---|---|\n| `public int TotalEntries get; set; }` | Gets or sets the total number of cached entries. |\n| `public long TotalHits get; set; }` | Gets or sets the total number of cache hits. |\n| `public double HitRate get; set; }` | Gets or sets the cache hit rate (0.0 to 1.0). |\n| `public DateTime OldestEntry get; set; }` | Gets or sets the timestamp of the oldest cache entry. |\n| `public DateTime NewestEntry get; set; }` | Gets or sets the timestamp of the newest cache entry. |\n| `public int CachedValueCount get; internal set; }` | Gets the cached value count. \u003cvalue\u003e The cached value count. \u003c/value\u003e |\n| `public int PendingRequestCount get; internal set; }` | Gets the pending request count. \u003cvalue\u003e The pending request count. \u003c/value\u003e |\n| `public double CacheHitRatio get; internal set; }` | Gets the cache hit ratio. \u003cvalue\u003e The cache hit ratio. \u003c/value\u003e |\n\n##### `S7PlcRx.Cache.CachedTagValue`\nSource: `Cache/CachedTagValue.cs:12`\n\nRepresents a cached value along with metadata about its storage and usage. \u003cremarks\u003eThis class is typically used to store a value retrieved from a data source, along with the time it was cached and the number of times it has been accessed. It is intended for use in caching scenarios where tracking cache usage and freshness is important.\u003c/remarks\u003e\n\n| Member | Summary |\n|---|---|\n| `public object? Value get; set; }` | Gets or sets the cached value. |\n| `public DateTime Timestamp get; set; }` | Gets or sets when the value was cached. |\n| `public long HitCount get; set; }` | Gets or sets the number of cache hits. |\n\n#### Namespace `S7PlcRx.Core`\n\n##### `S7PlcRx.Core.ConnectionPool`\nSource: `Core/ConnectionPool.cs:17`\n\nManages a pool of PLC connections, providing load-balanced access and connection reuse according to the specified configuration. \u003cremarks\u003eThe ConnectionPool enables efficient management of multiple PLC connections by reusing and balancing requests across available connections. It supports configurable pool size and connection reuse strategies. This class is thread-safe for concurrent access. Call Dispose to release all connections when the pool is no longer needed.\u003c/remarks\u003e\n\n| Member | Summary |\n|---|---|\n| `public ConnectionPool(ConnectionPoolConfig config) =\u003e ...` | Initializes a new instance of the \u003csee cref=\"ConnectionPool\"/\u003e class. \u003cparam name=\"config\"\u003eThe pool configuration.\u003c/param\u003e |\n| `public ConnectionPool( IEnumerable\u003cPlcConnectionConfig\u003e connectionConfigs, ConnectionPoolConfig poolConfig)` | Initializes a new instance of the \u003csee cref=\"ConnectionPool\"/\u003e class. \u003cparam name=\"connectionConfigs\"\u003eThe connection configurations.\u003c/param\u003e \u003cparam name=\"poolConfig\"\u003eThe pool configuration.\u003c/param\u003e |\n| `public int MaxConnections =\u003e ...` | Gets the maximum number of connections in the pool. |\n| `public int ActiveConnections =\u003e ...` | Gets the number of active connections. |\n| `public IRxS7 GetConnection` | Gets a connection from the pool using load balancing. \u003creturns\u003eAn available PLC connection.\u003c/returns\u003e |\n| `public IEnumerable\u003cIRxS7\u003e GetAllConnections() =\u003e ...` | Gets all connections in the pool. \u003creturns\u003eAll connections.\u003c/returns\u003e |\n| `public void Dispose()` | Disposes all connections in the pool. |\n\n##### `S7PlcRx.Core.ConnectionPoolConfig`\nSource: `Core/ConnectionPoolConfig.cs:12`\n\nRepresents the configuration settings for a connection pool, including limits, timeouts, and behavior options. \u003cremarks\u003eUse this class to specify parameters that control the size, performance, and health monitoring of a connection pool. Adjusting these settings can help optimize resource usage and connection reliability for applications that manage multiple concurrent connections.\u003c/remarks\u003e\n\n| Member | Summary |\n|---|---|\n| `public int MaxPoolSize get; set; } = 10;` | Gets or sets the maximum pool size. |\n| `public int MaxConnections get; set; } = 10;` | Gets or sets the maximum number of connections in the pool. |\n| `public TimeSpan ConnectionTimeout get; set; } = TimeSpan.FromSeconds(30);` | Gets or sets the connection timeout. |\n| `public bool EnableLoadBalancing get; set; } = true;` | Gets or sets a value indicating whether to enable load balancing. |\n| `public bool EnableConnectionReuse get; set; } = true;` | Gets or sets a value indicating whether to enable connection reuse. |\n| `public TimeSpan HealthCheckInterval get; set; } = TimeSpan.FromMinutes(1);` | Gets or sets the health check interval. |\n\n##### `S7PlcRx.Core.DataBlockInfo`\nSource: `Core/DataBlockInfo.cs:14`\n\nRepresents metadata and configuration information for a data block, including its identifier, size, tag details, access frequency, and optimization settings. \u003cremarks\u003eUse this class to describe the characteristics of a data block, such as its block number, size in bytes, and associated tag names. The properties provide information useful for managing, analyzing, or optimizing data storage and access patterns. Instances of this class are typically used in scenarios where data blocks are processed, monitored, or configured for batch operations.\u003c/remarks\u003e\n\n| Member | Summary |\n|---|---|\n| `public int BlockNumber get; set; }` | Gets or sets the data block number. |\n| `public int SizeBytes get; set; }` | Gets or sets the total size in bytes. |\n| `public int TagCount get; set; }` | Gets or sets the number of tags in this block. |\n| `public double AccessFrequency get; set; }` | Gets or sets the access frequency. |\n| `public bool IsBatchOptimized get; set; }` | Gets or sets a value indicating whether gets or sets whether the block is optimized for batch operations. |\n| `public List\u003cstring\u003e TagNames get; } = [];` | Gets the tags in this data block. |\n\n##### `S7PlcRx.Core.OperationDetail`\nSource: `Core/OperationDetail.cs:9`\n\nRepresents the details of an operation, including its type, status, duration, and related metadata.\n\n| Member | Summary |\n|---|---|\n| `public string TagName get; set; } = string.Empty;` | Gets or sets the tag name. |\n| `public string OperationType get; set; } = string.Empty;` | Gets or sets the operation type. |\n| `public bool Success get; set; }` | Gets or sets a value indicating whether gets or sets whether the operation succeeded. |\n| `public TimeSpan Duration get; set; }` | Gets or sets the operation duration. |\n| `public string? ErrorMessage get; set; }` | Gets or sets any error message. |\n| `public int DataBlockNumber get; set; }` | Gets or sets the data block number. |\n\n##### `S7PlcRx.Core.RequestPriority`\nSource: `Core/RequestPriority.cs:9`\n\nRequest priority levels for batch processing.\n\nEnum values: `Low`, `Normal`, `High`, `Critical`.\n\n#### Namespace `S7PlcRx.Enterprise`\n\n##### `S7PlcRx.Enterprise.EnterpriseExtensions`\nSource: `Enterprise/EnterpriseExtensions.cs:19`\n\nProvides extension methods for enhanced PLC connectivity, symbolic addressing, high-availability management, and connection pooling in enterprise automation scenarios. \u003cremarks\u003eThe EnterpriseExtensions class offers advanced features for working with PLCs, including loading and caching symbol tables for symbolic access, reading and writing values by symbol name, creating high-availability connections with automatic failover, and managing connection pools for high-throughput applications. These methods are designed to simplify integration with industrial automation systems and improve reliability and scalability in production environments.\u003c/remarks\u003e\n\n| Member | Summary |\n|---|---|\n| `public static async Task\u003cSymbolTable\u003e LoadSymbolTable( this IRxS7 plc, string symbolTableData, SymbolTableFormat format = SymbolTableFormat.Csv)` | Loads and caches a symbol table for symbolic addressing support. Enables reading/writing using symbolic names instead of absolute addresses. \u003cparam name=\"plc\"\u003eThe PLC instance.\u003c/param\u003e \u003cparam name=\"symbolTableData\"\u003eSymbol table data (CSV format supported).\u003c/param\u003e \u003cparam name=\"format\"\u003eThe format of the symbol table data.\u003c/param\u003e \u003creturns\u003eThe loaded symbol table.\u003c/returns\u003e |\n| `public static async Task\u003cT?\u003e ReadSymbol\u003cT\u003e(this IRxS7 plc, string symbolName)` | Asynchronously reads the value of the specified symbol from the PLC and returns it as the specified type. \u003ctypeparam name=\"T\"\u003eThe type to which the symbol's value is converted and returned.\u003c/typeparam\u003e \u003cparam name=\"plc\"\u003eThe PLC connection used to access the symbol table and read the symbol value. Cannot be null.\u003c/param\u003e \u003cparam name=\"symbolName\"\u003eThe name of the symbol to read from the PLC. Must correspond to a symbol present in the PLC's symbol table.\u003c/param\u003e \u003creturns\u003eA task that represents the asynchronous read operation. The task result contains the value of the symbol as type \u003ctypeparamref name=\"T\"/\u003e, or \u003csee langword=\"null\"/\u003e if the symbol's value is null.\u003c/returns\u003e \u003cexception cref=\"ArgumentNullException\"\u003eThrown if \u003cparamref name=\"plc\"/\u003e is null.\u003c/exception\u003e \u003cexception cref=\"ArgumentException\"\u003eThrown if a symbol with the specified \u003cparamref name=\"symbolName\"/\u003e does not exist in the PLC's symbol table.\u003c/exception\u003e |\n| `public static void WriteSymbol\u003cT\u003e(this IRxS7 plc, string symbolName, T value)` | Writes a value to the specified PLC symbol by name. \u003ctypeparam name=\"T\"\u003eThe type of the value to write to the symbol.\u003c/typeparam\u003e \u003cparam name=\"plc\"\u003eThe PLC instance to which the symbol value will be written. Cannot be null.\u003c/param\u003e \u003cparam name=\"symbolName\"\u003eThe name of the symbol in the PLC to write the value to. Must correspond to a valid symbol in the PLC's symbol table.\u003c/param\u003e \u003cparam name=\"value\"\u003eThe value to write to the specified symbol.\u003c/param\u003e \u003cexception cref=\"ArgumentNullException\"\u003eThrown if \u003cparamref name=\"plc\"/\u003e is null.\u003c/exception\u003e \u003cexception cref=\"ArgumentException\"\u003eThrown if \u003cparamref name=\"symbolName\"/\u003e does not correspond to a symbol in the PLC's symbol table.\u003c/exception\u003e |\n| `public static HighAvailabilityPlcManager CreateHighAvailabilityConnection( IRxS7 primaryPlc, IList\u003cIRxS7\u003e backupPlcs, TimeSpan? healthCheckInterval = null)` | Creates a high-availability connection manager that coordinates failover between a primary PLC and one or more backup PLCs. \u003cremarks\u003eThe returned manager automatically monitors the health of the primary and backup PLCs and handles failover as needed. The order of backupPlcs determines the failover priority.\u003c/remarks\u003e \u003cparam name=\"primaryPlc\"\u003eThe primary PLC instance to be used for initial communication and operations. Cannot be null.\u003c/param\u003e \u003cparam name=\"backupPlcs\"\u003eA list of backup PLC instances to be used for failover if the primary PLC becomes unavailable. Cannot be null or empty.\u003c/param\u003e \u003cparam name=\"healthCheckInterval\"\u003eThe interval at which the health of the PLCs is checked. If null, a default interval is used.\u003c/param\u003e \u003creturns\u003eA HighAvailabilityPlcManager instance that manages high-availability communication across the specified PLCs.\u003c/returns\u003e \u003cexception cref=\"ArgumentNullException\"\u003eThrown if primaryPlc or backupPlcs is null.\u003c/exception\u003e |\n| `public static ConnectionPool CreateConnectionPool( IEnumerable\u003cPlcConnectionConfig\u003e connectionConfigs, ConnectionPoolConfig poolConfig)` | Creates a new connection pool using the specified PLC connection configurations and pool settings. \u003cparam name=\"connectionConfigs\"\u003eA collection of PLC connection configurations to include in the pool. Must contain at least one configuration.\u003c/param\u003e \u003cparam name=\"poolConfig\"\u003eThe configuration settings to apply to the connection pool. Cannot be null.\u003c/param\u003e \u003creturns\u003eA new instance of \u003csee cref=\"ConnectionPool\"/\u003e initialized with the provided connection configurations and pool settings.\u003c/returns\u003e \u003cexception cref=\"ArgumentNullException\"\u003eThrown if \u003cparamref name=\"connectionConfigs\"/\u003e or \u003cparamref name=\"poolConfig\"/\u003e is null.\u003c/exception\u003e \u003cexception cref=\"ArgumentException\"\u003eThrown if \u003cparamref name=\"connectionConfigs\"/\u003e does not contain at least one configuration.\u003c/exception\u003e |\n\n##### `S7PlcRx.Enterprise.HighAvailabilityPlcManager`\nSource: `Enterprise/HighAvailabilityPlcManager.cs:17`\n\nProvides high-availability management for a set of PLC (Programmable Logic Controller) connections, automatically handling failover to backup PLCs in case of connection loss. \u003cremarks\u003eThe HighAvailabilityPlcManager monitors the health of the primary PLC and automatically switches to a backup PLC if the primary becomes unavailable. It exposes an observable stream of failover events for monitoring and allows manual triggering of failover. This class is thread-safe for typical usage scenarios. Dispose the manager when it is no longer needed to release resources.\u003c/remarks\u003e\n\n| Member | Summary |\n|---|---|\n| `public HighAvailabilityPlcManager( IRxS7 primaryPlc, IList\u003cIRxS7\u003e backupPlcs, TimeSpan? healthCheckInterval = null)` | Initializes a new instance of the \u003csee cref=\"HighAvailabilityPlcManager\"/\u003e class with a primary PLC, a list of backup PLCs,. and an optional health check interval. \u003cremarks\u003eThe primary PLC is always treated as the first PLC in the managed list, regardless of its position in the provided backupPlcs collection. Health checks are performed at the specified interval to monitor PLC availability and facilitate failover if necessary.\u003c/remarks\u003e \u003cparam name=\"primaryPlc\"\u003eThe primary PLC to be managed. Cannot be null.\u003c/param\u003e \u003cparam name=\"backupPlcs\"\u003eA list of backup PLCs to use for failover. The primary PLC will be inserted as the first element in this list.\u003c/param\u003e \u003cparam name=\"healthCheckInterval\"\u003eThe interval at which health checks are performed on the PLCs. If null, a default interval of 30 seconds is used.\u003c/param\u003e \u003cexception cref=\"ArgumentNullException\"\u003eThrown if primaryPlc is null.\u003c/except","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fchrispulman%2Fs7plcrx","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fchrispulman%2Fs7plcrx","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fchrispulman%2Fs7plcrx/lists"}