Auto: abcip-2.4 — CSV tag import/export
CsvTagImporter / CsvTagExporter parse and emit Kepware-format AB CIP tag CSVs (Tag Name, Address, Data Type, Respect Data Type, Client Access, Scan Rate, Description, Scaling). Import maps Tag Name → AbCipTagDefinition.Name, Address → TagPath, Data Type → DataType, Description → Description, Client Access → Writable. Skips blank rows + ;/# section markers; honours column reordering via header lookup; RFC-4180-ish quoting. CsvImports collection on AbCipDriverOptions mirrors L5kImports/L5xImports and is consumed by InitializeAsync (declared > L5K > L5X > CSV precedence). CLI tag-export command dumps the merged tag table from a driver-options JSON to a Kepware CSV — runs the same import-merge precedence the driver uses but without contacting any PLC. Tests cover R/W mapping, blank-row skip, quoted comma, escaped quote, name prefix, unknown-type fall-through, header reordering, and a load → export → reparse round-trip. Closes #232
This commit is contained in:
@@ -152,6 +152,10 @@ public sealed class AbCipDriver : IDriver, IReadable, IWritable, ITagDiscovery,
|
||||
declaredNames: declaredNames,
|
||||
allTags: allTags);
|
||||
}
|
||||
foreach (var import in _options.CsvImports)
|
||||
{
|
||||
MergeCsvImport(import, declaredNames, allTags);
|
||||
}
|
||||
|
||||
foreach (var tag in allTags)
|
||||
{
|
||||
@@ -234,6 +238,43 @@ public sealed class AbCipDriver : IDriver, IReadable, IWritable, ITagDiscovery,
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// CSV-import variant of <see cref="MergeImport"/>. The CSV path produces
|
||||
/// <see cref="AbCipTagDefinition"/> records directly (no intermediate document) so we
|
||||
/// can't share the L5K/L5X parser-delegate signature. Merge semantics are identical:
|
||||
/// a name already covered by a declaration or an earlier import is left untouched so
|
||||
/// the precedence chain (declared > L5K > L5X > CSV) holds.
|
||||
/// </summary>
|
||||
private static void MergeCsvImport(
|
||||
AbCipCsvImportOptions import,
|
||||
HashSet<string> declaredNames,
|
||||
List<AbCipTagDefinition> allTags)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(import.DeviceHostAddress))
|
||||
throw new InvalidOperationException(
|
||||
"AbCip CSV import is missing DeviceHostAddress — every imported tag needs a target device.");
|
||||
|
||||
string? csvText = null;
|
||||
if (!string.IsNullOrEmpty(import.FilePath))
|
||||
csvText = System.IO.File.ReadAllText(import.FilePath);
|
||||
else if (!string.IsNullOrEmpty(import.InlineText))
|
||||
csvText = import.InlineText;
|
||||
if (csvText is null) return;
|
||||
|
||||
var importer = new CsvTagImporter
|
||||
{
|
||||
DefaultDeviceHostAddress = import.DeviceHostAddress,
|
||||
NamePrefix = import.NamePrefix,
|
||||
};
|
||||
var result = importer.Import(csvText);
|
||||
foreach (var tag in result.Tags)
|
||||
{
|
||||
if (declaredNames.Contains(tag.Name)) continue;
|
||||
allTags.Add(tag);
|
||||
declaredNames.Add(tag.Name);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task ReinitializeAsync(string driverConfigJson, CancellationToken cancellationToken)
|
||||
{
|
||||
await ShutdownAsync(cancellationToken).ConfigureAwait(false);
|
||||
|
||||
Reference in New Issue
Block a user