Skip to content

Get low-frequency Waveform data from HL7 to Interchange - #172

Open
jeremyestein wants to merge 23 commits into
developfrom
jeremy/settings-variables
Open

Get low-frequency Waveform data from HL7 to Interchange#172
jeremyestein wants to merge 23 commits into
developfrom
jeremy/settings-variables

Conversation

@jeremyestein

@jeremyestein jeremyestein commented Aug 7, 2026

Copy link
Copy Markdown
Collaborator

Start at Hl7ParseAndQueue.java if you want to see the main part of the change first.

Process low frequency waveform data, eg. settings, from HL7 to Emap interchange messages. These are scalar values reported at a lower frequency (typically 3Hz) and can measure anything, but are not actually waveforms themselves.

  • Move much of WaveformMessage into abstract base class WaveformBaseMessage and add another subclass WaveformLowFreqMessage for low frequency messages. Kept original name for compatibility with downstream waveform controller.
  • emap core processor intentionally ignores LF messages as this is out of scope for this phase of the waveform project
  • synthetic generator emits LF messages alongside HF ones, for testing outside of production
  • LF interchange messages are sent to the publisher as-is, unlike HF ones which go through collation first. De-duping, if necessary, will have to be done in the waveform controller
  • Anonymised real HL7 test messages settings*.hl7 and parse tests cover numeric + string + categorical mapping

jeremyestein and others added 5 commits August 5, 2026 16:58
Fully anonymised:
- Messages IDs made sequential
- All timestamps shifted by same amount
- Location changed to another bed
- Numerical values changed slightly

Also extract the "value type" out of CSV metadata.
create these messages for non-Waveform variables.
Not passing them into the collator just yet.
@github-actions

github-actions Bot commented Aug 7, 2026

Copy link
Copy Markdown

PR checklist

Default guide for a PR (if multiple PRs for the work, only keep one version of it and link to it on the other PRs)

  • From the UCLH data science desktop, a validation run has been set off
  • Check that content that reveals Epic IP (eg. Clarity/Caboodle queries) has not been checked into a public repo.
  • load times
    in UCL teams has been populated with the run information
  • During the run, glowroot has been checked for any queries which are taking a substantial proportion of the
    total processing time. This can be useful to identify indexes that are required.
  • After the run, look for any unexpected errors in the etl_per_message_logging table, the error_search.sql file
    on the shared drive can be used for this \\sharefs6\UCLH6\EMAP\Shared\EmapSqlScripts\devops\error_search.sql.
    Create an issue if you find an unexpected exception and is not related to the changes you've made, otherwise
    fix them!
  • After the run, populate the end time in
    load times
  • Let Aasiyah know about the completed validation and give her information on the changes and where to start
    with the validation
  • Check validation report and give any feedback to Aasiyah if there are any changes needed on her side,
    iterate on getting the validation to match at least 99% (validation and emap code).

@jeremyestein
jeremyestein marked this pull request as ready for review August 12, 2026 14:33

@thompson318 thompson318 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks logical and the workflow makes sense. A few comments / questions.
General comment. I wonder if by keeping "waveform" messages for backward compatibility with what we've already done on the controller we're storing up problems for the future. Would it be better to rename then "waveformHighFreqMessage" to be clear about the two different message types? It would make a lot of work for us for marginal gain so perhaps not.

Comment thread waveform-reader/src/main/resources/source-metadata/unit_codes.csv
record ExpectedWaveformMessage(String variableId, String sourceValue, String mappedUnits, Double numericValue, String stringValue) {
public void assertIsEqual(WaveformLowFreqMessage actualMessage) {
assertEquals(variableId, actualMessage.getSourceVariableId());
assertEquals(sourceValue, actualMessage.getSourceValue().get());

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
assertEquals(sourceValue, actualMessage.getSourceValue().get());
assertEquals(sourceValue, actualMessage.getSourceValue().get());
assertEquals(mappedUnits, actualMessage.getMappedUnits().get());

Why aren't we checking the mapped units? Would the above suggestion work?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well spotted. It's actualMessage.getUnit(), we don't even send the source unit ID. Fixed now.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lovely flow chart, makes it clear what's supposed to happen.

logger.trace("HL7 message generated {} Waveform messages ({} collatable, {} not), sending for collation",
msgs.size(), waveformMessages.size(), lfMessages.size());
for (var m: lfMessages) {
waveformOperations.sendMessage(m);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the rationale for not sending low frequency messages to a collator?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Low frequency messages don't have a fixed sampling frequency in the same way that HF ones do, so I decided we should store them as one row (CSV row or emap-star row) per data point, as opposed to arrays that we already use for the HF data.

In practice, they seem to be sent 3 times a second (haven't checked intervals for exactness), but my take was that this is probably configurable and not an inherent property of the source data.

But yes, you could treat these exactly like an HF data source, and reduce them down to a start time and an array, from which you have to work out the time of any particular point by knowing its position in the array and the sampling frequency (start_time + idx / samp_freq).

And maybe 3/sec will create an annoyingly long CSV/parquet file, and we'll wish we used arrays.

Another option, if we're not sure the intervals are spot on, is to squash them into periods with a start and end date where the value is the same (good for things like ventilation mode that rarely change, but maybe bad for other things?), as we talked about in one of the meetings this week. But this would be extra work.

Maybe we should talk about this more?

@jeremyestein

Copy link
Copy Markdown
Collaborator Author

I wonder if by keeping "waveform" messages for backward compatibility with what we've already done on the controller we're storing up problems for the future. Would it be better to rename then "waveformHighFreqMessage" to be clear about the two different message types? It would make a lot of work for us for marginal gain so perhaps not.

It wouldn't be that much work to rename. And I'm currently working on the waveform controller code anyway (which, as it happens, doesn't check the message type anyway). So it would be possible to change it if you think it's better.

@thompson318

Copy link
Copy Markdown
Contributor

I wonder if by keeping "waveform" messages for backward compatibility with what we've already done on the controller we're storing up problems for the future. Would it be better to rename then "waveformHighFreqMessage" to be clear about the two different message types? It would make a lot of work for us for marginal gain so perhaps not.

It wouldn't be that much work to rename. And I'm currently working on the waveform controller code anyway (which, as it happens, doesn't check the message type anyway). So it would be possible to change it if you think it's better.

If it's not much work then I suggest renaming it. I'm trying to think from the perspective of someone in a couple of years time who get's asked to implement a new type of waveform message, and may struggle to understand the inheritance structure as is.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants