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(