Record CSV output invariantly: ISO 8601 timestamps, full-precision values, real array contents - #170
Conversation
CSV recordings were not portable across locales and permanently lost
precision:
- Timestamps used culture-sensitive ToString: fi-FI replaced the ':'
format placeholder with '.', and th-TH applied the Buddhist calendar
(year 2569), breaking the documented ISO 8601 contract. Both
WriteRecord timestamps (snapshot and DateTime.Now fallback) now use
CultureInfo.InvariantCulture. The same defect class in default
filename timestamps (GenerateDefaultRecordingFilename and
ConnectionIdentifier.Generate) is fixed the same way.
- The Value column recorded the UI display string ("F2", current
culture), truncating every Float/Double to two decimals, writing
"42,12" under European locales, and recording arrays as "[5 items]".
MonitoredNode now carries RawValue, an invariant-culture
full-precision string set alongside the display Value in the
notification path (SubscriptionManager.FormatRawValue: round-trip
floating point, ISO 8601 "O" for DateTime, InvariantCulture for
IFormattable, arrays as semicolon-joined elements). CsvRecordingManager
snapshots RawValue (falling back to Value when unset), preserving the
immutable RecordSnapshot-at-enqueue-time design and thread-safety
guarantees. The UI display string is unchanged.
Tests cover hostile cultures (fi-FI, th-TH, de-DE), full-precision
round-trips, array serialization, and snapshot immutability of the raw
value. README updated to document the export format.
https://claude.ai/code/session_012Vopnd9vWkzELveHRgZhie
Code Review: CSV Invariant Output FixSummary: This PR correctly fixes culture-sensitive bugs in CSV recording — timestamp formatting that broke under fi-FI/th-TH locales, float/double values truncated to 2 decimal places in the display format, and arrays serialized as What's done well
Issues worth addressing1. Semicolons within string array elements are not escaped (potential data loss)
OPC UA // Option A: escape ';' within elements
parts.Add(FormatRawValue(element).Replace(";", "\;"));
// Option B: document the known limitation in the XML doc comment2.
3. Minor: XML doc on The current doc comment explains how the value is set (references /// <summary>
/// Full-precision, culture-invariant value for data export (CSV).
/// Set at the same points as <see cref="Value"/>; see
/// <c>SubscriptionManager.FormatRawValue</c> for formatting rules.
/// </summary>Minor observations (no action required)
VerdictApprove with the semicolon-escaping issue flagged as the only non-trivial concern. The core bug fixes are correct, the test coverage is solid, and the separation of |
["a;b", "c"] previously serialized identically to ["a", "b", "c"], silently misparsing multi-element string arrays on CSV import. https://claude.ai/code/session_012Vopnd9vWkzELveHRgZhie
Code Review
OverviewThis PR fixes a real and well-scoped bug: What Works WellCorrect diagnosis and fix. Pinning Clean display/export separation. The Array serialization is well thought-out. Using Impressive th-TH collation catch. The comment explaining why Test quality is high. Round-trip precision tests ( Issues and SuggestionsMinor
Empty array serializes to empty string.
NitThe doc comments on SummaryCorrect fix, correct approach, good test coverage. The two substantive observations worth tracking are the empty-array ambiguity and the null-string ambiguity — both edge cases that don't affect current usage but could trip up future CSV consumers. The |
Summary
Fixes the culture-sensitive / lossy CSV recording output from the v1.0 follow-up review (
docs/V1-REVIEW-FOLLOWUP.md, item 6, plus the related filename-calendar low).Timestamps — invariant ISO 8601:
WriteRecordtimestamp formats (snapshot timestamp and theDateTime.Nowfallback) now passCultureInfo.InvariantCulture. Previously the:custom-format placeholder was replaced by the culture's time separator (fi-FI wrote14.30.00) and the culture's default calendar applied (th-TH wrote Buddhist year 2569), breaking the documented ISO 8601 contract.CsvRecordingManager) and connection identifiers (ConnectionIdentifier.Generate) get the same fix — no more Buddhist/Hijri years in default filenames.Values — full precision, invariant:
MonitoredNode.RawValue: an invariant-culture, full-precision string set at the same points the displayValueis set (initial read, change notifications, pending/stale markers). The CSV snapshot now recordsRawValue; the UI display string ("F2", current culture) is untouched.SubscriptionManager.FormatRawValue: float/double via shortest round-trippableToString(InvariantCulture),DateTimevia"O", otherIFormattablevia invariant culture. Previously every recorded float/double was truncated to 2 decimals (permanent precision loss) and wrote42,12under European locales.1;2;3;4;5, includingbyte[]), instead of"[5 items]". Documented in the README recording bullet.RecordSnapshotimmutability/thread-safety design is unchanged: strings captured synchronously at enqueue time on the notification thread.Tests: 39 new tests — ISO 8601 under fi-FI/th-TH hostile cultures, full-precision round-trips, dot decimals under fi-FI/de-DE/th-TH, array serialization, Gregorian filenames under th-TH, snapshot immutability. Temporarily reverting only
CsvRecordingManager.csmakes 6 of them fail, confirming they catch the original defects. (One subtlety: xUnit'sAssert.DoesNotContain(string)is itself culture-sensitive under th-TH collation, so an exactAssert.Equalis used instead.)Test plan
dotnet build Opcilloscope.sln -c Release— 0 warnings, 0 errorsdotnet test -c Release— 639/639 passing (full suite incl. integration)Part of the v1 follow-up punch list (
docs/V1-REVIEW-FOLLOWUP.md, item 6).https://claude.ai/code/session_012Vopnd9vWkzELveHRgZhie
Generated by Claude Code