Auto: s7-d1 — TIA Portal CSV + STEP 7 Classic AWL symbol import
Closes #299
This commit is contained in:
@@ -1,6 +1,10 @@
|
||||
using System.IO;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Logging.Abstractions;
|
||||
using ZB.MOM.WW.OtOpcUa.Core.Hosting;
|
||||
using ZB.MOM.WW.OtOpcUa.Driver.S7.SymbolImport;
|
||||
using S7NetCpuType = global::S7.Net.CpuType;
|
||||
|
||||
namespace ZB.MOM.WW.OtOpcUa.Driver.S7;
|
||||
@@ -102,6 +106,128 @@ public static class S7DriverFactoryExtensions
|
||||
DeadbandAbsolute: t.DeadbandAbsolute,
|
||||
DeadbandPercent: t.DeadbandPercent);
|
||||
|
||||
/// <summary>
|
||||
/// PR-S7-D1 / #299 — append TIA Portal "Show all tags" CSV rows to
|
||||
/// <paramref name="options"/> as <see cref="S7TagDefinition"/> entries. Returns a new
|
||||
/// <see cref="S7DriverOptions"/> with the imported tags concatenated onto the existing
|
||||
/// <c>Tags</c> list — useful both at startup-time (server-side bootstrap that wants
|
||||
/// to seed a device's address space from a customer-supplied CSV) and from the CLI
|
||||
/// (<c>import-symbols</c> emits the resulting JSON fragment for hand-merging into an
|
||||
/// appsettings file).
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// The importer is permissive by default — malformed rows are logged and skipped;
|
||||
/// the resulting <see cref="S7ImportResult"/> counts surface on
|
||||
/// <paramref name="result"/> for callers that want to assert "we got the row count
|
||||
/// we expected".
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// UDT-typed rows materialise as placeholder tags (data type forced to
|
||||
/// <see cref="S7DataType.Byte"/>); PR-S7-D2 will replace the placeholders with
|
||||
/// proper UDT layout. See <c>docs/drivers/S7-TIA-Import.md</c>.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public static S7DriverOptions AddTiaCsvImport(
|
||||
this S7DriverOptions options,
|
||||
string path,
|
||||
out S7ImportResult result,
|
||||
S7ImportOptions? importOptions = null,
|
||||
ILogger<TiaCsvImporter>? logger = null)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(options);
|
||||
ArgumentException.ThrowIfNullOrWhiteSpace(path);
|
||||
|
||||
using var stream = File.OpenRead(path);
|
||||
var importer = new TiaCsvImporter(logger ?? NullLogger<TiaCsvImporter>.Instance);
|
||||
result = importer.Parse(stream, importOptions);
|
||||
|
||||
return MergeImportedTags(options, result.Tags);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// CLI-friendly overload that returns the <see cref="S7ImportResult"/> alongside the
|
||||
/// modified options as a tuple. Mirrors <see cref="AddTiaCsvImport"/> but avoids the
|
||||
/// <c>out</c> parameter for call sites that prefer pattern-matched destructuring.
|
||||
/// </summary>
|
||||
public static (S7DriverOptions Options, S7ImportResult Result) AddTiaCsvImportWithResult(
|
||||
this S7DriverOptions options,
|
||||
string path,
|
||||
S7ImportOptions? importOptions = null,
|
||||
ILogger<TiaCsvImporter>? logger = null)
|
||||
{
|
||||
var updated = options.AddTiaCsvImport(path, out var result, importOptions, logger);
|
||||
return (updated, result);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// PR-S7-D1 / #299 — append STEP 7 Classic AWL <c>VAR_GLOBAL</c> + <c>DATA_BLOCK</c>
|
||||
/// declarations to <paramref name="options"/> as <see cref="S7TagDefinition"/> entries.
|
||||
/// Best-effort heuristic — see <see cref="AwlImporter"/> for the position-based
|
||||
/// addressing rules.
|
||||
/// </summary>
|
||||
public static S7DriverOptions AddAwlImport(
|
||||
this S7DriverOptions options,
|
||||
string path,
|
||||
out S7ImportResult result,
|
||||
S7ImportOptions? importOptions = null,
|
||||
ILogger<AwlImporter>? logger = null)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(options);
|
||||
ArgumentException.ThrowIfNullOrWhiteSpace(path);
|
||||
|
||||
using var stream = File.OpenRead(path);
|
||||
var importer = new AwlImporter(logger ?? NullLogger<AwlImporter>.Instance);
|
||||
result = importer.Parse(stream, importOptions);
|
||||
|
||||
return MergeImportedTags(options, result.Tags);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// CLI-friendly overload that returns the <see cref="S7ImportResult"/> alongside the
|
||||
/// modified options as a tuple. Mirrors <see cref="AddAwlImport"/>.
|
||||
/// </summary>
|
||||
public static (S7DriverOptions Options, S7ImportResult Result) AddAwlImportWithResult(
|
||||
this S7DriverOptions options,
|
||||
string path,
|
||||
S7ImportOptions? importOptions = null,
|
||||
ILogger<AwlImporter>? logger = null)
|
||||
{
|
||||
var updated = options.AddAwlImport(path, out var result, importOptions, logger);
|
||||
return (updated, result);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Concatenate <paramref name="imported"/> onto <paramref name="options"/>.<see cref="S7DriverOptions.Tags"/>
|
||||
/// and return a new options object with every other field untouched. The importers
|
||||
/// are additive so hand-edited Tags rows (e.g., system-status fields not surfaced by
|
||||
/// the TIA / AWL export) keep sitting alongside the bulk-imported symbol rows.
|
||||
/// </summary>
|
||||
private static S7DriverOptions MergeImportedTags(
|
||||
S7DriverOptions options, IReadOnlyList<S7TagDefinition> imported)
|
||||
{
|
||||
var merged = new List<S7TagDefinition>(options.Tags.Count + imported.Count);
|
||||
merged.AddRange(options.Tags);
|
||||
merged.AddRange(imported);
|
||||
|
||||
return new S7DriverOptions
|
||||
{
|
||||
Host = options.Host,
|
||||
Port = options.Port,
|
||||
CpuType = options.CpuType,
|
||||
Rack = options.Rack,
|
||||
Slot = options.Slot,
|
||||
Timeout = options.Timeout,
|
||||
Tags = merged,
|
||||
Probe = options.Probe,
|
||||
BlockCoalescingGapBytes = options.BlockCoalescingGapBytes,
|
||||
TsapMode = options.TsapMode,
|
||||
LocalTsap = options.LocalTsap,
|
||||
RemoteTsap = options.RemoteTsap,
|
||||
ScanGroupIntervals = options.ScanGroupIntervals,
|
||||
};
|
||||
}
|
||||
|
||||
private static T ParseEnum<T>(string? raw, string driverInstanceId, string field,
|
||||
string? tagName = null, T? fallback = null) where T : struct, Enum
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user