feat: state sync service and hanging protocol updates to preserve state (#3131)
* feat: Add state sync and use it to remember viewport grid info fix: Version updates Fixes for toggling MPR mode Fix the display when the interleaved load module fails Fix the memory of the state to restore correctly PR fixes for the state sync service PR fixes PR fixes PR fixes Added a hack warning to remove volumeDeactivate Fixes for TMTV colormap setting Fix the casing Missed renames fix: tests not running due to variance in ordering Reverting some fixes to change case PR changes - mostly comments and minor improvements fix: All display sets were being updated on drag and drop PR fixes - mostly renames PR fixes Test support for OHIF, for HP branch test: Add at least a minimal set of automated tests for hanging protocols Docs PR fixes Merge fixes DOCS updates Add an example of the mn hanging protocol PR fixes PR fixes PR fixes * Fix the drag and drop PR fixes * PR changes - update default keys for next/previous stage * fix: Was storing the custom viewport grid too aggressively Caused by a PR change misspelling a variable
This commit is contained in:
1 parent
b7fff77e17
commit
803f638401
87 files changed
+3413
-1919
No files matched your search
@@ -27,6 +27,27 @@ registered automatically to the HangingProtocolService.
|
||||
|
||||
All protocols are stored in the `HangingProtocolService` using their `id` as the key, and the protocol itself as the value.
|
||||
|
||||
## Protocol Definition
|
||||
Protocols are defined in a getHangingProtocolModule inside an extension. As such,
|
||||
they are defined with a module structure that starts with an id, and has field protocol
|
||||
that is the actual protocol definition. This setup allows defining more than
|
||||
one protocol within a module, each one needing it's own definition file.
|
||||
|
||||
```javascript
|
||||
import MyProtocol from './MyProtocol';
|
||||
export default function getHangingProtocolModule() {
|
||||
return [
|
||||
{
|
||||
id: MyProtocol.id,
|
||||
protocol: MyProtocol,
|
||||
},
|
||||
];
|
||||
}
|
||||
```
|
||||
|
||||
Within the protocol itself, the structure is layed out as described in the HangingProtocol.ts
|
||||
type definition, starting with `Protocol`. See the type definition for more details.
|
||||
|
||||
## Events
|
||||
|
||||
There are two events that get publish in `HangingProtocolService`:
|
||||
@@ -34,31 +55,101 @@ There are two events that get publish in `HangingProtocolService`:
|
||||
| Event | Description |
|
||||
| ------------ | -------------------------------------------------------------------- |
|
||||
| NEW_LAYOUT | Fires when a new layout is requested by the `HangingProtocolService` |
|
||||
| STAGE_CHANGE | Fires when the the stage is changed in the hanging protocols |
|
||||
| PROTOCOL_CHANGED | Fires when the the protocol is changed in the hanging protocols |
|
||||
| HANGING_PROTOCOL_APPLIED_FOR_VIEWPORT | Fires when the hanging protocol applies for a viewport (sets its displaySets) |
|
||||
| PROTOCOL_CHANGED | Fires when the the protocol is changed in the hanging protocols, or when the applied stage is changed. |
|
||||
| RESTORE_PROTOCOL | Fires when the protocol or stage is restored, for example, after turning off MPR mode |
|
||||
| STAGE_ACTIVATION | Fires when the stages are known to have stage.status set. |
|
||||
|
||||
## Stage Activation and Status
|
||||
Sometimes a hanging protocol can be applicable generally, but not all stages
|
||||
should be shown by default, or should be shown at all. This can be handled by
|
||||
using the stage activation to control whether the stage is shown by default (`enabled`),
|
||||
whether it can be navigated to (`passive`) or whether it should not be shown
|
||||
at all (`disabled`).
|
||||
|
||||
The `stage.status` is used to control this, and the status is controlled by
|
||||
the stage activate. The status values are:
|
||||
|
||||
* enabled - meaning that the stage is fully applicable
|
||||
* passive - meaning that the stage can be applied, but might be missing details
|
||||
* disabled - meaning that the study has insuffient information for this stage
|
||||
|
||||
The default values for no `stageActivation` are to assume that `enabled` has `minViewports` of 1,
|
||||
and `passive` has `minViewports=0`. That is, enable the stage if at least one
|
||||
viewport is filled, and make it passive if no viewports are filled.
|
||||
|
||||
The setting for these are controlled by the stageActivation property, for example
|
||||
the following:
|
||||
|
||||
```javascript
|
||||
stageActivation: {
|
||||
// The enabled activation specifies requirements to enable the stage, that is,
|
||||
// make it preferred.
|
||||
enabled: {
|
||||
// The default value here is 1, and indicates how many non-blank viewports
|
||||
// are required.
|
||||
minViewportsMatched: 3,
|
||||
// This enables specifying cross cutting concerns, such as having a stage
|
||||
// only apply to males or females, and is a list of display set selector ids
|
||||
displaySetSelectorsMatched: ['dsMale'],
|
||||
},
|
||||
// The passive check is performed first. If it fails, the enabled is NOT
|
||||
// checked, but the status set to disabled. The default passive check
|
||||
// should always be passed, so it is fine to just define enabled if desired.
|
||||
passive: {
|
||||
// The default is 0, which means allow the stage even if no viewports are
|
||||
// filled. This allows dragging and dropping into the viewports to
|
||||
// make matches manually, which can then be re-used for other stages.
|
||||
minViewportsMatched: 0,
|
||||
displaySetSelectorsMatched: [...],
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
## API
|
||||
|
||||
- `destroy`: Destroys the HP service
|
||||
|
||||
- `reset` and `onModeEnter`: Resets the HP service to not have any active
|
||||
hanging protocols
|
||||
|
||||
- `getActiveProtocol`: Returns an object of the internal state of the HP service,
|
||||
useful for storing said state, as well as for getting direct access to the
|
||||
protocol and stage objects. Users of this should count on it being not completely
|
||||
stable as to exactly what this returns, as internal details can change.
|
||||
|
||||
- `getState`: Returns the currently applied protocol ID, stage index and active study UID.
|
||||
This information is storable/useable as state information to be used elsewhere.
|
||||
|
||||
- `getDefaultProtocol`: Returns the default protocol to apply.
|
||||
|
||||
- `getMatchDetails`: returns an object which contains the details of the
|
||||
matching for the viewports, displaySets and whether the protocol is
|
||||
applied to the viewport or not yet.
|
||||
applied to the viewport or not yet. This is deprecated as it is expected
|
||||
to be communicated by events instead.
|
||||
|
||||
- `getProtocols`: Returns a list of the currently active protocols.
|
||||
|
||||
- `getProtocolById`: Gets the protocol with the given id.
|
||||
|
||||
- `addProtocol`: adds provided protocol to the list of registered protocols
|
||||
for matching
|
||||
for matching. Will replacing any protocol with the same id, allowing, for example,
|
||||
to replace the default protocol.
|
||||
|
||||
- `setActiveProtocols`: Choose the protocols which are active. Can take a
|
||||
single protocol id or a list. When a single one is provided, that one will be
|
||||
applied whether or not the required rules match. Called automatically on mode
|
||||
init.
|
||||
|
||||
- `setActiveStudyUID`: Sets the given study UID as active, which has significance
|
||||
in terms of the matching rules being able to match against the active study.
|
||||
|
||||
- `run({studies, activeStudy, displaySets }, protocolId)`: runs the HPService with the provided
|
||||
studyMetaData and optional protocolId. If protocol is not given, HP Matching
|
||||
engine will search all the registered protocols for the best matching one
|
||||
based on the constraints.
|
||||
|
||||
- `registerImageLoadStrategy`: Adds a custom image load strategy.
|
||||
|
||||
- `addCustomAttribute`: adding a custom attribute for matching. (see below)
|
||||
|
||||
- `setProtocol`: applies a protocol to the current studies, it can be used for instance to apply a
|
||||
@@ -68,6 +159,12 @@ init.
|
||||
used for the protocol. If no options are provided, all displaySets will
|
||||
be used to match the protocol.
|
||||
|
||||
- `getStageIndex`: Finds the stage index for a given set of match keys. Currently
|
||||
only works on the currently active protocol, but is supposed to be able to work
|
||||
with other protocols as well.
|
||||
|
||||
- `getMissingViewport`: Returns a viewport object to be used as the missing
|
||||
viewport instance. This is used to fill out new viewports.
|
||||
|
||||
Default initialization of the modes handles running the `HangingProtocolService`
|
||||
|
||||
@@ -78,7 +175,7 @@ do not overlap, with the suggested id being `${moduleId}.${simpleName}`. The
|
||||
'default' name is used as the hanging protocol id when no other protocol applies,
|
||||
and can be set as the last module listed containing 'default'.
|
||||
|
||||
A hanging protocol can also be defined with a generator.
|
||||
A hanging protocol can also be defined with a generator.
|
||||
A generator is a function we can write this way:
|
||||
|
||||
```ts
|
||||
@@ -93,6 +190,33 @@ function protocolGenerator({ servicesManager, commandsManager }) {
|
||||
|
||||
See the typescript definitions for more details on the structure of protocols.
|
||||
|
||||
## Additional viewports for layout - `defaultViewport`
|
||||
Sometimes the user manually selects a layout of a given size, say `2x3`. The
|
||||
hanging protocol can define what viewport options to use for this viewport by
|
||||
defining an extra viewport option in `defaultViewport`. For example:
|
||||
|
||||
```javascript
|
||||
defaultViewport: {
|
||||
viewportOptions: {
|
||||
viewportType: 'stack',
|
||||
toolGroupId: 'default',
|
||||
allowUnmatchedView: true,
|
||||
},
|
||||
displaySets: [
|
||||
{
|
||||
id: 'defaultDisplaySetId',
|
||||
matchedDisplaySetsIndex: -1,
|
||||
},
|
||||
],
|
||||
},
|
||||
```
|
||||
|
||||
This allows defining the type of additional viewports, what tool group etc they
|
||||
are allowed in, and which display set is used to fill them. In the above case,
|
||||
the display set is the same as the other viewports, but the
|
||||
`matchedDisplaySetsIndex=-1`, so that means find the next matching display set
|
||||
from the display set selector which isn't already filling a view.
|
||||
|
||||
## Custom Attribute
|
||||
In some situations, you might want to match based on a custom attribute and not the DICOM tags. For instance,
|
||||
if you have assigned a `timepointId` to each study, and you want to match based on it.
|
||||
@@ -102,7 +226,7 @@ There are various ways that you can let `HangingProtocolService` know of you
|
||||
custom attribute. We will show how to add it inside the mode configuration.
|
||||
|
||||
```js
|
||||
const deafultProtocol = {
|
||||
const defaultProtocol = {
|
||||
id: 'defaultProtocol',
|
||||
/** ... **/
|
||||
protocolMatchingRules: [
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
---
|
||||
sidebar_position: 8
|
||||
sidebar_label: State Sync Service
|
||||
---
|
||||
|
||||
# State Sync Service
|
||||
|
||||
## Overview
|
||||
The state sync service is designed to allow short and long term memory of things such as
|
||||
annotations applied, last annotation state, hanging protocol viewport state,
|
||||
window level etc. This allows for better interaction with things like navigation
|
||||
between hanging protocols, ensuring that the previously displayed layouts
|
||||
can be redisplayed after returning to a given hanging protocol.
|
||||
|
||||
Currently, all the state sync service configurations have one of the following two
|
||||
lifetimes. See the mode description for general information on the mode lifetime.
|
||||
|
||||
* Application load - when the application is restarted, the state is lost
|
||||
* `clearOnModeExit` - which stores state until the mode onModeExit is called, and then throws away the remaining state. This is useful for mode specific information.
|
||||
|
||||
### TODO work - add more storage locations
|
||||
It is expected to add a few more storage locations, which will store to various
|
||||
locations on updates:
|
||||
|
||||
* User specific server store - to store things between application restarts at the user level
|
||||
* Browser state store - to store things in the browser local state, to recover after crashing.
|
||||
* Study specific server store - to store things relevant to a given study between application restarts, on the server.
|
||||
|
||||
## Events
|
||||
|
||||
Currently the service does not fire events.
|
||||
|
||||
## API
|
||||
|
||||
- `register`: to create a new named state storage
|
||||
- `reduce`: to apply a set of changes to several states at once
|
||||
- `getState`: to retrieve the current state
|
||||
- `onModeExit`: clears the states configured as clearOnModeExit states
|
||||
|
||||
### register
|
||||
The register call is typically added to an extension to create a new
|
||||
syncable state. A typical call is shown below, registering the viewport
|
||||
grid store state as a modal state.
|
||||
|
||||
```javascript
|
||||
stateSyncService.register('viewportGridStore', { clearOnModeExit: true });
|
||||
```
|
||||
|
||||
### getState
|
||||
The `getState` call returns an object containing all of the reigstered states,
|
||||
by id. The values can be read directly, but should not be modified.
|
||||
|
||||
### reduce
|
||||
The `reduce` call is used to apply a set of updates to various states. The
|
||||
updates are performed for every state as a simply "set" call.
|
||||
|
||||
### onModeExit
|
||||
When the Mode is exited, the onModeExit is called on the sync state, and this
|
||||
clears all states registered with `clearOnModeExit: true`.
|
||||
To avoid clearing the state, the mode definition should store any transient
|
||||
state in the mode onModeExit and recover it in the `mode.onModeEnter`.
|
||||
|
||||
## OHIF Registered State
|
||||
There are a number of defined states here. It is recommended to update this
|
||||
list as states are added:
|
||||
|
||||
* `viewportGridStore` has viewport grid restore information for returning to an earlier grid layout.
|
||||
* `reuseIdMap` has a map of names to display sets for preserving user changes to hp display set selections.
|
||||
* `hanging` has a map of the hanging protocol stage information applied (HPInfo)
|
||||
* `presentationSync` has the cornerstone presentation state information
|
||||
* `toggleHangingProtocol` has the previously applied hanging protocol, to toggle an HP off.
|
||||
* `querySync` has the previously applied query information. Not fully implemented yet.
|
||||
@@ -33,6 +33,7 @@ button is clicked by the user.
|
||||
presets.
|
||||
- `commandName`: if tool has a command attached to run
|
||||
- `commandOptions`: arguments for the command.
|
||||
- `setActive`: Sets a given tool active (not as primary but as secondary)
|
||||
|
||||
- `reset`: reset the state of the toolbarService, set the primary tool to be
|
||||
`Wwwc` and unsubscribe tools that have registered their functions.
|
||||
|
||||
@@ -19,7 +19,8 @@ We maintain the following non-ui Services:
|
||||
- [Hanging Protocol Service](../data/HangingProtocolService.md)
|
||||
- [Toolbar Service](../data/ToolBarService.md)
|
||||
- [Measurement Service](../data/MeasurementService.md)
|
||||
- [Customization Service](customization-service.md)
|
||||
- [Customization Service](../data/customization-service.md)
|
||||
- [State Sync Service](../data/StateSyncService.md)
|
||||
- [Panel Service](../data/PanelService.md)
|
||||
|
||||
## Service Architecture
|
||||
|
||||
Reference in new issue
Block a user