feat: make hanging protocol work on displaySets instead of series (#2837)

* feat(HP): Apply HP to display sets, fix race condition

* fix: displaySetService no event not needed (#2912)

* fix various styles and renamings

* feat: refactored hp service

* fix: use HP service event for viewport grid

* remove unnecessary doc

* fix test

* apply review comment

* fix: segmentation creation

Co-authored-by: Alireza <ar.sedghi@gmail.com>
This commit is contained in:
Bill WallaceandAlireza authored and GitHub committed 2022-09-07 11:59:11 -04:00
1 parent 7c0756824e
commit e1d366e1e1
26 files changed
+893 -979

No files matched your search

+27 -20
View File
@@ -26,12 +26,12 @@ function ViewerViewportGrid(props) {
return;
}
const [
matchDetails,
const {
viewportMatchDetails,
hpAlreadyApplied,
] = HangingProtocolService.getState();
} = HangingProtocolService.getMatchDetails();
if (!matchDetails.length) {
if (!viewportMatchDetails.length) {
return;
}
@@ -43,24 +43,24 @@ function ViewerViewportGrid(props) {
}
// if current viewport doesn't have a match
if (matchDetails[i] === undefined) return;
if (viewportMatchDetails[i] === undefined) {
return;
}
const { displaySetsInfo, viewportOptions } = matchDetails[i];
const { displaySetsInfo, viewportOptions } = viewportMatchDetails[i];
const displaySetUIDsToHang = [];
const displaySetUIDsToHangOptions = [];
displaySetsInfo.forEach(({ SeriesInstanceUID, displaySetOptions }) => {
const matchingDisplaySet = availableDisplaySets.find(ds => {
return ds.SeriesInstanceUID === SeriesInstanceUID;
});
displaySetsInfo.forEach(
({ displaySetInstanceUID, displaySetOptions }) => {
if (!displaySetInstanceUID) {
return;
}
if (!matchingDisplaySet) {
return;
displaySetUIDsToHang.push(displaySetInstanceUID);
displaySetUIDsToHangOptions.push(displaySetOptions);
}
displaySetUIDsToHang.push(matchingDisplaySet.displaySetInstanceUID);
displaySetUIDsToHangOptions.push(displaySetOptions);
});
);
if (!displaySetUIDsToHang.length) {
continue;
@@ -113,10 +113,11 @@ function ViewerViewportGrid(props) {
// Using Hanging protocol engine to match the displaySets
useEffect(() => {
const { unsubscribe } = DisplaySetService.subscribe(
DisplaySetService.EVENTS.DISPLAY_SETS_CHANGED,
activeDisplaySets => {
updateDisplaySetsForViewports(activeDisplaySets);
const { unsubscribe } = HangingProtocolService.subscribe(
HangingProtocolService.EVENTS.PROTOCOL_CHANGED,
() => {
const displaySets = DisplaySetService.getActiveDisplaySets();
updateDisplaySetsForViewports(displaySets);
}
);
@@ -369,6 +370,11 @@ function _getViewportComponent(displaySets, viewportComponents) {
const SOPClassHandlerId = displaySets[0].SOPClassHandlerId;
for (let i = 0; i < viewportComponents.length; i++) {
if (!viewportComponents[i])
throw new Error('viewport components not defined');
if (!viewportComponents[i].displaySetsToDisplay) {
throw new Error('displaySetsToDisplay is null');
}
if (
viewportComponents[i].displaySetsToDisplay.includes(SOPClassHandlerId)
) {
@@ -376,6 +382,7 @@ function _getViewportComponent(displaySets, viewportComponents) {
return component;
}
}
throw new Error(`No display set handler for ${SOPClassHandlerId}`);
}
export default ViewerViewportGrid;
+46 -20
View File
@@ -9,24 +9,26 @@ import { useQuery } from '@hooks';
import ViewportGrid from '@components/ViewportGrid';
import Compose from './Compose';
async function defaultRouteInit({
servicesManager,
studyInstanceUIDs,
dataSource,
}) {
/**
* Initialize the route.
*
* @param props.servicesManager to read services from
* @param props.studyInstanceUIDs for a list of studies to read
* @param props.dataSource to read the data from
* @returns array of subscriptions to cancel
*/
function defaultRouteInit({ servicesManager, studyInstanceUIDs, dataSource }) {
const {
DisplaySetService,
HangingProtocolService,
} = servicesManager.services;
const unsubscriptions = [];
// TODO: This should be baked into core, not manual?
// DisplaySetService would wire this up?
const {
unsubscribe: instanceAddedUnsubscribe,
} = DicomMetadataStore.subscribe(
DicomMetadataStore.EVENTS.INSTANCES_ADDED,
({ StudyInstanceUID, SeriesInstanceUID, madeInClient = false }) => {
function({ StudyInstanceUID, SeriesInstanceUID, madeInClient = false }) {
const seriesMetadata = DicomMetadataStore.getSeries(
StudyInstanceUID,
SeriesInstanceUID
@@ -38,19 +40,43 @@ async function defaultRouteInit({
unsubscriptions.push(instanceAddedUnsubscribe);
const { unsubscribe: seriesAddedUnsubscribe } = DicomMetadataStore.subscribe(
DicomMetadataStore.EVENTS.SERIES_ADDED,
({ StudyInstanceUID, madeInClient }) => {
const studyMetadata = DicomMetadataStore.getStudy(StudyInstanceUID);
if (!madeInClient) {
HangingProtocolService.run(studyMetadata);
}
}
const allRetrieves = studyInstanceUIDs.map(StudyInstanceUID =>
dataSource.retrieve.series.metadata({ StudyInstanceUID })
);
unsubscriptions.push(seriesAddedUnsubscribe);
studyInstanceUIDs.forEach(StudyInstanceUID => {
dataSource.retrieve.series.metadata({ StudyInstanceUID });
// The hanging protocol matching service is fairly expensive to run multiple
// times, and doesn't allow partial matches to be made (it will simply fail
// to display anything if a required match fails), so we wait here until all metadata
// is retrieved (which will synchronously trigger the display set creation)
// until we run the hanging protocol matching service.
Promise.allSettled(allRetrieves).then(() => {
const displaySets = DisplaySetService.getActiveDisplaySets();
if (!displaySets || !displaySets.length) {
return;
}
const studyMap = {};
// Prior studies don't quite work properly yet, but the studies list
// is at least being generated and passed in.
const studies = displaySets.reduce((prev, curr) => {
const { StudyInstanceUID } = curr;
if (!studyMap[StudyInstanceUID]) {
const study = DicomMetadataStore.getStudy(StudyInstanceUID);
studyMap[StudyInstanceUID] = study;
prev.push(study);
}
return prev;
}, []);
// The assumption is that the display set at position 0 is the first
// study being displayed, and is thus the "active" study.
const activeStudy = studies[0];
// run the hanging protocol matching service on the displaySets
HangingProtocolService.run({ studies, activeStudy, displaySets });
});
return unsubscriptions;
@@ -236,7 +262,7 @@ export default function ModeRoute({
});
}
return await defaultRouteInit({
return defaultRouteInit({
servicesManager,
studyInstanceUIDs,
dataSource,