Configuring PackedHistoricalTagValue in Ignition 8

Patricia Callen5 min read
HMI / SCADAOther ManufacturerTechnical Reference
Licensed PE Working through this on a live machine? A Maine-licensed engineer can take it from here — included with IMD hardware, by the hour for everything else. Book an engineer

PackedHistoricalTagValue can remain in an Ignition 8 module by bridging Ignition 8 tag reads to the legacy path and quality types required by its constructor. Read with the Ignition 8 TagPath and QualifiedValue APIs, convert the full path through the legacy parser, translate the quality code to DataQuality, and preserve the value timestamp. Treat history-query timeouts as a separate storage or retrieval diagnostic until a controlled test proves that the constructed record is wrong.

What symptoms identify the failing stage?

Look at the trend first. Separate acquisition, conversion, storage, and retrieval before changing code. A current read that returns the right value does not prove that the historical path, quality, timestamp, or datatype is correct. Likewise, a queryHistory timeout does not prove that PackedHistoricalTagValue caused the failure.

Signal Source Wrong-value symptom
Tag path Ignition 8 TagPath.toStringFull(), then legacy TagPathParser.parseSafe() History is written or queried under the wrong provider or path, or no matching samples appear.
Process value QualifiedValue.getValue() The sample exists but contains a stale, null, or unexpected value type.
Quality QualifiedValue.getQuality().getCode(), mapped by DataQuality.getQualityFor() Good data appears bad, or invalid data is accepted as usable.
Timestamp QualifiedValue.getTimestamp() Samples appear outside the requested history window or out of sequence.
Datatype and interpolation DataTypeClass and the selected InterpolationMode Numeric trends compress or interpolate incorrectly, or discrete states are treated as analog values.

Capture the current read result and the constructed historical record side by side. Compare the full path, runtime value class, quality code, and timestamp. That measurement identifies the boundary where the signal changes.

Why can the Ignition 8 and legacy tag models coexist?

The read side and the historical-record constructor operate at different API boundaries. Ignition 8 returns a list of QualifiedValue objects from gatewayContext.getTagManager().readAsync(tagPaths). The available PackedHistoricalTagValue constructor still accepts the legacy com.inductiveautomation.ignition.common.sqltags.model.TagPath and DataQuality.

The bridge does not change the measured process value. It adapts the metadata surrounding that value:

  1. Render the Ignition 8 path with toStringFull().
  2. Parse that string into a legacy path with com.inductiveautomation.ignition.common.sqltags.parser.TagPathParser.parseSafe("default", ...).
  3. Extract the modern quality code and map it through DataQuality.getQualityFor(...).
  4. Pass the original value and value timestamp into the packed record.

The provider argument "default" matters when the rendered path lacks an explicit provider. Verify the parsed legacy path before storing data; a syntactically valid path can still identify the wrong provider.

How should the compatibility bridge be implemented?

Use an indexed loop so every returned value stays paired with its requested path and configured datatype. Incrementing an index outside the value loop reuses one path for multiple results and is a common cause of misplaced history.

CompletableFuture<List<QualifiedValue>> future =
    gatewayContext.getTagManager().readAsync(tagPaths);

List<QualifiedValue> qualifiedValues = future.get();

if (qualifiedValues.size() == tagPaths.size()) {
    for (int i = 0; i < qualifiedValues.size(); i++) {
        QualifiedValue qualifiedValue = qualifiedValues.get(i);
        if (qualifiedValue == null) {
            continue;
        }

        com.inductiveautomation.ignition.common.sqltags.model.TagPath legacyPath =
            com.inductiveautomation.ignition.common.sqltags.parser.TagPathParser.parseSafe(
                "default", tagPaths.get(i).toStringFull());

        InterpolationMode interpolation =
            tagDataTypes.get(i).getTypeClass() == DataTypeClass.Float
                ? InterpolationMode.Analog_Compressed
                : InterpolationMode.Discrete;

        PackedHistoricalTagValue historicalTagValue =
            new PackedHistoricalTagValue(
                legacyPath,
                tagDataTypes.get(i).getTypeClass(),
                interpolation,
                TimestampSource.Value,
                qualifiedValue.getValue(),
                DataQuality.getQualityFor(
                    qualifiedValue.getQuality().getCode()),
                qualifiedValue.getTimestamp());
    }
}

This constructor creates a historical value object; it does not by itself prove that the object reached persistent history storage. Test the subsequent storage call separately. Also handle asynchronous completion and failures according to the module lifecycle: future.get() blocks the calling thread until completion, so it should not sit on a gateway execution path that must remain responsive.

How do datatype, interpolation, and time affect the result?

The shown selection assigns InterpolationMode.Analog_Compressed only when the configured type class equals DataTypeClass.Float; every other type receives InterpolationMode.Discrete. That is an explicit application rule, not a universal classification of every numeric datatype. Review the actual runtime value class and the intended historian behavior before extending the rule to additional types.

TimestampSource.Value makes the supplied value timestamp part of the historical record. Compare qualifiedValue.getTimestamp() with the start and end of the history request. A valid sample outside that interval correctly produces an empty result, while a badly skewed timestamp can make a storage problem look like a query problem.

Tuning does not fix wiring, and query expansion does not fix a bad path. Confirm the tag provider, full path, datatype, quality, and timestamp at the conversion boundary before changing interpolation or widening the query.

How is the bridge verified without masking a query fault?

  1. Read one known tag and record its full Ignition 8 path, value, quality code, runtime value class, and timestamp.
  2. Construct one PackedHistoricalTagValue and inspect the corresponding legacy path, datatype, interpolation mode, quality, and timestamp.
  3. Submit that single record through the module's existing history-storage path.
  4. Query only that tag over a bounded interval containing the recorded timestamp.
  5. Compare the returned value, quality, and timestamp with the original QualifiedValue.
  6. Only after the single-tag test passes, increase the tag count and requested interval while watching gateway and database diagnostics.

If queryHistory times out, determine whether the delay occurs before the query reaches storage, while storage executes it, or while the gateway assembles the result. Check gateway logs, historian/database health, requested tag count, requested time range, and whether the same bounded query succeeds for a known-good historical tag. Avoid treating a timeout as a reason to modify quality mapping or interpolation unless those fields are demonstrably wrong.

Which migration pitfalls recur?

  • Index drift: Pair result index i with tagPaths.get(i) and tagDataTypes.get(i) inside the same loop.
  • Unchecked result size: Do not construct records when the returned list cannot be mapped unambiguously to the request list.
  • Null-only filtering: A non-null QualifiedValue can still carry a quality that the application should reject or record explicitly.
  • Provider substitution: Confirm what "default" does for paths that are not fully provider-qualified.
  • Datatype assumptions: The displayed rule distinguishes only DataTypeClass.Float from all other classes.
  • Blocking an operational thread: A wait on future.get() can turn a slow tag read into a module responsiveness problem.
  • Conflating construction with persistence: Successfully creating the object says nothing about the later storage transaction or history query.

FAQ

Can I use PackedHistoricalTagValue in Ignition 8?

Yes. Read modern QualifiedValue objects, convert each full tag path through the legacy TagPathParser, map its quality code with DataQuality.getQualityFor(), and pass through the original value and timestamp.

Does every numeric tag use Analog_Compressed interpolation?

No. The shown logic selects InterpolationMode.Analog_Compressed only for DataTypeClass.Float and uses InterpolationMode.Discrete otherwise; validate any broader datatype policy against the required historian behavior.

Does a queryHistory timeout mean the compatibility bridge failed?

No; test persistence and a bounded, single-tag history request separately. Stop and escalate to Inductive Automation official support when that minimal query still times out after gateway and database health checks, or when path or quality conversion differs reproducibly. Provide the Ignition version, gateway logs, minimal module code, full tag path, requested interval, and database symptoms.

Back to blog