Skip to main content

tagaudit

CI PyPI Python License

Check industrial tag data before you trust it.

tagaudit reads a historian CSV export or an OPC UA capture and tells you which values to distrust and why:

  • wrong units and values outside the declared range
  • bad or uncertain quality codes
  • text where a number should be (I/O Timeout, Bad Input)
  • duplicate and backwards timestamps
  • gaps and stuck values
  • local times that happen twice, or never, when the clocks change

It runs on your machine, needs only Python 3.10+, and never writes to a PLC or server.

Quick start

pip install tagaudit
tagaudit check export.csv

Without a profile, tagaudit works out the layout and checks what the file alone can prove. To check ranges, quality codes, gaps and stuck values, write a profile and edit it:

tagaudit init export.csv -o profile.json      # lists tags and units; you add limits
tagaudit check export.csv --profile profile.json

Output for examples/historian-long.csv with examples/historian-profile.json:

tagaudit 0.1.0 · historian-long.csv · long export · asset pump-station-1
2026-10-24T23:59:40Z to 2026-10-24T23:59:58Z (UTC)

FAIL  10 of 20 values rejected

Problems
  ambiguous_local_time                  2  local time happens twice (clocks went back)
      line 19  PT-101 = 8.07  at 2026-10-25 02:00:01
      line 20  FT-102 = 42.0  at 2026-10-25 02:00:01
  gap_exceeds_limit                     2  gap since the previous value exceeds max_gap_seconds
      line 17  PT-101 = 8.06  at 2026-10-25 01:59:58
      line 18  FT-102 = 41.9  at 2026-10-25 01:59:58
  duplicate_timestamp                   1  same tag twice at the same time
      line 12  PT-101 = 8.04  at 2026-10-25 01:59:44
  nonfinite_or_nonnumeric_value         1  value is not a number
      line 9  FT-102 = I/O Timeout  at 2026-10-25 01:59:43
  outside_engineering_range             1  value outside the declared range
      line 6  PT-101 = 803  at 2026-10-25 01:59:42
  quality_bad                           1  quality code says bad
      line 9  FT-102 = I/O Timeout  at 2026-10-25 01:59:43
  quality_uncertain                     1  quality code says uncertain
      line 11  FT-102 = 41.8  at 2026-10-25 01:59:44
  stuck_value                           1  value unchanged for more than max_repeats samples
      line 16  FT-102 = 41.8  at 2026-10-25 01:59:48
  timestamp_went_backwards              1  earlier than this tag's previous value
      line 21  PT-101 = 8.06  at 2026-10-25 01:59:50
  unit_mismatch                         1  unit differs from the profile
      line 6  PT-101 = 803  at 2026-10-25 01:59:42

Not checked
  freshness                    exports carry no receive time, so data age at the edge is unknown
  sample_groups                long export is not declared synchronized

Exit codes: 0 all checks passed, 2 problems found, 1 the file or profile couldn't be read. Add --output report.json for a JSON report, or --json to print it.

What it reads

Layout Looks like Notes
Long Timestamp,TagName,Value,Quality,Units One value per row. Quality and unit columns are optional.
Wide Timestamp,PT-101,FT-102 One column per tag. A blank cell counts as a missing value in that row.
Native capture written by tagaudit collect Adds OPC UA source, server and receive timestamps, so freshness can be checked.

For wide and synchronized long exports, the report also counts missing values per tag and names the tags that are empty in every row.

Each file needs one column holding the full date and time. Exports that split date and time across several columns aren't supported yet. Timestamps can be ISO 8601 with or without a UTC offset, epoch seconds or milliseconds, or any strptime pattern. If they have no offset, set input.timezone (UTC, +04:00 or a zone such as Europe/Berlin). With a named zone, tagaudit flags times that occur twice or never during clock changes.

Profiles

A profile says what the data should look like. tagaudit init writes the layout, tags and units it finds. It leaves the rest for you, because guessing them would make those checks meaningless.

{
  "schema": "tagaudit.profile.v1",
  "quality_encoding": "opc_da_quality",
  "max_gap_seconds": 5,
  "max_repeats": 3,
  "input": {"format": "long", "timestamp": "Timestamp", "tag": "TagName", "value": "Value",
            "quality": "Quality", "unit": "Units", "timezone": "Europe/Berlin"},
  "tags": [
    {"name": "PT-101", "unit": "bar", "minimum": 0, "maximum": 16},
    {"name": "FT-102", "unit": "m3/h", "minimum": 0, "maximum": 120, "max_gap_seconds": 10}
  ]
}
  • quality_encoding: opcua_status_code (0 is good), opc_da_quality (192 is good, 0 is bad) or quality_words (good, uncertain, bad). The two numeric schemes read 0 in opposite ways, so tagaudit never guesses.
  • minimum and maximum: either or both. Tags without them are listed under "Not checked".
  • max_gap_seconds: the longest silence allowed between values of a tag.
  • max_repeats: how many times in a row a value may repeat before it counts as stuck. Per-tag values override the profile-wide ones.
  • Wide exports map tag names to columns with "input": {"format": "wide", "columns": {"PT-101": "Pump01/Pressure"}}.

OPC UA captures

pip install "tagaudit[opcua]"
tagaudit collect --profile capture-profile.json --endpoint opc.tcp://192.168.0.10:4840 \
  --certificate client.pem --private-key client-key.pem --server-certificate server.der \
  --csv capture.csv --output capture.audit.json

The collector only reads: it exposes no Write or Call service, and it takes a bounded number of samples (--samples, default 5). It records each value's source, server and receive timestamps, so the audit can tell a fresh server timestamp on an old value from genuinely new data. Use a server-side read-only role as well, because client code is not access control. examples/capture-faults.csv shows the checks on a capture.

For machines without pip, copy audit.py out of the repository or the wheel. It runs on its own with the standard library and checks native captures: python audit.py --profile profile.json --csv capture.csv --output report.json.

What a pass does not mean

A pass means the file matches its profile. It doesn't show that sensors are calibrated, that the source was authentic, that an export is still current, or that a model trained on the data will behave. Checks the file can't support are listed under "Not checked" instead of being skipped silently.

License

Apache-2.0. Copyright 2026 Sankalp Thakur.

Metadata

Release files for tagaudit 0.1.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for tagaudit 0.1.1
File Size Uploaded
tagaudit-0.1.1.tar.gz 45.9 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for tagaudit 0.1.1
File Interpreter ABI Platform
tagaudit-0.1.1-py3-none-any.whl Python 3 none any Details

Total release size: 80.5 kB

Release files / tagaudit-0.1.1.tar.gz

Download URL tagaudit-0.1.1.tar.gz
Size 45.9 kB
Tags Source
SHA-256 checksum
How to use checksums
44093bba09c484172f6c147ec54ed64713fc76aae83eb8bf4fff466466d79271
BLAKE2b-256 checksum
How to use checksums
1beda2619664f097885635f42498dce2577594df2a51e6aa311a547cbb18d0f9
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.14

Release files / tagaudit-0.1.1-py3-none-any.whl

Download URL tagaudit-0.1.1-py3-none-any.whl
Size 34.5 kB
Tags Python 3
SHA-256 checksum
How to use checksums
eaf1da5682b5a750c2d513528132b6d83e98789f5e67d20cd26ad5f701fafd01
BLAKE2b-256 checksum
How to use checksums
93e7c330527568c8f3cb2a2e97049aeee0aa47d1af0b09cd699dd714947d55a6
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.14

Release history Release notifications | RSS feed

This release

0.1.1 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page