55 */
66
77import { PageSection } from "@patternfly/react-core" ;
8- import { App } from "antd " ;
9- import type React from "react " ;
8+ import config from "config " ;
9+ import { access } from "fs " ;
1010import { useEffect , useState } from "react" ;
1111import {
12+ genUUID ,
1213 getRoot ,
1314 getRootID ,
15+ getState ,
1416 type ModuleToFunc ,
17+ StateType ,
1518 useReducer ,
1619} from "react-reducer-utils" ;
17- import { useLonk } from "../../api/lonk/index.ts" ;
20+ import { useLocation , useSearchParams } from "react-router-dom" ;
21+ import type { Lonk , LonkMessageData } from "../../api/lonk/types.ts" ;
1822import type { PACSqueryCore } from "../../api/pfdcm/index.ts" ;
1923import * as DoPacs from "../../reducers/pacs" ;
2024import ErrorScreen from "./components/ErrorScreen.tsx" ;
2125import PacsLoadingScreen from "./components/PacsLoadingScreen.tsx" ;
22- import { getSeriesDescription } from "./components/utils.ts" ;
2326import { DEFAULT_PREFERENCES } from "./defaultPreferences.ts" ;
2427import styles from "./PacsApp.module.css" ;
2528import PacsView from "./PacsView.tsx" ;
26- import { type PacsState , SeriesPullState } from "./types.ts" ;
29+ import { type PacsState , QUERY_PROMPT , SearchMode } from "./types.ts" ;
2730import { createFeedWithSeriesInstanceUID , errorCodeIsNot4xx } from "./utils.ts" ;
2831
2932type TDoPacs = ModuleToFunc < typeof DoPacs > ;
@@ -76,22 +79,25 @@ export default () => {
7679 // ========================================
7780 // CLIENTS AND MISC
7881 // ========================================
79- const { message } = App . useApp ( ) ;
80-
81- const [ statePacs , doPacs ] = useReducer < DoPacs . State , TDoPacs > ( DoPacs ) ;
82+ const [ statePacs , doPacs ] = useReducer < DoPacs . State , TDoPacs > (
83+ DoPacs ,
84+ StateType . LOCAL ,
85+ ) ;
86+ const [ searchParams , setSearchParams ] = useSearchParams ( ) ;
87+ const location = useLocation ( ) ;
88+ const [ pacsID , _ ] = useState ( genUUID ( ) ) ;
8289
83- const pacsID = getRootID ( statePacs ) ;
84- const pacs = getRoot ( statePacs ) ?? DoPacs . defaultState ;
90+ const pacs = getState ( statePacs , pacsID ) ?? DoPacs . defaultState ;
8591
8692 const {
8793 expandedStudyUids,
8894 expandedSeries,
8995 studies,
9096 services,
97+ service,
9198 isGetServices,
92- seriesMap,
9399 isLoadingStudies,
94- wsUrl ,
100+ isExpandedAllDone ,
95101 } = pacs ;
96102
97103 // ========================================
@@ -101,92 +107,22 @@ export default () => {
101107 /**
102108 * Indicates a fatal error with the WebSocket.
103109 */
104- const [ wsError , setWsError ] = useState < React . ReactNode | null > ( null ) ;
110+ const [ wsError , setWsError ] = useState ( "" ) ;
105111 // TODO create a settings component for changing preferences
106112 const [ preferences , setPreferences ] = useState ( DEFAULT_PREFERENCES ) ;
107113
108114 // ========================================
109115 // COMBINED STATE OF EVERYTHING
110116 // ========================================
111117
118+ console . info ( "Pacs.App: pacs:" , pacs ) ;
119+
112120 /**
113121 * Combined states of PFDCM, LONK, and CUBE into one object.
114122 */
115123 const state : PacsState = { preferences, studies } ;
116124
117125 const error = wsError || pacs . errmsg ;
118- // ========================================
119- // LONK WEBSOCKET
120- // ========================================
121- const lonk = useLonk ( {
122- url : wsUrl ,
123- onDone : async ( pacs_name : string , SeriesInstanceUID : string ) => {
124- doPacs . updateReceiveState ( pacsID , pacs_name , SeriesInstanceUID , {
125- pullState : SeriesPullState . WAITING_OR_COMPLETE ,
126- done : true ,
127- } ) ;
128- doPacs . queryCubeSeriesStateBySeriesUID (
129- pacsID ,
130- pacs_name ,
131- SeriesInstanceUID ,
132- ) ;
133-
134- await createFeedWithSeriesInstanceUID ( SeriesInstanceUID ) ;
135-
136- return ;
137- } ,
138- onProgress : (
139- pacs_name : string ,
140- SeriesInstanceUID : string ,
141- ndicom : number ,
142- ) => {
143- doPacs . updateReceiveState (
144- pacsID ,
145- pacs_name ,
146- SeriesInstanceUID ,
147- { receivedCount : ndicom } ,
148- ( theOrig : number , theNew : number ) => theOrig < theNew ,
149- ) ;
150- } ,
151- onLonkError : (
152- pacs_name : string ,
153- SeriesInstanceUID : string ,
154- error : string ,
155- ) => {
156- doPacs . pushReceiveStateError ( pacsID , pacs_name , SeriesInstanceUID , error ) ;
157-
158- const desc = getSeriesDescription (
159- pacs_name ,
160- SeriesInstanceUID ,
161- seriesMap ,
162- ) ;
163- message . error (
164- < > There was an error while receiving the series "{ desc } "</ > ,
165- ) ;
166- } ,
167- onMessageError : ( data : any , error : string ) => {
168- message . error (
169- < >
170- A < em > LONK</ em > error occurred, please check the console.
171- </ > ,
172- ) ;
173- } ,
174- heartbeat : false ,
175- retryOnError : true ,
176- reconnectAttempts : 3 ,
177- reconnectInterval : 3000 ,
178- shouldReconnect : errorCodeIsNot4xx ,
179- onReconnectStop : ( ) => {
180- console . error ( "PacsApp.lonk: onReconnectStop" ) ;
181- setWsError ( < > The WebSocket is disconnected.</ > ) ;
182- } ,
183- onWebsocketError : ( ) => {
184- console . error ( "PacsApp.lonk: onWebsocketError" ) ;
185- message . error (
186- < > There was an error with the WebSocket. Reconnecting…</ > ,
187- ) ;
188- } ,
189- } ) ;
190126
191127 // ========================================
192128 // CALLBACKS
@@ -216,35 +152,110 @@ export default () => {
216152 // ========================================
217153
218154 // init
219- // biome-ignore lint/correctness/useExhaustiveDependencies: doPacs.init
220155 useEffect ( ( ) => {
156+ doPacs . init ( pacsID ) ;
157+
158+ const service = searchParams . get ( "service" ) || "" ;
159+ const mrn = searchParams . get ( SearchMode . MRN ) || "" ;
160+ const accessionNumber = searchParams . get ( SearchMode . AccessNo ) || "" ;
161+
162+ if ( service ) {
163+ doPacs . setService ( pacsID , service ) ;
164+ }
165+
166+ if ( mrn ) {
167+ doPacs . setQuery ( pacsID , QUERY_PROMPT [ SearchMode . MRN ] , mrn ) ;
168+ } else if ( accessionNumber ) {
169+ doPacs . setQuery (
170+ pacsID ,
171+ QUERY_PROMPT [ SearchMode . AccessNo ] ,
172+ accessionNumber ,
173+ ) ;
174+ }
175+ } , [ pacsID , doPacs . init , doPacs . setService , doPacs . setQuery , searchParams ] ) ;
176+
177+ useEffect ( ( ) => {
178+ if ( ! pacsID ) {
179+ return ;
180+ }
181+
182+ if ( ! location . pathname . startsWith ( "/pacs" ) ) {
183+ return ;
184+ }
185+
221186 // set document title.
222187 const originalTitle = document . title ;
223188 document . title = "ChRIS PACS" ;
224189
225190 // doPacs
226- doPacs . init ( ) ;
191+ const service = searchParams . get ( "service" ) || "" ;
192+ const mrn = searchParams . get ( SearchMode . MRN ) || "" ;
193+ const accessionNumber = searchParams . get ( SearchMode . AccessNo ) || "" ;
194+
195+ if ( service ) {
196+ doPacs . setService ( pacsID , service ) ;
197+ }
198+
199+ if ( mrn ) {
200+ doPacs . setQuery ( pacsID , QUERY_PROMPT [ SearchMode . MRN ] , mrn ) ;
201+ } else if ( accessionNumber ) {
202+ doPacs . setQuery (
203+ pacsID ,
204+ QUERY_PROMPT [ SearchMode . AccessNo ] ,
205+ accessionNumber ,
206+ ) ;
207+ }
227208
228209 return ( ) => {
229210 document . title = originalTitle ;
230211 } ;
231- } , [ ] ) ;
212+ } , [
213+ pacsID ,
214+ location . pathname ,
215+ searchParams ,
216+ doPacs . setService ,
217+ doPacs . setQuery ,
218+ ] ) ;
232219
233220 // Subscribe to all expanded series
234221 // biome-ignore lint/correctness/useExhaustiveDependencies: updateReceiveState
235222 useEffect ( ( ) => {
236- for ( const { pacs_name, SeriesInstanceUID } of expandedSeries ) {
237- lonk
238- . subscribe ( pacs_name , SeriesInstanceUID )
239- . then ( ( { pacs_name, SeriesInstanceUID } ) => {
240- doPacs . updateReceiveState ( pacsID , pacs_name , SeriesInstanceUID , {
241- subscribed : true ,
242- } ) ;
243- } ) ;
223+ if ( wsError ) {
224+ return ;
225+ }
226+
227+ if ( isExpandedAllDone ) {
228+ return ;
229+ }
230+
231+ if ( ! expandedSeries . length ) {
232+ return ;
244233 }
234+
235+ const series_uids = expandedSeries
236+ . map ( ( each ) => each . SeriesInstanceUID )
237+ . join ( "," ) ;
238+
239+ const url = `${ config . API_ROOT } /pacs/sse/?pacs_name=${ service } &series_uids=${ series_uids } ` ;
240+ const eventSource = new EventSource ( url ) ;
241+
242+ eventSource . onmessage = ( event ) => {
243+ const data : Lonk < LonkMessageData > = JSON . parse ( event . data ) ;
244+ doPacs . processLonkMsg ( pacsID , data ) ;
245+ } ;
246+
247+ eventSource . onerror = ( err ) => {
248+ console . error ( "PacsApp.eventSource.onerror: err:" , err ) ;
249+ setWsError ( `event error: ${ err } ` ) ;
250+ } ;
251+
252+ return ( ) => {
253+ console . info ( "PacsApp.eventSource: to return" ) ;
254+ eventSource . close ( ) ;
255+ } ;
245256 // Note: we are subscribing to series, but never unsubscribing.
246257 // This is mostly harmless.
247- } , [ expandedSeries ] ) ;
258+ } , [ expandedSeries , wsError , isExpandedAllDone ] ) ;
248259
249260 // ========================================
250261 // RENDER
0 commit comments