From e401358887dfa9a60f8caa409d9e2f79ff58aaa8 Mon Sep 17 00:00:00 2001 From: Bill Wallace Date: Wed, 5 Oct 2022 08:32:54 -0400 Subject: [PATCH] fix(hp): Make the updated hanging protocol compatible with modes (#2971) There had been a change made to make the hanging protocols auto initialize, but that caused problems on mode init because the couldn't specify the hanging protocols any longer. THis change restores that, and also improves the API a little bit by allowing the display set selectors to be specified by id instead of positionally. fix(hp):PR Review Comments fix(hp):Unit test and docs --- .../src/getHangingProtocolModule.ts | 2 +- .../default/src/getHangingProtocolModule.js | 21 ++- .../tmtv/src/getHangingProtocolModule.js | 169 ++++++------------ modes/basic-dev-mode/src/index.js | 4 +- modes/tmtv/src/index.js | 4 +- platform/cli/templates/mode/src/index.tsx | 6 +- .../core/src/extensions/ExtensionManager.js | 5 +- .../HangingProtocolService/HPMatcher.js | 2 +- .../HangingProtocolService.test.js | 62 +++---- .../HangingProtocolService.ts | 83 ++++++--- .../HangingProtocolService/ProtocolEngine.js | 10 +- platform/core/src/types/HangingProtocol.ts | 31 +++- platform/docs/docs/platform/modes/index.md | 4 +- .../services/data/HangingProtocolService.md | 20 ++- .../version-3.0/platform/modes/index.md | 4 +- platform/viewer/src/routes/Mode/Mode.tsx | 10 +- 16 files changed, 228 insertions(+), 209 deletions(-) diff --git a/extensions/cornerstone/src/getHangingProtocolModule.ts b/extensions/cornerstone/src/getHangingProtocolModule.ts index e7a2b2cd5..c728912f7 100644 --- a/extensions/cornerstone/src/getHangingProtocolModule.ts +++ b/extensions/cornerstone/src/getHangingProtocolModule.ts @@ -91,6 +91,7 @@ const MPRHangingProtocolGenerator: Types.HangingProtocol.ProtocolGenerator = ({ const protocol = { id: 'mpr', + displaySetSelectors: {}, stages: [ { id: 'mprStage', @@ -122,7 +123,6 @@ const MPRHangingProtocolGenerator: Types.HangingProtocol.ProtocolGenerator = ({ ], }, }, - displaySets: [], viewports: hpViewports, }, ], diff --git a/extensions/default/src/getHangingProtocolModule.js b/extensions/default/src/getHangingProtocolModule.js index 162cba0cf..eac96d91c 100644 --- a/extensions/default/src/getHangingProtocolModule.js +++ b/extensions/default/src/getHangingProtocolModule.js @@ -9,6 +9,15 @@ const defaultProtocol = { editableBy: {}, protocolMatchingRules: [], toolGroupIds: ['default'], + displaySetSelectors: { + defaultDisplaySetId: { + // Unused currently + imageMatchingRules: [], + // Matches displaysets, NOT series + seriesMatchingRules: [], + studyMatchingRules: [], + }, + }, stages: [ { id: 'hYbmMy3b7pz7GLiaT', @@ -20,16 +29,6 @@ const defaultProtocol = { columns: 1, }, }, - displaySets: [ - { - id: 'displaySet', - // Unused currently - imageMatchingRules: [], - // Matches displaysets, NOT series - seriesMatchingRules: [], - studyMatchingRules: [], - }, - ], viewports: [ { viewportOptions: { @@ -42,7 +41,7 @@ const defaultProtocol = { displaySets: [ { options: [], - id: 'displaySet', + id: 'defaultDisplaySetId', }, ], }, diff --git a/extensions/tmtv/src/getHangingProtocolModule.js b/extensions/tmtv/src/getHangingProtocolModule.js index 541055e5c..65353edf6 100644 --- a/extensions/tmtv/src/getHangingProtocolModule.js +++ b/extensions/tmtv/src/getHangingProtocolModule.js @@ -1,10 +1,10 @@ const ptCT = { - id: 'ptCT', + id: '@ohif/extension-tmtv.hangingProtocolModule.ptCT', locked: true, hasUpdatedPriorsInformation: false, name: 'Default', createdDate: '2021-02-23T19:22:08.894Z', - modifiedDate: '2021-02-23T19:22:08.894Z', + modifiedDate: '2022-10-04T19:22:08.894Z', availableTo: {}, editableBy: {}, toolGroupIds: [ @@ -16,37 +16,79 @@ const ptCT = { imageLoadStrategy: 'interleaveTopToBottom', // "default" , "interleaveTopToBottom", "interleaveCenter" protocolMatchingRules: [ { - id: 'wauZK2QNEfDPwcAQo', - weight: 1, attribute: 'ModalitiesInStudy', constraint: { contains: ['CT', 'PT'], }, - required: false, }, { - id: 'wauZK2QNEfDPwcAQo', - weight: 1, attribute: 'StudyDescription', constraint: { - contains: { - value: 'PETCT', - }, + contains: 'PETCT', }, - required: false, }, { - id: 'wauZK2QNEfDPwcAQo', - weight: 1, attribute: 'StudyDescription', constraint: { - contains: { - value: 'PET/CT', - }, + contains: 'PET/CT', }, - required: false, }, ], + displaySetSelectors: { + ctDisplaySet: { + seriesMatchingRules: [ + { + weight: 1, + attribute: 'Modality', + constraint: { + equals: { + value: 'CT', + }, + }, + required: true, + }, + { + attribute: 'SeriesDescription', + constraint: { + contains: 'CT', + }, + }, + { + attribute: 'SeriesDescription', + constraint: { + contains: 'CT WB', + }, + }, + ], + }, + ptDisplaySet: { + seriesMatchingRules: [ + { + attribute: 'Modality', + constraint: { + equals: 'PT', + }, + required: true, + }, + { + attribute: 'SeriesDescription', + constraint: { + contains: 'Corrected', + }, + }, + { + weight: 2, + attribute: 'SeriesDescription', + constraint: { + doesNotContain: { + value: 'Uncorrected', + }, + }, + }, + ], + }, + }, + stages: [ { id: 'hYbmMy3b7pz7GLiaT', @@ -120,99 +162,6 @@ const ptCT = { ], }, }, - displaySets: [ - { - id: 'ctDisplaySet', - imageMatchingRules: [], - seriesMatchingRules: [ - { - id: 'GPEYqFLv2dwzCM322', - weight: 1, - attribute: 'Modality', - constraint: { - equals: { - value: 'CT', - }, - }, - required: true, - }, - { - id: 'vSjk7NCYjtdS3XZAw', - weight: 1, - attribute: 'SeriesNumber', - constraint: { - equals: { - value: '4', - }, - }, - required: false, - }, - { - id: 'vSjk7NCYjtdS3XZAw', - weight: 1, - attribute: 'SeriesDescription', - constraint: { - contains: { - value: 'CT', - }, - }, - required: false, - }, - { - id: 'vSjk7NCYjtdS3XZAw', - weight: 1, - attribute: 'SeriesDescription', - constraint: { - contains: { - value: 'CT WB', - }, - }, - required: false, - }, - ], - studyMatchingRules: [], - }, - { - id: 'ptDisplaySet', - imageMatchingRules: [], - seriesMatchingRules: [ - { - id: 'GPEYqFLv2dwzCM322', - weight: 1, - attribute: 'Modality', - constraint: { - equals: { - value: 'PT', - }, - }, - required: true, - }, - { - id: 'GPEYqFLv2dwzCM322', - weight: 1, - attribute: 'SeriesDescription', - constraint: { - contains: { - value: 'Corrected', - }, - }, - required: false, - }, - { - id: 'GPEYqFLv2dwzCM322', - weight: 2, - attribute: 'SeriesDescription', - constraint: { - doesNotContain: { - value: 'Uncorrected', - }, - }, - required: false, - }, - ], - studyMatchingRules: [], - }, - ], viewports: [ { viewportOptions: { diff --git a/modes/basic-dev-mode/src/index.js b/modes/basic-dev-mode/src/index.js index 3d586f70a..54ce9ab68 100644 --- a/modes/basic-dev-mode/src/index.js +++ b/modes/basic-dev-mode/src/index.js @@ -10,7 +10,7 @@ const configs = { const ohif = { layout: '@ohif/extension-default.layoutTemplateModule.viewerLayout', sopClassHandler: '@ohif/extension-default.sopClassHandlerModule.stack', - hangingProtocols: '@ohif/extension-default.hangingProtocolModule.default', + hangingProtocol: '@ohif/extension-default.hangingProtocolModule.default', measurements: '@ohif/extension-default.panelModule.measure', thumbnailList: '@ohif/extension-default.panelModule.seriesList', }; @@ -186,7 +186,7 @@ function modeFactory({ modeConfiguration }) { }, ], extensions: extensionDependencies, - hangingProtocols: [ohif.hangingProtocols], + hangingProtocol: [ohif.hangingProtocol], sopClassHandlers: [ dicomvideo.sopClassHandler, ohif.sopClassHandler, diff --git a/modes/tmtv/src/index.js b/modes/tmtv/src/index.js index 4018dd538..75ba63ee5 100644 --- a/modes/tmtv/src/index.js +++ b/modes/tmtv/src/index.js @@ -17,7 +17,7 @@ const cs3d = { }; const tmtv = { - hangingProtocols: '@ohif/extension-tmtv.hangingProtocolModule.ptCT', + hangingProtocol: '@ohif/extension-tmtv.hangingProtocolModule.ptCT', petSUV: '@ohif/extension-tmtv.panelModule.petSUV', ROIThresholdPanel: '@ohif/extension-tmtv.panelModule.ROIThresholdSeg', }; @@ -187,7 +187,7 @@ function modeFactory({ modeConfiguration }) { }, ], extensions: extensionDependencies, - hangingProtocol: 'ptCT', + hangingProtocol: tmtv.hangingProtocol, sopClassHandlers: [ohif.sopClassHandler], hotkeys: [...hotkeys.defaults.hotkeyBindings], }; diff --git a/platform/cli/templates/mode/src/index.tsx b/platform/cli/templates/mode/src/index.tsx index 5574f3730..a9c28dd93 100644 --- a/platform/cli/templates/mode/src/index.tsx +++ b/platform/cli/templates/mode/src/index.tsx @@ -3,7 +3,7 @@ import { id } from './id'; const ohif = { layout: '@ohif/extension-default.layoutTemplateModule.viewerLayout', sopClassHandler: '@ohif/extension-default.sopClassHandlerModule.stack', - hangingProtocols: '@ohif/extension-default.hangingProtocolModule.default', + hangingProtocol: '@ohif/extension-default.hangingProtocolModule.default', leftPanel: '@ohif/extension-default.panelModule.seriesList', rightPanel: '@ohif/extension-default.panelModule.measure', }; @@ -88,8 +88,8 @@ function modeFactory({ modeConfiguration }) { ], /** List of extensions that are used by the mode */ extensions: extensionDependencies, - /** HangingProtocols used by the mode */ - hangingProtocols: [''], + /** HangingProtocol used by the mode */ + // hangingProtocol: [''], /** SopClassHandlers used by the mode */ sopClassHandlers: [ohif.sopClassHandler], /** hotkeys for mode */ diff --git a/platform/core/src/extensions/ExtensionManager.js b/platform/core/src/extensions/ExtensionManager.js index 7da7a9d92..48f7c9960 100644 --- a/platform/core/src/extensions/ExtensionManager.js +++ b/platform/core/src/extensions/ExtensionManager.js @@ -287,7 +287,10 @@ export default class ExtensionManager { _initHangingProtocolsModule = (extensionModule, extensionId) => { const { HangingProtocolService } = this._servicesManager.services; extensionModule.forEach(({ id, protocol }) => { - HangingProtocolService.addProtocol(id, protocol); + if (protocol) { + // Only auto-register if protocol specified, otherwise let mode register + HangingProtocolService.addProtocol(id, protocol); + } }); }; diff --git a/platform/core/src/services/HangingProtocolService/HPMatcher.js b/platform/core/src/services/HangingProtocolService/HPMatcher.js index 48a552973..854dab807 100644 --- a/platform/core/src/services/HangingProtocolService/HPMatcher.js +++ b/platform/core/src/services/HangingProtocolService/HPMatcher.js @@ -11,7 +11,7 @@ import validate from './lib/validator'; */ const match = ( metadataInstance, - rules, + rules = [], customAttributeRetrievalCallbacks, options ) => { diff --git a/platform/core/src/services/HangingProtocolService/HangingProtocolService.test.js b/platform/core/src/services/HangingProtocolService/HangingProtocolService.test.js index 4942aa3fd..dc70ef870 100644 --- a/platform/core/src/services/HangingProtocolService/HangingProtocolService.test.js +++ b/platform/core/src/services/HangingProtocolService/HangingProtocolService.test.js @@ -2,31 +2,40 @@ import HangingProtocolServiceClass from './HangingProtocolService'; const testProtocol = { id: 'test', - locked: true, hasUpdatedPriorsInformation: false, name: 'Default', - createdDate: '2021-02-23T19:22:08.894Z', - modifiedDate: '2021-02-23T19:22:08.894Z', - availableTo: {}, - editableBy: {}, - toolGroupIds: ['ctToolGroup', 'ptToolGroup'], - imageLoadStrategy: 'interleaveTopToBottom', // "default" , "interleaveTopToBottom", "interleaveCenter" protocolMatchingRules: [ { - id: 'wauZK2QNEfDPwcAQo', - weight: 1, attribute: 'StudyDescription', constraint: { - contains: { - value: 'PETCT', - }, + contains: 'PETCT', }, - required: false, }, ], + displaySetSelectors: { + displaySetSelector: { + seriesMatchingRules: [ + { + weight: 1, + attribute: 'Modality', + constraint: { + equals: 'CT', + }, + required: true, + }, + { + weight: 1, + attribute: 'numImageFrames', + constraint: { + greaterThan: 10, + }, + }, + ], + studyMatchingRules: [], + }, + }, stages: [ { - id: 'hYbmMy3b7pz7GLiaT', name: 'default', viewportStructure: { layoutType: 'grid', @@ -35,29 +44,6 @@ const testProtocol = { columns: 1, }, }, - displaySets: [ - { - id: 'displaySet', - seriesMatchingRules: [ - { - weight: 1, - attribute: 'Modality', - constraint: { - equals: 'CT', - }, - required: true, - }, - { - weight: 1, - attribute: 'numImageFrames', - constraint: { - greaterThan: 10, - }, - }, - ], - studyMatchingRules: [], - }, - ], viewports: [ { viewportOptions: { @@ -83,7 +69,7 @@ const testProtocol = { }, displaySets: [ { - id: 'displaySet', + id: 'displaySetSelector', }, ], }, diff --git a/platform/core/src/services/HangingProtocolService/HangingProtocolService.ts b/platform/core/src/services/HangingProtocolService/HangingProtocolService.ts index a4914d003..eaa40bc7a 100644 --- a/platform/core/src/services/HangingProtocolService/HangingProtocolService.ts +++ b/platform/core/src/services/HangingProtocolService/HangingProtocolService.ts @@ -19,6 +19,8 @@ class HangingProtocolService { studies: StudyMetadata[]; // stores all the protocols (object or function that returns an object) in a map protocols: Map; + // Contains the list of currently active keys + activeProtocolIds: string[]; // the current protocol that is being applied to the viewports in object format protocol: HangingProtocol.Protocol; stage: number; @@ -123,8 +125,9 @@ class HangingProtocolService { // this.protocols is a map of protocols with the protocol id as the key // and the protocol or a function that returns a protocol as the value const protocols = []; + const keys = this.activeProtocolIds || this.protocols.keys(); // @ts-ignore - for (const protocolId of this.protocols.keys()) { + for (const protocolId of keys) { const protocol = this.getProtocolById(protocolId); if (protocol) { protocols.push(protocol); @@ -164,7 +167,8 @@ class HangingProtocolService { /** * It adds a protocol to the protocols map object. If a protocol with the given - * id already exists, warn the user and overwrite it. + * id already exists, warn the user and overwrite it. This can be used to + * set a new "default" protocol. * * @param {string} protocolId - The id of the protocol. * @param {Protocol} protocol - Protocol - This is the protocol that you want to @@ -184,6 +188,40 @@ class HangingProtocolService { this.protocols.set(protocolId, protocol); } + /** + * Add a given protocol object as active. + * If active protocols ids is null right now, then the specified + * protocol will become the only active protocol. + */ + public addActiveProtocol(id: string): void { + if (!id) { + return; + } + if (!this.activeProtocolIds) { + this.activeProtocolIds = []; + } + this.activeProtocolIds.push(id); + } + + /** + * Sets the active hanging protocols to use, by name. If the value is empty, + * then resets the active protocols to all the named items. + */ + public setActiveProtocols( + hangingProtocol?: string[] | string + ): void { + if (!hangingProtocol || !hangingProtocol.length) { + this.activeProtocolIds = null; + console.log('No active protocols, setting all to active'); + return; + } + if (typeof hangingProtocol === 'string') { + this.setActiveProtocols([hangingProtocol]); + return; + } + this.activeProtocolIds = [...hangingProtocol]; + } + /** * Run the hanging protocol decisions tree on the active study, * studies list and display sets, firing a hanging protocol event when @@ -208,7 +246,7 @@ class HangingProtocolService { this.customAttributeRetrievalCallbacks ); - if (protocolId) { + if (protocolId && typeof protocolId === 'string') { const protocol = this.getProtocolById(protocolId); this._setProtocol(protocol); return; @@ -504,6 +542,8 @@ class HangingProtocolService { ); } + const { displaySetSelectors = {} } = this.protocol; + // Retrieve the current stage const stageModel = this._getCurrentStageModel(); @@ -513,7 +553,6 @@ class HangingProtocolService { !stageModel || !stageModel.viewportStructure || !stageModel.viewports || - !stageModel.displaySets || !stageModel.viewports.length ) { console.log('Stage cannot be applied', stageModel); @@ -541,17 +580,27 @@ class HangingProtocolService { }); // Matching the displaySets - for (const displaySet of stageModel.displaySets) { - // skip matching if already matched - if (this.displaySetMatchDetails.has(displaySet.id)) { - continue; - } + for (const viewport of stageModel.viewports) { + for (const displaySet of viewport.displaySets) { + const { id: displaySetId } = displaySet; + // skip matching if already matched + if (this.displaySetMatchDetails.has(displaySetId)) { + continue; + } + const displaySetSelector = displaySetSelectors[displaySetId]; - const { bestMatch, matchingScores } = this._matchImages(displaySet); - this.displaySetMatchDetails.set(displaySet.id, bestMatch); + if (!displaySetSelector) { + console.warn('No display set selector for', displaySetId); + continue; + } + const { bestMatch, matchingScores } = this._matchImages( + displaySetSelector + ); + this.displaySetMatchDetails.set(displaySetId, bestMatch); - if (bestMatch) { - bestMatch.matchingScores = matchingScores; + if (bestMatch) { + bestMatch.matchingScores = matchingScores; + } } } @@ -564,8 +613,6 @@ class HangingProtocolService { // but it is a info to locate the displaySet from the displaySetService const displaySetsInfo = []; viewport.displaySets.forEach( - // Todo: why do we have displaySetIndex here? It is not used in the protocol - // definition ({ id, displaySetIndex = 0, options: displaySetOptions }) => { const viewportDisplaySetMain = this.displaySetMatchDetails.get(id); // Use the display set index to allow getting the "next" match, eg @@ -612,11 +659,7 @@ class HangingProtocolService { // level matching needs to be added in future // Todo: handle fusion viewports by not taking the first displaySet rule for the viewport - const { - studyMatchingRules = [], - seriesMatchingRules, - findAll = false, - } = displaySetRules; + const { studyMatchingRules = [], seriesMatchingRules } = displaySetRules; const matchingScores = []; let highestStudyMatchingScore = 0; diff --git a/platform/core/src/services/HangingProtocolService/ProtocolEngine.js b/platform/core/src/services/HangingProtocolService/ProtocolEngine.js index 2c8fddca9..5cfdf1264 100644 --- a/platform/core/src/services/HangingProtocolService/ProtocolEngine.js +++ b/platform/core/src/services/HangingProtocolService/ProtocolEngine.js @@ -131,12 +131,14 @@ export default class ProtocolEngine { // If no matches were found, select the default protocol if provided // if not select the first protocol in the list if (!matched.length) { + const protocol = + this.protocols.find(protocol => protocol.id === 'default') ?? + this.protocols[0]; + console.log('No protocol matches, defaulting to', protocol); return [ { - score: 1, - protocol: - this.protocols.find(protocol => protocol.id === 'default') ?? - this.protocols[0], + score: 0, + protocol, }, ]; } diff --git a/platform/core/src/types/HangingProtocol.ts b/platform/core/src/types/HangingProtocol.ts index 8d299ed09..92088e382 100644 --- a/platform/core/src/types/HangingProtocol.ts +++ b/platform/core/src/types/HangingProtocol.ts @@ -48,11 +48,21 @@ type ViewportStructure = { }; }; -type DisplaySet = { - id: string; - imageMatchingRules: MatchingRule[]; +/** + * Selects the display sets to apply for a given id. + * This is a set of rules which match the study and display sets + * and then provides an id for them so that they can re-used in different + * viewports. + * The matches are done lazily, so if a stage doesn't need a given match, + * it won't be selected. + */ +type DisplaySetSelector = { + // The image matching rule (not currently implemented) selects which image to + // display initially, only for stack views. + imageMatchingRules?: MatchingRule[]; + // The matching rules to choose the display sets at the series level seriesMatchingRules: MatchingRule[]; - studyMatchingRules: MatchingRule[]; + studyMatchingRules?: MatchingRule[]; }; type SyncGroup = { @@ -75,11 +85,17 @@ type ViewportOptions = { viewportId?: string; initialImageOptions?: initialImageOptions; syncGroups?: SyncGroup[]; - customViewportProps? : Record; + customViewportProps?: Record; }; type DisplaySetOptions = { + // The id is used to choose which display set selector to apply here id: string; + // An offset to allow display secondary series, for example + // to display the second matching series (displaySetIndex==1) + // This cannot easily be done with the matching rules directly. + displaySetIndex?: number; + // The options to apply to the display set. options?: Record; }; @@ -92,7 +108,6 @@ type ProtocolStage = { id: string; name: string; viewportStructure: ViewportStructure; - displaySets: DisplaySet[]; viewports: Viewport[]; createdDate?: string; }; @@ -100,6 +115,8 @@ type ProtocolStage = { type Protocol = { // Mandatory id: string; + // Selects which display sets are given a specific name. + displaySetSelectors: Record; stages: ProtocolStage[]; // Optional locked?: boolean; @@ -129,7 +146,7 @@ export type { Protocol, ProtocolStage, Viewport, - DisplaySet, + DisplaySetSelector, ViewportStructure, ViewportLayoutOptions, DisplaySetOptions, diff --git a/platform/docs/docs/platform/modes/index.md b/platform/docs/docs/platform/modes/index.md index 95421bfae..b01049b0f 100644 --- a/platform/docs/docs/platform/modes/index.md +++ b/platform/docs/docs/platform/modes/index.md @@ -77,7 +77,7 @@ function modeFactory() { }, ], extensions: extensionDependencies, - hangingProtocols: [], + hangingProtocol: [], sopClassHandlers: [], hotkeys: [], }; @@ -272,7 +272,7 @@ function modeFactory({ modeConfiguration }) { }, ], extensions: extensionDependencies, - hangingProtocols: ['@ohif/extension-default.hangingProtocolModule.petCT'], + hangingProtocol: ['@ohif/extension-default.hangingProtocolModule.petCT'], sopClassHandlers: ['@ohif/extension-default.sopClassHandlerModule.stack'], // ... }; diff --git a/platform/docs/docs/platform/services/data/HangingProtocolService.md b/platform/docs/docs/platform/services/data/HangingProtocolService.md index ded2252cd..e019260a5 100644 --- a/platform/docs/docs/platform/services/data/HangingProtocolService.md +++ b/platform/docs/docs/platform/services/data/HangingProtocolService.md @@ -56,6 +56,11 @@ There are two events that get publish in `HangingProtocolService`: - `addProtocol`: adds provided protocol to the list of registered protocols for matching +- `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. + - `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 @@ -65,7 +70,14 @@ There are two events that get publish in `HangingProtocolService`: Default initialization of the modes handles running the `HangingProtocolService` +## Hanging Protocol Instance Definition +A hanging protocol has an id provided in the module which is used to identify +the protocol. Mostly these should include the module name so that they +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'. +See the typescript definitions for more details on the structure. ## Custom Attribute In some situations, you might want to match based on a custom attribute and not the DICOM tags. For instance, @@ -81,17 +93,17 @@ const deafultProtocol = { /** ... **/ protocolMatchingRules: [ { - id: 'vSjk7NCYjtdS3XZAw', weight: 3, attribute: 'timepoint', constraint: { - equals: { - value: 'first', - }, + equals: 'first', }, required: false, }, ], + displaySetSelectors: { + /** ... */ + } stages: [ /** ... **/ ], diff --git a/platform/docs/versioned_docs/version-3.0/platform/modes/index.md b/platform/docs/versioned_docs/version-3.0/platform/modes/index.md index 95421bfae..b01049b0f 100644 --- a/platform/docs/versioned_docs/version-3.0/platform/modes/index.md +++ b/platform/docs/versioned_docs/version-3.0/platform/modes/index.md @@ -77,7 +77,7 @@ function modeFactory() { }, ], extensions: extensionDependencies, - hangingProtocols: [], + hangingProtocol: [], sopClassHandlers: [], hotkeys: [], }; @@ -272,7 +272,7 @@ function modeFactory({ modeConfiguration }) { }, ], extensions: extensionDependencies, - hangingProtocols: ['@ohif/extension-default.hangingProtocolModule.petCT'], + hangingProtocol: ['@ohif/extension-default.hangingProtocolModule.petCT'], sopClassHandlers: ['@ohif/extension-default.sopClassHandlerModule.stack'], // ... }; diff --git a/platform/viewer/src/routes/Mode/Mode.tsx b/platform/viewer/src/routes/Mode/Mode.tsx index 628e33088..3c772939b 100644 --- a/platform/viewer/src/routes/Mode/Mode.tsx +++ b/platform/viewer/src/routes/Mode/Mode.tsx @@ -114,7 +114,10 @@ export default function ModeRoute({ locationRef.current = location; } - const { DisplaySetService } = servicesManager.services; + const { + DisplaySetService, + HangingProtocolService: hangingProtocolService, + } = servicesManager.services; const { extensions, sopClassHandlers, hotkeys, hangingProtocol } = mode; @@ -241,8 +244,13 @@ export default function ModeRoute({ extensionManager, commandsManager, }); + // Sets the active hanging protocols - if hangingProtocol is undefined, + // resets to default. Done before the onModeEnter to allow the onModeEnter + // to perform custom hanging protocol actions + hangingProtocolService.setActiveProtocols(hangingProtocol); mode?.onModeEnter({ servicesManager, extensionManager, commandsManager }); + const setupRouteInit = async () => { if (route.init) { return await route.init(